diff --git a/.editorconfig b/.editorconfig
index a3c068f12a..68366630d7 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -10,3 +10,7 @@ end_of_line = lf
charset = utf-8
trim_trailing_whitespace = false
insert_final_newline = true
+
+[*.razor]
+indent_style = space
+indent_size = 4
diff --git a/.githook-templates/pre-commit.sample b/.githook-templates/pre-commit.sample
new file mode 100755
index 0000000000..7171d30e7e
--- /dev/null
+++ b/.githook-templates/pre-commit.sample
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+
+RUFF_PATH=.venv/bin/ruff
+
+$RUFF_PATH check --fix
+$RUFF_PATH format
\ No newline at end of file
diff --git a/.github/workflows/test-install.yml b/.github/workflows/test-install.yml
index 9617b05bb8..1de89b22c5 100644
--- a/.github/workflows/test-install.yml
+++ b/.github/workflows/test-install.yml
@@ -1,4 +1,4 @@
-name: do test install
+name: Checks
on:
push:
@@ -6,9 +6,10 @@ on:
- main
- develop
- importer-rework
- paths-ignore:
- - 'documentation/**'
- - 'design/**'
+ # paths-ignore:
+ # - 'documentation/**'
+ # - 'design/**'
+ # do not ignore any paths as this breaks the github actions workflow
pull_request:
paths-ignore:
@@ -24,11 +25,10 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest]
- # os: [ubuntu-latest, ubuntu-22.04]
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
- - uses: actions/setup-dotnet@v4
+ - uses: actions/setup-dotnet@v5
with:
dotnet-version: '8.0.x'
@@ -38,9 +38,30 @@ jobs:
ansible-playbook -e force_install=true site.yml -K
- name: Running in GitHub actions requires testing puppeteer pdf creation separately
- if: ${{ env.RUNNING_ON_GITHUB_ACTIONS }} == 'true'
+ if: ${{ env.RUNNING_ON_GITHUB_ACTIONS == 'true' }}
run: |
cd /home/runner/work/firewall-orchestrator/firewall-orchestrator/roles/tests-unit/files/FWO.Test
dotnet restore
dotnet build
dotnet test --filter "Name=HtmlToPdfTest"
+
+
+ python-code-check:
+ name: Python Code Check
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v6
+
+ - uses: actions/setup-python@v6
+ with:
+ python-version: '3.11'
+
+ - run: pip install -r roles/importer/files/importer/requirements.txt
+
+ - uses: astral-sh/ruff-action@57714a7c8a2e59f32539362ba31877a1957dded1
+
+ - run: pyright
+
+ - run: ruff check
+
+ - run: ruff format --exit-non-zero-on-format
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 25c7ba2c72..f0f095d4e9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,9 +1,108 @@
.vs/
-.idea/
+.vscode/launch.json
+.vscode/settings.local.json
.test_data/
roles/importer/venv/
ansible_venv/
+*.todo
**/.venv/
**/__pycache__/
**/*.pyc
.vscode/launch.json
+
+# Created by https://www.toptal.com/developers/gitignore/api/jetbrains+iml
+# Edit at https://www.toptal.com/developers/gitignore?templates=jetbrains+iml
+
+### JetBrains+iml ###
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/**/usage.statistics.xml
+.idea/**/dictionaries
+.idea/**/shelf
+
+# AWS User-specific
+.idea/**/aws.xml
+
+# Generated files
+.idea/**/contentModel.xml
+
+# Sensitive or high-churn files
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+.idea/**/dbnavigator.xml
+
+# Gradle
+.idea/**/gradle.xml
+.idea/**/libraries
+
+# Gradle and Maven with auto-import
+# When using Gradle or Maven with auto-import, you should exclude module files,
+# since they will be recreated, and may cause churn. Uncomment if using
+# auto-import.
+# .idea/artifacts
+# .idea/compiler.xml
+# .idea/jarRepositories.xml
+# .idea/modules.xml
+# .idea/*.iml
+# .idea/modules
+# *.iml
+# *.ipr
+
+# CMake
+cmake-build-*/
+
+# Mongo Explorer plugin
+.idea/**/mongoSettings.xml
+
+# File-based project format
+*.iws
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Cursive Clojure plugin
+.idea/replstate.xml
+
+# SonarLint plugin
+.idea/sonarlint/
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+# Editor-based Rest Client
+.idea/httpRequests
+
+# Android studio 3.1+ serialized cache file
+.idea/caches/build_file_checksums.ser
+
+### JetBrains+iml Patch ###
+# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
+
+*.iml
+modules.xml
+.idea/misc.xml
+*.ipr
+
+# End of https://www.toptal.com/developers/gitignore/api/jetbrains+iml
+
+.idea/copilot*
+.idea/vcs.xml
+.idea/inspectionProfiles
+.idea/.gitignore
diff --git a/.idea/ruff.xml b/.idea/ruff.xml
new file mode 100644
index 0000000000..01e90d2137
--- /dev/null
+++ b/.idea/ruff.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000000..fb14048429
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,14 @@
+repos:
+- repo: https://github.com/astral-sh/ruff-pre-commit
+ # Ruff version.
+ rev: v0.14.8
+ hooks:
+ # Run the linter.
+ - id: ruff-check
+ args: [ --fix ]
+ # Run the formatter.
+ - id: ruff-format
+- repo: https://github.com/RobertCraigie/pyright-python
+ rev: v1.1.407
+ hooks:
+ - id: pyright
\ No newline at end of file
diff --git a/.sonarlint/FWO.json b/.sonarlint/FWO.json
new file mode 100644
index 0000000000..3d268041d8
--- /dev/null
+++ b/.sonarlint/FWO.json
@@ -0,0 +1,6 @@
+{
+ "sonarCloudOrganization": "cactusesecurity",
+ "projectKey": "CactuseSecurity_firewall-orchestrator",
+ "region": "EU",
+ "sonar.scanner.scanAll": false
+}
diff --git a/.vscode/launch.template.json b/.vscode/launch.template.json
index ea786a856e..f00948bab9 100644
--- a/.vscode/launch.template.json
+++ b/.vscode/launch.template.json
@@ -1,5 +1,14 @@
{
"version": "0.2.0",
+ "compounds": [
+ {
+ "name": "Launch MW and UI",
+ "configurations": [
+ "c#-MiddlewareServer",
+ "c#-Blazor UI"
+ ]
+ }
+ ],
"configurations": [
{
"name": ".NET Core SSH Attach ubu20",
@@ -8,7 +17,11 @@
"processId": "${command:pickRemoteProcess}",
"pipeTransport": {
"pipeProgram": "ssh",
- "pipeArgs": [ "-T", "-p 22006", "tim@localhost" ],
+ "pipeArgs": [
+ "-T",
+ "-p 22006",
+ "tim@localhost"
+ ],
"debuggerPath": "~/vsdbg-root/vsdbg",
"pipeCwd": "${workspaceRoot}",
"quoteArgs": true
@@ -17,7 +30,7 @@
"/home/tim/firewall-orchestrator/roles": "${workspaceRoot}"
},
"logging": {
- "logging.diagnosticsLog.protocolMessages": true
+ "logging.diagnosticsLog.protocolMessages": true
}
},
{
@@ -41,11 +54,62 @@
"moduleLoad": false
}
},
+ {
+ "name": "c#-MiddlewareServer - Hot Reload",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build_middleware",
+ "enableStepFiltering": false,
+ "program": "dotnet",
+ "args": [
+ "watch",
+ "run"
+ ],
+ "cwd": "${workspaceFolder}/roles/middleware/files/FWO.Middleware.Server",
+ "console": "internalConsole",
+ "stopAtEntry": false,
+ "requireExactSource": false,
+ "serverReadyAction": {
+ "action": "openExternally",
+ "pattern": "\\bNow listening on:\\s+(https?://\\S+)",
+ "uriFormat": "%s/swagger"
+ },
+ "env": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "logging": {
+ "moduleLoad": false
+ }
+ },
{
"name": "c#-Blazor UI",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build_UI",
+ "program": "${workspaceFolder}/roles/ui/files/FWO.UI/bin/Debug/net8.0/FWO.Ui.dll",
+ "args": [],
+ "cwd": "${workspaceFolder}/roles/ui/files/FWO.UI",
+ "stopAtEntry": false,
+ "requireExactSource": false,
+ "serverReadyAction": {
+ "action": "openExternally",
+ "pattern": "\\bNow listening on:\\s+(https?://\\S+)"
+ },
+ "env": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "sourceFileMap": {
+ "/Views": "${workspaceFolder}/Views"
+ },
+ "logging": {
+ "moduleLoad": false
+ }
+ },
+ {
+ "name": "c#-Blazor UI - Hot Reload",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build_UI",
"program": "dotnet",
"args": [
"watch",
@@ -100,18 +164,41 @@
"PYTHONPATH": "${PYTHONPATH}:${workspaceRoot}"
},
"args": [
- "-m6",
+ "-m1",
"-d1",
"-f",
"-s",
- //"-l",
- // "-c"
- //"-l250"
- // 41 - lab fortimanager
- //"-ihttps://fwodemodata.cactus.de/demo04_cpr8x.json",
- //"-ihttps://fwodemodata.cactus.de/demo01_fortiMgrLab.json"
- //"-ihttps://fwodemodata.cactus.de/demo05_fortiMgr2.json"
- //"-ihttps://fwodemodata.cactus.de/big/xxx.json",
+ // "-c",
+ // "-l66",
+ // "-l500",
+ // "-ihttps://fwodemodata.cactus.de/demo04_cpr8x.json",
+ // "-ihttps://fwodemodata.cactus.de/demo05_fortiMgr2.json"
+ // "-ihttps://fwodemodata.cactus.de/demo11-r82_v9.json",
+ ]
+ },
+ {
+ "name": "py-test-ruleorder",
+ "type": "debugpy",
+ "request": "launch",
+ "program": "${workspaceFolder}/roles/importer/files/test/ruleorder.py",
+ "console": "integratedTerminal",
+ "env": {
+ "PYTHONPATH": "${PYTHONPATH}:${workspaceRoot}"
+ },
+ "args": []
+ },
+ {
+ "name": "py-kimport",
+ "type": "debugpy",
+ "request": "launch",
+ "program": "${workspaceFolder}/roles/importer/files/importer/checkpointR8x/scripts/Python-nsPYm-v2/get_all_firewall_data.py",
+ "console": "integratedTerminal",
+ "env": {
+ "PYTHONPATH": "${PYTHONPATH}:${workspaceRoot}"
+ },
+ "args": [
+ "itsecorg",
+ "st8chel"
]
},
{
@@ -309,38 +396,49 @@
"-i/tmp/fworch-config.graphql"
]
},
+ // {
+ // "name": "py-cpr8x-autodiscovery",
+ // "type": "debugpy",
+ // "request": "launch",
+ // "program": "${workspaceFolder}/roles/importer/files/importer/checkpointR8x/auto-discover.py",
+ // "console": "integratedTerminal",
+ // "cwd": "${workspaceFolder}/roles/importer/files/importer/checkpointR8x",
+ // "env": {
+ // "PYTHONPATH": "${PYTHONPATH}:${workspaceRoot}"
+ // },
+ // "args": [
+ // "-a192.168.100.111",
+ // "-w/home/tim/secrets/cp_apiuser_pwd",
+ // "-uitsecorg",
+ // "-d4",
+ // "-fjson"
+ // ]
+ // },
{
- "name": "py-cpr8x-autodiscovery",
+ "name": "py-import-main-loop",
"type": "debugpy",
"request": "launch",
- "program": "${workspaceFolder}/roles/importer/files/importer/checkpointR8x/auto-discover.py",
+ "program": "${workspaceFolder}/roles/importer/files/importer/import-main-loop.py",
"console": "integratedTerminal",
- "cwd": "${workspaceFolder}/roles/importer/files/importer/checkpointR8x",
"env": {
"PYTHONPATH": "${PYTHONPATH}:${workspaceRoot}"
},
"args": [
- "-a192.168.100.110",
- "-w/home/tim/secrets/cp_apiuser_pwd",
- "-uitsecorg",
- "-d4",
- "-fjson"
+ //"-c",
+ //"-f",
+ "-d0"
]
},
{
- "name": "py-import-main-loop",
+ "name": "py-create-mock-config",
"type": "debugpy",
"request": "launch",
- "program": "${workspaceFolder}/roles/importer/files/importer/import-main-loop.py",
+ "program": "${workspaceFolder}/roles/importer/files/importer/test/tools/create_mock_config_file.py",
"console": "integratedTerminal",
+ "cwd": "${workspaceFolder}/roles/importer/files/importer",
"env": {
- "PYTHONPATH": "${PYTHONPATH}:${workspaceRoot}"
- },
- "args": [
- //"-c",
- //"-f",
- "-d0"
- ]
+ "PYTHONPATH": "${workspaceFolder}/roles/importer/files/importer:${env:PYTHONPATH}"
+ }
},
{
"name": "c#-FWO Test",
@@ -360,6 +458,12 @@
"type": "coreclr",
"request": "attach",
"processId": "${command:pickProcess}"
+ },
+ {
+ "name": "Create mock_config",
+ "type": "python",
+ "request": "launch",
+ "module": "roles.importer.files.test.mocking.mock_config"
}
]
}
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 65ff848758..4b7fd372e5 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -10,13 +10,22 @@
"files.associations": {
"launch.template.json": "jsonc"
},
- "python.testing.unittestEnabled": true,
- "python.testing.pytestEnabled": false,
+ "python.analysis.extraPaths": ["roles/importer/files/importer"],
"python.testing.unittestArgs": [
- "-v",
- "-s",
- "scripts/customizing/app_data_import",
- "-p",
- "test_*.py"
- ]
-}
+ "-v",
+ "-p",
+ "test_*.py",
+ "-s",
+ "./roles/importer/files/importer/test"
+ ],
+ "python.testing.pytestEnabled": false,
+ "python.testing.unittestEnabled": true,
+ "[python]": {
+ "editor.formatOnSave": true,
+ "editor.codeActionsOnSave": {
+ "source.fixAll": "explicit",
+ "source.organizeImports": "explicit"
+ },
+ "editor.defaultFormatter": "charliermarsh.ruff"
+ }
+}
\ No newline at end of file
diff --git a/.vscode/settings.local.template.json b/.vscode/settings.local.template.json
new file mode 100644
index 0000000000..b276514844
--- /dev/null
+++ b/.vscode/settings.local.template.json
@@ -0,0 +1,5 @@
+{
+ "test.unittests.csharp.verbose": true,
+ "test.unittests.python.verbose": true,
+ "log.compliancecheck.verbose": true
+}
diff --git a/LICENSE b/LICENSE
index 261eeb9e9f..be75dcc9a3 100644
--- a/LICENSE
+++ b/LICENSE
@@ -199,3 +199,50 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+
+## Third-Party Notices
+
+This project includes the following third-party components. License names use SPDX identifiers.
+
+Package/Library | Language | Version | License
+-- | -- | -- | --
+bunit | C# | 1.40.0 | MIT
+GraphQL.Client | C# | >=6.1.0 | MIT
+GraphQL.Client.Serializer.Newtonsoft | C# | >=6.1.0 | MIT
+GraphQL.Client.Serializer.SystemTextJson | C# | >=6.1.0 | MIT
+HtmlAgilityPack | C# | >=1.12.2 | MIT
+IPAddressRange | C# | >=6.2.0 | MPL-2.0
+IPNetwork2 | C# | 3.4.832 | BSD 2-Clause "Simplified" License
+MailKit | C# | >=4.13.0 | Apache-2.0
+Microsoft.AspNetCore.Authentication.JwtBearer | C# | >=8.0.15 | Apache-2.0
+Microsoft.AspNetCore.Components | C# | >=8.0.15 | Apache-2.0
+Microsoft.AspNetCore.Components.Authorization | C# | >=8.0.15 | Apache-2.0
+Microsoft.AspNetCore.Http | C# | >=2.3.0 | Apache-2.0
+Microsoft.IdentityModel.Tokens | C# | >=6.7.1 | MIT
+Microsoft.IdentityModel.Tokens | C# | >=8.14.0 | MIT
+Microsoft.NET.Test.Sdk | C# | >=17.14.1 | MIT
+Microsoft.Rest.ClientRuntime | C# | >=2.3.24 | MIT
+MimeKit | C# | >=4.13.0 | Apache-2.0
+Novell.Directory.Ldap.NETStandard | C# | >=4.0.0 | MIT
+NSubstitute | C# | >=5.3.0 | BSD-2-Clause
+NUnit | C# | >=4.4.0 | MIT
+NUnit3TestAdapter | C# | >=5.1.0 | MIT
+PuppeteerSharp | C# | >=20.2.2 | MIT
+RestSharp | C# | >=112.1.0 | Apache-2.0
+RestSharp.Serializers.NewtonsoftJson | C# | >=112.1.0 | Apache-2.0
+Swashbuckle.AspNetCore | C# | >=8.1.1 | MIT
+System.IdentityModel.Tokens.Jwt | C# | >=8.14.0 | MIT
+System.Text.Encodings.Web | C# | >=9.0.8 | MIT
+Phosphor icons | CSS | >=2.1.0 | [MIT](https://raw.githubusercontent.com/phosphor-icons/homepage/master/LICENSE)
+cryptography | python | >=40.0 | Apache-2.0 OR BSD-3-Clause
+graphql-core | python | >=3.0 | BSD-3-Clause
+jsonpickle | python | >=3.0 | BSD-3-Clause
+netaddr | python | >=1.0 | BSD-2-Clause
+netmiko | python | ? | MIT
+paramiko | python | ? | LGPL-2.1-only
+pydantic | python | >=2.0 | MIT
+PyJWT | python | ? | MIT
+pytest | python | >=7.0 | MIT
+python-dateutil | python | >=2.8 | BSD-3-Clause
+requests | python | >=2.0 | Apache-2.0
+urllib3 | python | >=2.0 | MIT
diff --git a/documentation/SBOM/github-sbom.json b/documentation/SBOM/github-sbom.json
new file mode 100644
index 0000000000..a2faf2c303
--- /dev/null
+++ b/documentation/SBOM/github-sbom.json
@@ -0,0 +1,4153 @@
+{
+ "spdxVersion": "SPDX-2.3",
+ "dataLicense": "CC0-1.0",
+ "SPDXID": "SPDXRef-DOCUMENT",
+ "name": "com.github.CactuseSecurity/firewall-orchestrator",
+ "documentNamespace": "https://spdx.org/spdxdocs/protobom/64153841-aba6-49d0-ac52-3faf21f93218",
+ "comment": "Exact versions could not be resolved for some packages. For more information: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-the-dependency-graph#dependencies-included.",
+ "creationInfo": {
+ "creators": [
+ "Tool: protobom-v0.0.0-20251111180015-44acb8c1ef9f+dirty",
+ "Tool: GitHub.com-Dependency-Graph",
+ "Tool: Automatic Dependency Submission"
+ ],
+ "created": "2025-11-11T20:34:11Z"
+ },
+ "packages": [
+ {
+ "name": "Microsoft.Extensions.Logging.Abstractions",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Logging.Abstractions-8.0.3-0d07ba",
+ "versionInfo": "8.0.3",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization for Standardization 1986",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Logging.Abstractions@8.0.3"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.AspNetCore.Components.Analyzers",
+ "SPDXID": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Analyzers-8.0.15-75b14f",
+ "versionInfo": "8.0.15",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation Contributors, Copyright (c) 1989, 1993 The Regents of the University of California, Copyright (c) 1990, 1993 The Regents of the University of California, Copyright (c) 1996-1998 John D. Polstra, Copyright (c) 1998 John D. Polstra, Copyright (c) 2000-2013 Julian Seward, Copyright (c) 2007 John Birrell (jb@freebsd.org), Copyright (c) 2011-2021 The Bootstrap Authors, Copyright (c) 2011-2021 Twitter, Inc., Copyright (c) 2013 - 2018 AngleSharp, Copyright (c) 2019-2023 The Bootstrap Authors",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.AspNetCore.Components.Analyzers@8.0.15"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Primitives",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Primitives-8.0.0-973e61",
+ "versionInfo": "8.0.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Primitives@8.0.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.AspNetCore.Components.Authorization",
+ "SPDXID": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Authorization-8.0.15-58c86a",
+ "versionInfo": "8.0.15",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation Contributors, Copyright (c) 1989, 1993 The Regents of the University of California, Copyright (c) 1990, 1993 The Regents of the University of California, Copyright (c) 1996-1998 John D. Polstra, Copyright (c) 1998 John D. Polstra, Copyright (c) 2000-2013 Julian Seward, Copyright (c) 2007 John Birrell (jb@freebsd.org), Copyright (c) 2011-2021 The Bootstrap Authors, Copyright (c) 2011-2021 Twitter, Inc., Copyright (c) 2013 - 2018 AngleSharp, Copyright (c) 2019-2023 The Bootstrap Authors",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.AspNetCore.Components.Authorization@8.0.15"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.AspNetCore.Authorization",
+ "SPDXID": "SPDXRef-nuget-Microsoft.AspNetCore.Authorization-8.0.15-59a19e",
+ "versionInfo": "8.0.15",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1989, 1993 The Regents of the University of California, Copyright (c) 1990, 1993 The Regents of the University of California, Copyright (c) 1996-1998 John D. Polstra, Copyright (c) 1998 John D. Polstra, Copyright (c) 2000-2013 Julian Seward, Copyright (c) 2007 James Newton-King, Copyright (c) 2007 John Birrell (jb@freebsd.org), Copyright (c) 2010-2019 Google LLC. http://angular.io/license, Copyright (c) 2011 Alex MacCaw (info@eribium.org), Copyright (c) 2011 Nicolas Gallagher (nicolas@nicolasgallagher.com), Copyright (c) 2011-2021 The Bootstrap Authors, Copyright (c) 2011-2021 Twitter, Inc., Copyright (c) 2013 - 2018 AngleSharp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014-2018 Michael Daines, Copyright (c) 2015, Google Inc., Copyright (c) 2016 Richard Morris, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2019 David Fowler, Copyright (c) 2019-2020 West Wind Technologies, Copyright (c) 2019-2023 The Bootstrap Authors, Copyright (c) Andrew Arnott, Copyright (c) HTML5 Boilerplate, Copyright (c) Microsoft Corporation, Copyright (c) Nicolas Gallagher and Jonathan Neal, Copyright (c) Sindre Sorhus \u003csindresorhus@gmail.com\u003e (https://sindresorhus.com), Copyright 2019 The gRPC Authors, Copyright Jorn Zaefferer, Copyright OpenJS Foundation and other contributors, https://openjsf.org",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.AspNetCore.Authorization@8.0.15"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.AspNetCore.Components",
+ "SPDXID": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-154912",
+ "versionInfo": "8.0.15",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation Contributors, Copyright (c) 1989, 1993 The Regents of the University of California, Copyright (c) 1990, 1993 The Regents of the University of California, Copyright (c) 1996-1998 John D. Polstra, Copyright (c) 1998 John D. Polstra, Copyright (c) 2000-2013 Julian Seward, Copyright (c) 2007 John Birrell (jb@freebsd.org), Copyright (c) 2011-2021 The Bootstrap Authors, Copyright (c) 2011-2021 Twitter, Inc., Copyright (c) 2013 - 2018 AngleSharp, Copyright (c) 2019-2023 The Bootstrap Authors",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.AspNetCore.Components@8.0.15"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Options",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Options-8.0.2-ed918b",
+ "versionInfo": "8.0.2",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Options@8.0.2"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.DependencyInjection.Abstractions",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection.Abstractions-8.0.2-934d86",
+ "versionInfo": "8.0.2",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.DependencyInjection.Abstractions@8.0.2"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.AspNetCore.Metadata",
+ "SPDXID": "SPDXRef-nuget-Microsoft.AspNetCore.Metadata-8.0.15-f13caf",
+ "versionInfo": "8.0.15",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1989, 1993 The Regents of the University of California, Copyright (c) 1990, 1993 The Regents of the University of California, Copyright (c) 1996-1998 John D. Polstra, Copyright (c) 1998 John D. Polstra, Copyright (c) 2000-2013 Julian Seward, Copyright (c) 2007 James Newton-King, Copyright (c) 2007 John Birrell (jb@freebsd.org), Copyright (c) 2010-2019 Google LLC. http://angular.io/license, Copyright (c) 2011 Alex MacCaw (info@eribium.org), Copyright (c) 2011 Nicolas Gallagher (nicolas@nicolasgallagher.com), Copyright (c) 2011-2021 The Bootstrap Authors, Copyright (c) 2011-2021 Twitter, Inc., Copyright (c) 2013 - 2018 AngleSharp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014-2018 Michael Daines, Copyright (c) 2015, Google Inc., Copyright (c) 2016 Richard Morris, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2019 David Fowler, Copyright (c) 2019-2020 West Wind Technologies, Copyright (c) 2019-2023 The Bootstrap Authors, Copyright (c) Andrew Arnott, Copyright (c) HTML5 Boilerplate, Copyright (c) Microsoft Corporation, Copyright (c) Nicolas Gallagher and Jonathan Neal, Copyright (c) Sindre Sorhus \u003csindresorhus@gmail.com\u003e (https://sindresorhus.com), Copyright 2019 The gRPC Authors, Copyright Jorn Zaefferer, Copyright OpenJS Foundation and other contributors, https://openjsf.org",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.AspNetCore.Metadata@8.0.15"
+ }
+ ]
+ },
+ {
+ "name": "bunit.core",
+ "SPDXID": "SPDXRef-nuget-bunit.core-1.40.0-44d971",
+ "versionInfo": "1.40.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "Copyright (c) .NET Foundation and Contributors",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/bunit.core@1.40.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.TestPlatform.ObjectModel",
+ "SPDXID": "SPDXRef-nuget-Microsoft.TestPlatform.ObjectModel-17.14.1-341464",
+ "versionInfo": "17.14.1",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.TestPlatform.ObjectModel@17.14.1"
+ }
+ ]
+ },
+ {
+ "name": "AngleSharp",
+ "SPDXID": "SPDXRef-nuget-AngleSharp-1.2.0-ec1ca1",
+ "versionInfo": "1.2.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "LicenseRef-scancode-unknown-license-reference AND MIT",
+ "copyrightText": "Copyright 2013-2024, AngleSharp, Copyright AngleSharp, 2013-2024",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/AngleSharp@1.2.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.AspNetCore.Components.WebAssembly.Authentication",
+ "SPDXID": "SPDXRef-nuget-Microsoft.AspNetCore.Components.WebAssembly.Authentication-8.0.11-d84320",
+ "versionInfo": "8.0.11",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation Contributors, Copyright (c) 1989, 1993 The Regents of the University of California, Copyright (c) 1990, 1993 The Regents of the University of California, Copyright (c) 1996-1998 John D. Polstra, Copyright (c) 1998 John D. Polstra, Copyright (c) 2000-2013 Julian Seward, Copyright (c) 2007 John Birrell (jb@freebsd.org), Copyright (c) 2011-2021 The Bootstrap Authors, Copyright (c) 2011-2021 Twitter, Inc., Copyright (c) 2013 - 2018 AngleSharp, Copyright (c) 2019-2023 The Bootstrap Authors",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.AspNetCore.Components.WebAssembly.Authentication@8.0.11"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.JSInterop",
+ "SPDXID": "SPDXRef-nuget-Microsoft.JSInterop-8.0.11-5ea157",
+ "versionInfo": "8.0.11",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1989, 1993 The Regents of the University of California, Copyright (c) 1990, 1993 The Regents of the University of California, Copyright (c) 1996-1998 John D. Polstra, Copyright (c) 1998 John D. Polstra, Copyright (c) 2000-2013 Julian Seward, Copyright (c) 2007 James Newton-King, Copyright (c) 2007 John Birrell (jb@freebsd.org), Copyright (c) 2010-2019 Google LLC. http://angular.io/license, Copyright (c) 2011 Alex MacCaw (info@eribium.org), Copyright (c) 2011 Nicolas Gallagher (nicolas@nicolasgallagher.com), Copyright (c) 2011-2021 The Bootstrap Authors, Copyright (c) 2011-2021 Twitter, Inc., Copyright (c) 2013 - 2018 AngleSharp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014-2018 Michael Daines, Copyright (c) 2015, Google Inc., Copyright (c) 2016 Richard Morris, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2019 David Fowler, Copyright (c) 2019-2020 West Wind Technologies, Copyright (c) 2019-2023 The Bootstrap Authors, Copyright (c) Andrew Arnott, Copyright (c) HTML5 Boilerplate, Copyright (c) Microsoft Corporation, Copyright (c) Nicolas Gallagher and Jonathan Neal, Copyright (c) Sindre Sorhus \u003csindresorhus@gmail.com\u003e (https://sindresorhus.com), Copyright 2019 The gRPC Authors, Copyright Jorn Zaefferer, Copyright OpenJS Foundation and other contributors, https://openjsf.org",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.JSInterop@8.0.11"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Localization.Abstractions",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Localization.Abstractions-8.0.11-4cb515",
+ "versionInfo": "8.0.11",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1989, 1993 The Regents of the University of California, Copyright (c) 1990, 1993 The Regents of the University of California, Copyright (c) 1996-1998 John D. Polstra, Copyright (c) 1998 John D. Polstra, Copyright (c) 2000-2013 Julian Seward, Copyright (c) 2007 James Newton-King, Copyright (c) 2007 John Birrell (jb@freebsd.org), Copyright (c) 2010-2019 Google LLC. http://angular.io/license, Copyright (c) 2011 Alex MacCaw (info@eribium.org), Copyright (c) 2011 Nicolas Gallagher (nicolas@nicolasgallagher.com), Copyright (c) 2011-2021 The Bootstrap Authors, Copyright (c) 2011-2021 Twitter, Inc., Copyright (c) 2013 - 2018 AngleSharp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014-2018 Michael Daines, Copyright (c) 2015, Google Inc., Copyright (c) 2016 Richard Morris, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2019 David Fowler, Copyright (c) 2019-2020 West Wind Technologies, Copyright (c) 2019-2023 The Bootstrap Authors, Copyright (c) Andrew Arnott, Copyright (c) HTML5 Boilerplate, Copyright (c) Microsoft Corporation, Copyright (c) Nicolas Gallagher and Jonathan Neal, Copyright (c) Sindre Sorhus \u003csindresorhus@gmail.com\u003e (https://sindresorhus.com), Copyright 2019 The gRPC Authors, Copyright Jorn Zaefferer, Copyright OpenJS Foundation and other contributors, https://openjsf.org",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Localization.Abstractions@8.0.11"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.NET.Test.Sdk",
+ "SPDXID": "SPDXRef-nuget-Microsoft.NET.Test.Sdk-17.14.1-86ab34",
+ "versionInfo": "17.14.1",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.NET.Test.Sdk@17.14.1"
+ }
+ ]
+ },
+ {
+ "name": "System.Text.Json",
+ "SPDXID": "SPDXRef-nuget-System.Text.Json-8.0.5-74b06c",
+ "versionInfo": "8.0.5",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization, Portions (c) International Organization for Standardization 1986",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/System.Text.Json@8.0.5"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.AspNetCore.Components.WebAssembly",
+ "SPDXID": "SPDXRef-nuget-Microsoft.AspNetCore.Components.WebAssembly-8.0.11-b60482",
+ "versionInfo": "8.0.11",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation Contributors, Copyright (c) 1989, 1993 The Regents of the University of California, Copyright (c) 1990, 1993 The Regents of the University of California, Copyright (c) 1996-1998 John D. Polstra, Copyright (c) 1998 John D. Polstra, Copyright (c) 2000-2013 Julian Seward, Copyright (c) 2007 John Birrell (jb@freebsd.org), Copyright (c) 2011-2021 The Bootstrap Authors, Copyright (c) 2011-2021 Twitter, Inc., Copyright (c) 2013 - 2018 AngleSharp, Copyright (c) 2019-2023 The Bootstrap Authors",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.AspNetCore.Components.WebAssembly@8.0.11"
+ }
+ ]
+ },
+ {
+ "name": "System.IO.Pipelines",
+ "SPDXID": "SPDXRef-nuget-System.IO.Pipelines-8.0.0-f9572b",
+ "versionInfo": "8.0.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization, Portions (c) International Organization for Standardization 1986",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/System.IO.Pipelines@8.0.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.AspNetCore.Components.Analyzers",
+ "SPDXID": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Analyzers-8.0.15-67a21b",
+ "versionInfo": "8.0.15",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation Contributors, Copyright (c) 1989, 1993 The Regents of the University of California, Copyright (c) 1990, 1993 The Regents of the University of California, Copyright (c) 1996-1998 John D. Polstra, Copyright (c) 1998 John D. Polstra, Copyright (c) 2000-2013 Julian Seward, Copyright (c) 2007 John Birrell (jb@freebsd.org), Copyright (c) 2011-2021 The Bootstrap Authors, Copyright (c) 2011-2021 Twitter, Inc., Copyright (c) 2013 - 2018 AngleSharp, Copyright (c) 2019-2023 The Bootstrap Authors",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.AspNetCore.Components.Analyzers@8.0.15"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Configuration.Json",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Configuration.Json-8.0.1-bd4bb5",
+ "versionInfo": "8.0.1",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization, Portions (c) International Organization for Standardization 1986",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Configuration.Json@8.0.1"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Testing.Platform.MSBuild",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Testing.Platform.MSBuild-1.7.3-c5050d",
+ "versionInfo": "1.7.3",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Testing.Platform.MSBuild@1.7.3"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Options",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Options-8.0.2-553265",
+ "versionInfo": "8.0.2",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Options@8.0.2"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.FileSystemGlobbing",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.FileSystemGlobbing-8.0.0-df8146",
+ "versionInfo": "8.0.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization, Portions (c) International Organization for Standardization 1986",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.FileSystemGlobbing@8.0.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.AspNetCore.Components.Forms",
+ "SPDXID": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Forms-8.0.11-b3ce0c",
+ "versionInfo": "8.0.11",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation Contributors, Copyright (c) 1989, 1993 The Regents of the University of California, Copyright (c) 1990, 1993 The Regents of the University of California, Copyright (c) 1996-1998 John D. Polstra, Copyright (c) 1998 John D. Polstra, Copyright (c) 2000-2013 Julian Seward, Copyright (c) 2007 John Birrell (jb@freebsd.org), Copyright (c) 2011-2021 The Bootstrap Authors, Copyright (c) 2011-2021 Twitter, Inc., Copyright (c) 2013 - 2018 AngleSharp, Copyright (c) 2019-2023 The Bootstrap Authors",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.AspNetCore.Components.Forms@8.0.11"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.AspNetCore.Components",
+ "SPDXID": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-d179a3",
+ "versionInfo": "8.0.15",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation Contributors, Copyright (c) 1989, 1993 The Regents of the University of California, Copyright (c) 1990, 1993 The Regents of the University of California, Copyright (c) 1996-1998 John D. Polstra, Copyright (c) 1998 John D. Polstra, Copyright (c) 2000-2013 Julian Seward, Copyright (c) 2007 John Birrell (jb@freebsd.org), Copyright (c) 2011-2021 The Bootstrap Authors, Copyright (c) 2011-2021 Twitter, Inc., Copyright (c) 2013 - 2018 AngleSharp, Copyright (c) 2019-2023 The Bootstrap Authors",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.AspNetCore.Components@8.0.15"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.JSInterop.WebAssembly",
+ "SPDXID": "SPDXRef-nuget-Microsoft.JSInterop.WebAssembly-8.0.11-7d7f03",
+ "versionInfo": "8.0.11",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation Contributors, Copyright (c) 1989, 1993 The Regents of the University of California, Copyright (c) 1990, 1993 The Regents of the University of California, Copyright (c) 1996-1998 John D. Polstra, Copyright (c) 1998 John D. Polstra, Copyright (c) 2000-2013 Julian Seward, Copyright (c) 2007 John Birrell (jb@freebsd.org), Copyright (c) 2011-2021 The Bootstrap Authors, Copyright (c) 2011-2021 Twitter, Inc., Copyright (c) 2013 - 2018 AngleSharp, Copyright (c) 2019-2023 The Bootstrap Authors",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.JSInterop.WebAssembly@8.0.11"
+ }
+ ]
+ },
+ {
+ "name": "PuppeteerSharp",
+ "SPDXID": "SPDXRef-nuget-PuppeteerSharp-20.2.2-21f7e5",
+ "versionInfo": "20.2.2",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/PuppeteerSharp@20.2.2"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Primitives",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Primitives-8.0.0-7bb3ef",
+ "versionInfo": "8.0.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Primitives@8.0.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.ApplicationInsights",
+ "SPDXID": "SPDXRef-nuget-Microsoft.ApplicationInsights-2.23.0-92127e",
+ "versionInfo": "2.23.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.ApplicationInsights@2.23.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.TestPlatform.TestHost",
+ "SPDXID": "SPDXRef-nuget-Microsoft.TestPlatform.TestHost-17.14.1-5a76b2",
+ "versionInfo": "17.14.1",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation and Contributors, Copyright (c) 2007 James Newton-King, Copyright (c) 2008 - 2011 Novell, Inc., Copyright (c) 2008 - 2015 Jb Evain, Copyright 1995-2022 Mark Adler",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.TestPlatform.TestHost@17.14.1"
+ }
+ ]
+ },
+ {
+ "name": "System.Collections.Immutable",
+ "SPDXID": "SPDXRef-nuget-System.Collections.Immutable-8.0.0-00b56b",
+ "versionInfo": "8.0.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization, Portions (c) International Organization for Standardization 1986",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/System.Collections.Immutable@8.0.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Configuration.Binder",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Configuration.Binder-8.0.2-bb6751",
+ "versionInfo": "8.0.2",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Configuration.Binder@8.0.2"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Logging.Abstractions",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Logging.Abstractions-8.0.3-f5387c",
+ "versionInfo": "8.0.3",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization for Standardization 1986",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Logging.Abstractions@8.0.3"
+ }
+ ]
+ },
+ {
+ "name": "AngleSharp.Diffing",
+ "SPDXID": "SPDXRef-nuget-AngleSharp.Diffing-1.0.0-08ab89",
+ "versionInfo": "1.0.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/AngleSharp.Diffing@1.0.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.FileProviders.Abstractions",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.FileProviders.Abstractions-8.0.0-3e71db",
+ "versionInfo": "8.0.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization, Portions (c) International Organization for Standardization 1986",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.FileProviders.Abstractions@8.0.0"
+ }
+ ]
+ },
+ {
+ "name": "NUnit",
+ "SPDXID": "SPDXRef-nuget-NUnit-4.4.0-d51504",
+ "versionInfo": "4.4.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT AND zlib-acknowledgement",
+ "copyrightText": "Copyright (c) 2000-2002 Philip A. Craig, Copyright (c) 2002-2004 James W. Newkirk, Michael C. Two, Alexei A. Vorontsov, Copyright (c) 2002-2014 Charlie Poole, Copyright (c) 2008 Jonathan Pryor, Novell, Copyright (c) 2024 Charlie Poole, Rob Prouse, Copyright (c) Charlie Poole, Rob Prouse and Contributors, WCopyright (c) Charlie Poole, Rob Prouse and Contributors",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/NUnit@4.4.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.DependencyInjection.Abstractions",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection.Abstractions-8.0.2-5a630c",
+ "versionInfo": "8.0.2",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.DependencyInjection.Abstractions@8.0.2"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.TestPlatform.AdapterUtilities",
+ "SPDXID": "SPDXRef-nuget-Microsoft.TestPlatform.AdapterUtilities-17.13.0-22a1b0",
+ "versionInfo": "17.13.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.TestPlatform.AdapterUtilities@17.13.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.AspNetCore.Authorization",
+ "SPDXID": "SPDXRef-nuget-Microsoft.AspNetCore.Authorization-8.0.15-457dff",
+ "versionInfo": "8.0.15",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1989, 1993 The Regents of the University of California, Copyright (c) 1990, 1993 The Regents of the University of California, Copyright (c) 1996-1998 John D. Polstra, Copyright (c) 1998 John D. Polstra, Copyright (c) 2000-2013 Julian Seward, Copyright (c) 2007 James Newton-King, Copyright (c) 2007 John Birrell (jb@freebsd.org), Copyright (c) 2010-2019 Google LLC. http://angular.io/license, Copyright (c) 2011 Alex MacCaw (info@eribium.org), Copyright (c) 2011 Nicolas Gallagher (nicolas@nicolasgallagher.com), Copyright (c) 2011-2021 The Bootstrap Authors, Copyright (c) 2011-2021 Twitter, Inc., Copyright (c) 2013 - 2018 AngleSharp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014-2018 Michael Daines, Copyright (c) 2015, Google Inc., Copyright (c) 2016 Richard Morris, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2019 David Fowler, Copyright (c) 2019-2020 West Wind Technologies, Copyright (c) 2019-2023 The Bootstrap Authors, Copyright (c) Andrew Arnott, Copyright (c) HTML5 Boilerplate, Copyright (c) Microsoft Corporation, Copyright (c) Nicolas Gallagher and Jonathan Neal, Copyright (c) Sindre Sorhus \u003csindresorhus@gmail.com\u003e (https://sindresorhus.com), Copyright 2019 The gRPC Authors, Copyright Jorn Zaefferer, Copyright OpenJS Foundation and other contributors, https://openjsf.org",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.AspNetCore.Authorization@8.0.15"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Testing.Platform",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Testing.Platform-1.7.3-984f53",
+ "versionInfo": "1.7.3",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Testing.Platform@1.7.3"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.IdentityModel.Tokens",
+ "SPDXID": "SPDXRef-nuget-Microsoft.IdentityModel.Tokens-8.14.0-a883d6",
+ "versionInfo": "8.14.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) Microsoft Corporation",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.IdentityModel.Tokens@8.14.0"
+ }
+ ]
+ },
+ {
+ "name": "AngleSharp.Css",
+ "SPDXID": "SPDXRef-nuget-AngleSharp.Css-1.0.0-beta.154-30e617",
+ "versionInfo": "1.0.0-beta.154",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "LicenseRef-scancode-unknown-license-reference AND MIT",
+ "copyrightText": "Copyright 2016-2025, AngleSharp, Copyright AngleSharp, 2013-2019",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/AngleSharp.Css@1.0.0-beta.154"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Configuration.FileExtensions",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Configuration.FileExtensions-8.0.1-6b3a2e",
+ "versionInfo": "8.0.1",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Configuration.FileExtensions@8.0.1"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.FileProviders.Physical",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.FileProviders.Physical-8.0.0-628894",
+ "versionInfo": "8.0.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization, Portions (c) International Organization for Standardization 1986",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.FileProviders.Physical@8.0.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.IdentityModel.Abstractions",
+ "SPDXID": "SPDXRef-nuget-Microsoft.IdentityModel.Abstractions-8.14.0-6e2c7d",
+ "versionInfo": "8.14.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) Microsoft Corporation",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.IdentityModel.Abstractions@8.14.0"
+ }
+ ]
+ },
+ {
+ "name": "Newtonsoft.Json",
+ "SPDXID": "SPDXRef-nuget-Newtonsoft.Json-13.0.3-7c98bf",
+ "versionInfo": "13.0.3",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "Copyright (c) 2007 James Newton-King, Copyright (c) James Newton-King 2008, Copyright James Newton-King 2008, Copyright James Newton-King 2008 Json.NET",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Newtonsoft.Json@13.0.3"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.DependencyInjection",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection-8.0.1-276050",
+ "versionInfo": "8.0.1",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.DependencyInjection@8.0.1"
+ }
+ ]
+ },
+ {
+ "name": "bunit.web",
+ "SPDXID": "SPDXRef-nuget-bunit.web-1.40.0-b4ac35",
+ "versionInfo": "1.40.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "Copyright (c) .NET Foundation and Contributors",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/bunit.web@1.40.0"
+ }
+ ]
+ },
+ {
+ "name": "bunit",
+ "SPDXID": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "versionInfo": "1.40.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "Copyright (c) .NET Foundation and Contributors",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/bunit@1.40.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Configuration",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Configuration-8.0.0-dce331",
+ "versionInfo": "8.0.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Configuration@8.0.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.AspNetCore.Components.Authorization",
+ "SPDXID": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Authorization-8.0.15-c2e5e2",
+ "versionInfo": "8.0.15",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation Contributors, Copyright (c) 1989, 1993 The Regents of the University of California, Copyright (c) 1990, 1993 The Regents of the University of California, Copyright (c) 1996-1998 John D. Polstra, Copyright (c) 1998 John D. Polstra, Copyright (c) 2000-2013 Julian Seward, Copyright (c) 2007 John Birrell (jb@freebsd.org), Copyright (c) 2011-2021 The Bootstrap Authors, Copyright (c) 2011-2021 Twitter, Inc., Copyright (c) 2013 - 2018 AngleSharp, Copyright (c) 2019-2023 The Bootstrap Authors",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.AspNetCore.Components.Authorization@8.0.15"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.AspNetCore.Metadata",
+ "SPDXID": "SPDXRef-nuget-Microsoft.AspNetCore.Metadata-8.0.15-7088bb",
+ "versionInfo": "8.0.15",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1989, 1993 The Regents of the University of California, Copyright (c) 1990, 1993 The Regents of the University of California, Copyright (c) 1996-1998 John D. Polstra, Copyright (c) 1998 John D. Polstra, Copyright (c) 2000-2013 Julian Seward, Copyright (c) 2007 James Newton-King, Copyright (c) 2007 John Birrell (jb@freebsd.org), Copyright (c) 2010-2019 Google LLC. http://angular.io/license, Copyright (c) 2011 Alex MacCaw (info@eribium.org), Copyright (c) 2011 Nicolas Gallagher (nicolas@nicolasgallagher.com), Copyright (c) 2011-2021 The Bootstrap Authors, Copyright (c) 2011-2021 Twitter, Inc., Copyright (c) 2013 - 2018 AngleSharp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014-2018 Michael Daines, Copyright (c) 2015, Google Inc., Copyright (c) 2016 Richard Morris, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2019 David Fowler, Copyright (c) 2019-2020 West Wind Technologies, Copyright (c) 2019-2023 The Bootstrap Authors, Copyright (c) Andrew Arnott, Copyright (c) HTML5 Boilerplate, Copyright (c) Microsoft Corporation, Copyright (c) Nicolas Gallagher and Jonathan Neal, Copyright (c) Sindre Sorhus \u003csindresorhus@gmail.com\u003e (https://sindresorhus.com), Copyright 2019 The gRPC Authors, Copyright Jorn Zaefferer, Copyright OpenJS Foundation and other contributors, https://openjsf.org",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.AspNetCore.Metadata@8.0.15"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Caching.Memory",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Caching.Memory-8.0.1-24ebac",
+ "versionInfo": "8.0.1",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization, Portions (c) International Organization for Standardization 1986",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Caching.Memory@8.0.1"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.IdentityModel.Logging",
+ "SPDXID": "SPDXRef-nuget-Microsoft.IdentityModel.Logging-8.14.0-44c2b4",
+ "versionInfo": "8.14.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) Microsoft Corporation",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.IdentityModel.Logging@8.14.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Testing.Extensions.TrxReport.Abstractions",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Testing.Extensions.TrxReport.Abstractions-1.7.3-14ef65",
+ "versionInfo": "1.7.3",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Testing.Extensions.TrxReport.Abstractions@1.7.3"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.CodeCoverage",
+ "SPDXID": "SPDXRef-nuget-Microsoft.CodeCoverage-17.14.1-1dfedf",
+ "versionInfo": "17.14.1",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) 2008 - 2011 Novell, Inc., Copyright (c) 2008 - 2015 Jb Evain, Copyright (c) Microsoft, Copyright (c) Microsoft Corporation, Copyright 1995-2022 Mark Adler, Copyright 2008 - 2018 Jb Evain",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.CodeCoverage@17.14.1"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Configuration.Abstractions",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Configuration.Abstractions-8.0.0-639bd0",
+ "versionInfo": "8.0.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization, Portions (c) International Organization for Standardization 1986",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Configuration.Abstractions@8.0.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Logging",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Logging-8.0.1-d391ad",
+ "versionInfo": "8.0.1",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization, Portions (c) International Organization for Standardization 1986",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Logging@8.0.1"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Testing.Extensions.VSTestBridge",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Testing.Extensions.VSTestBridge-1.7.3-e3b7da",
+ "versionInfo": "1.7.3",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Testing.Extensions.VSTestBridge@1.7.3"
+ }
+ ]
+ },
+ {
+ "name": "System.Diagnostics.DiagnosticSource",
+ "SPDXID": "SPDXRef-nuget-System.Diagnostics.DiagnosticSource-5.0.0-31c6d8",
+ "versionInfo": "5.0.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson., (c) 2008 VeriSign, Inc., (c) Microsoft Corporation., Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) .NET Foundation., Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass., Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass. To, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft. To, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2007 James Newton-King, Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors., Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011, Google Inc., Copyright (c) 2012-2014, Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors., Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip., Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) The Internet Society (2003)., Copyright (c) The Internet Society 1997., Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers THIS WORK IS PROVIDED AS, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California., Copyright 2012 the V8 project, Copyright 2018 Daniel Lemire, Portions (c) International Organization",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/System.Diagnostics.DiagnosticSource@5.0.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Testing.Extensions.Telemetry",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Testing.Extensions.Telemetry-1.7.3-380d69",
+ "versionInfo": "1.7.3",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Testing.Extensions.Telemetry@1.7.3"
+ }
+ ]
+ },
+ {
+ "name": "System.Reflection.Metadata",
+ "SPDXID": "SPDXRef-nuget-System.Reflection.Metadata-8.0.0-2fa35d",
+ "versionInfo": "8.0.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Gets the Copyright Table, Portions (c) International Organization, Portions (c) International Organization for Standardization 1986",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/System.Reflection.Metadata@8.0.0"
+ }
+ ]
+ },
+ {
+ "name": "NUnit3TestAdapter",
+ "SPDXID": "SPDXRef-nuget-NUnit3TestAdapter-5.1.0-5f80ff",
+ "versionInfo": "5.1.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 2022 Charlie Poole, Rob Prouse, Copyright (c) 2011-2021 Charlie Poole, 2014-2025 Terje Sandstrom, Copyright (c) 2021 Charlie Poole, Copyright (c) 2021 Charlie Poole Readonly, Copyright (c) 2022 Charlie Poole, Rob Prouse, Copyright 2011-2021 Charlie Poole, 2014-2025 Terje Sandstrom",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/NUnit3TestAdapter@5.1.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.AspNetCore.Components.Web",
+ "SPDXID": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Web-8.0.11-3380f9",
+ "versionInfo": "8.0.11",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation Contributors, Copyright (c) 1989, 1993 The Regents of the University of California, Copyright (c) 1990, 1993 The Regents of the University of California, Copyright (c) 1996-1998 John D. Polstra, Copyright (c) 1998 John D. Polstra, Copyright (c) 2000-2013 Julian Seward, Copyright (c) 2007 John Birrell (jb@freebsd.org), Copyright (c) 2011-2021 The Bootstrap Authors, Copyright (c) 2011-2021 Twitter, Inc., Copyright (c) 2013 - 2018 AngleSharp, Copyright (c) 2019-2023 The Bootstrap Authors",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.AspNetCore.Components.Web@8.0.11"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Caching.Abstractions",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Caching.Abstractions-8.0.0-82b542",
+ "versionInfo": "8.0.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization, Portions (c) International Organization for Standardization 1986",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Caching.Abstractions@8.0.0"
+ }
+ ]
+ },
+ {
+ "name": "PuppeteerSharp",
+ "SPDXID": "SPDXRef-nuget-PuppeteerSharp-20.2.2-d76daa",
+ "versionInfo": "20.2.2",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/PuppeteerSharp@20.2.2"
+ }
+ ]
+ },
+ {
+ "name": "HtmlAgilityPack",
+ "SPDXID": "SPDXRef-nuget-HtmlAgilityPack-1.12.2-e4ed5d",
+ "versionInfo": "1.12.2",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "Copyright (c) ZZZ Projects Inc., Copyright ZZZ Projects Inc.",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/HtmlAgilityPack@1.12.2"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.DependencyInjection.Abstractions",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection.Abstractions-8.0.2-fa587a",
+ "versionInfo": "8.0.2",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.DependencyInjection.Abstractions@8.0.2"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.DependencyInjection",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection-8.0.0-76ea4d",
+ "versionInfo": "8.0.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization, Portions (c) International Organization for Standardization 1986",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.DependencyInjection@8.0.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Logging.Abstractions",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Logging.Abstractions-8.0.3-c90f02",
+ "versionInfo": "8.0.3",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization for Standardization 1986",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Logging.Abstractions@8.0.3"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Logging",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Logging-8.0.0-c71dd9",
+ "versionInfo": "8.0.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization, Portions (c) International Organization for Standardization 1986",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Logging@8.0.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Options",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Options-8.0.2-f33fde",
+ "versionInfo": "8.0.2",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Options@8.0.2"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Primitives",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Primitives-8.0.0-0353ed",
+ "versionInfo": "8.0.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Primitives@8.0.0"
+ }
+ ]
+ },
+ {
+ "name": "IPAddressRange",
+ "SPDXID": "SPDXRef-nuget-IPAddressRange-6.2.0-96c860",
+ "versionInfo": "6.2.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MPL-2.0",
+ "copyrightText": "Copyright (c) 2012-2024 J.Sakamoto, Mozilla, Copyright 2012-2024 J.Sakamoto, Mozilla",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/IPAddressRange@6.2.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.IdentityModel.Logging",
+ "SPDXID": "SPDXRef-nuget-Microsoft.IdentityModel.Logging-8.14.0-b11f58",
+ "versionInfo": "8.14.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) Microsoft Corporation",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.IdentityModel.Logging@8.14.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.IdentityModel.Tokens",
+ "SPDXID": "SPDXRef-nuget-Microsoft.IdentityModel.Tokens-8.14.0-8284c6",
+ "versionInfo": "8.14.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) Microsoft Corporation",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.IdentityModel.Tokens@8.14.0"
+ }
+ ]
+ },
+ {
+ "name": "System.IdentityModel.Tokens.Jwt",
+ "SPDXID": "SPDXRef-nuget-System.IdentityModel.Tokens.Jwt-8.14.0-62ac6a",
+ "versionInfo": "8.14.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) Microsoft Corporation",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/System.IdentityModel.Tokens.Jwt@8.14.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.DependencyInjection.Abstractions",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection.Abstractions-8.0.0-88c4e5",
+ "versionInfo": "8.0.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.DependencyInjection.Abstractions@8.0.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Logging.Abstractions",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Logging.Abstractions-8.0.0-145c39",
+ "versionInfo": "8.0.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Logging.Abstractions@8.0.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.IdentityModel.Abstractions",
+ "SPDXID": "SPDXRef-nuget-Microsoft.IdentityModel.Abstractions-8.14.0-dada85",
+ "versionInfo": "8.14.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) Microsoft Corporation",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.IdentityModel.Abstractions@8.14.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.IdentityModel.JsonWebTokens",
+ "SPDXID": "SPDXRef-nuget-Microsoft.IdentityModel.JsonWebTokens-8.14.0-3e0bac",
+ "versionInfo": "8.14.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) Microsoft Corporation",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.IdentityModel.JsonWebTokens@8.14.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Primitives",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Primitives-8.0.0-07a9d5",
+ "versionInfo": "8.0.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Primitives@8.0.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Rest.ClientRuntime",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Rest.ClientRuntime-2.3.24-508401",
+ "versionInfo": "2.3.24",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 2008 VeriSign, Inc., (c) Microsoft Corporation, Copyright (c) 2019 Microsoft, Copyright (c) Microsoft Corporation",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Rest.ClientRuntime@2.3.24"
+ }
+ ]
+ },
+ {
+ "name": "RestSharp.Serializers.NewtonsoftJson",
+ "SPDXID": "SPDXRef-nuget-RestSharp.Serializers.NewtonsoftJson-112.1.0-1f0a6b",
+ "versionInfo": "112.1.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "Apache-2.0",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/RestSharp.Serializers.NewtonsoftJson@112.1.0"
+ }
+ ]
+ },
+ {
+ "name": "RestSharp",
+ "SPDXID": "SPDXRef-nuget-RestSharp-112.1.0-839660",
+ "versionInfo": "112.1.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "Apache-2.0",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/RestSharp@112.1.0"
+ }
+ ]
+ },
+ {
+ "name": "GraphQL.Primitives",
+ "SPDXID": "SPDXRef-nuget-GraphQL.Primitives-6.1.0-8b54bf",
+ "versionInfo": "6.1.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "Copyright (c) 2017 graphql-dotnet",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/GraphQL.Primitives@6.1.0"
+ }
+ ]
+ },
+ {
+ "name": "GraphQL.Client.Abstractions",
+ "SPDXID": "SPDXRef-nuget-GraphQL.Client.Abstractions-6.1.0-132a88",
+ "versionInfo": "6.1.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "Copyright (c) 2017 graphql-dotnet",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/GraphQL.Client.Abstractions@6.1.0"
+ }
+ ]
+ },
+ {
+ "name": "GraphQL.Client",
+ "SPDXID": "SPDXRef-nuget-GraphQL.Client-6.1.0-7c0c75",
+ "versionInfo": "6.1.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "Copyright (c) 2017 graphql-dotnet",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/GraphQL.Client@6.1.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.AspNetCore.Components",
+ "SPDXID": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-259cdf",
+ "versionInfo": "8.0.15",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation Contributors, Copyright (c) 1989, 1993 The Regents of the University of California, Copyright (c) 1990, 1993 The Regents of the University of California, Copyright (c) 1996-1998 John D. Polstra, Copyright (c) 1998 John D. Polstra, Copyright (c) 2000-2013 Julian Seward, Copyright (c) 2007 John Birrell (jb@freebsd.org), Copyright (c) 2011-2021 The Bootstrap Authors, Copyright (c) 2011-2021 Twitter, Inc., Copyright (c) 2013 - 2018 AngleSharp, Copyright (c) 2019-2023 The Bootstrap Authors",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.AspNetCore.Components@8.0.15"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.DependencyInjection.Abstractions",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection.Abstractions-8.0.2-b43535",
+ "versionInfo": "8.0.2",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.DependencyInjection.Abstractions@8.0.2"
+ }
+ ]
+ },
+ {
+ "name": "GraphQL.Client.Abstractions.Websocket",
+ "SPDXID": "SPDXRef-nuget-GraphQL.Client.Abstractions.Websocket-6.1.0-604e8e",
+ "versionInfo": "6.1.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "Copyright (c) 2017 graphql-dotnet",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/GraphQL.Client.Abstractions.Websocket@6.1.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Options",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Options-8.0.2-3ddafa",
+ "versionInfo": "8.0.2",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Options@8.0.2"
+ }
+ ]
+ },
+ {
+ "name": "GraphQL.Client.Serializer.Newtonsoft",
+ "SPDXID": "SPDXRef-nuget-GraphQL.Client.Serializer.Newtonsoft-6.1.0-9b01fa",
+ "versionInfo": "6.1.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "Copyright (c) 2017 graphql-dotnet",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/GraphQL.Client.Serializer.Newtonsoft@6.1.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.AspNetCore.Authorization",
+ "SPDXID": "SPDXRef-nuget-Microsoft.AspNetCore.Authorization-8.0.15-6b8922",
+ "versionInfo": "8.0.15",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1989, 1993 The Regents of the University of California, Copyright (c) 1990, 1993 The Regents of the University of California, Copyright (c) 1996-1998 John D. Polstra, Copyright (c) 1998 John D. Polstra, Copyright (c) 2000-2013 Julian Seward, Copyright (c) 2007 James Newton-King, Copyright (c) 2007 John Birrell (jb@freebsd.org), Copyright (c) 2010-2019 Google LLC. http://angular.io/license, Copyright (c) 2011 Alex MacCaw (info@eribium.org), Copyright (c) 2011 Nicolas Gallagher (nicolas@nicolasgallagher.com), Copyright (c) 2011-2021 The Bootstrap Authors, Copyright (c) 2011-2021 Twitter, Inc., Copyright (c) 2013 - 2018 AngleSharp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014-2018 Michael Daines, Copyright (c) 2015, Google Inc., Copyright (c) 2016 Richard Morris, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2019 David Fowler, Copyright (c) 2019-2020 West Wind Technologies, Copyright (c) 2019-2023 The Bootstrap Authors, Copyright (c) Andrew Arnott, Copyright (c) HTML5 Boilerplate, Copyright (c) Microsoft Corporation, Copyright (c) Nicolas Gallagher and Jonathan Neal, Copyright (c) Sindre Sorhus \u003csindresorhus@gmail.com\u003e (https://sindresorhus.com), Copyright 2019 The gRPC Authors, Copyright Jorn Zaefferer, Copyright OpenJS Foundation and other contributors, https://openjsf.org",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.AspNetCore.Authorization@8.0.15"
+ }
+ ]
+ },
+ {
+ "name": "Newtonsoft.Json",
+ "SPDXID": "SPDXRef-nuget-Newtonsoft.Json-13.0.3-631925",
+ "versionInfo": "13.0.3",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "Copyright (c) 2007 James Newton-King, Copyright (c) James Newton-King 2008, Copyright James Newton-King 2008, Copyright James Newton-King 2008 Json.NET",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Newtonsoft.Json@13.0.3"
+ }
+ ]
+ },
+ {
+ "name": "GraphQL.Client.Serializer.SystemTextJson",
+ "SPDXID": "SPDXRef-nuget-GraphQL.Client.Serializer.SystemTextJson-6.1.0-c25e33",
+ "versionInfo": "6.1.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "Copyright (c) 2017 graphql-dotnet",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/GraphQL.Client.Serializer.SystemTextJson@6.1.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.AspNetCore.Components.Analyzers",
+ "SPDXID": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Analyzers-8.0.15-89dc0e",
+ "versionInfo": "8.0.15",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation Contributors, Copyright (c) 1989, 1993 The Regents of the University of California, Copyright (c) 1990, 1993 The Regents of the University of California, Copyright (c) 1996-1998 John D. Polstra, Copyright (c) 1998 John D. Polstra, Copyright (c) 2000-2013 Julian Seward, Copyright (c) 2007 John Birrell (jb@freebsd.org), Copyright (c) 2011-2021 The Bootstrap Authors, Copyright (c) 2011-2021 Twitter, Inc., Copyright (c) 2013 - 2018 AngleSharp, Copyright (c) 2019-2023 The Bootstrap Authors",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.AspNetCore.Components.Analyzers@8.0.15"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Logging.Abstractions",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Logging.Abstractions-8.0.3-854476",
+ "versionInfo": "8.0.3",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization for Standardization 1986",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Logging.Abstractions@8.0.3"
+ }
+ ]
+ },
+ {
+ "name": "IPAddressRange",
+ "SPDXID": "SPDXRef-nuget-IPAddressRange-6.2.0-2c2fd5",
+ "versionInfo": "6.2.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MPL-2.0",
+ "copyrightText": "Copyright (c) 2012-2024 J.Sakamoto, Mozilla, Copyright 2012-2024 J.Sakamoto, Mozilla",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/IPAddressRange@6.2.0"
+ }
+ ]
+ },
+ {
+ "name": "System.Reactive",
+ "SPDXID": "SPDXRef-nuget-System.Reactive-6.0.0-f0ef58",
+ "versionInfo": "6.0.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation and Contributors. Rx Reactive Extensions Observable LINQ Events",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/System.Reactive@6.0.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.AspNetCore.Metadata",
+ "SPDXID": "SPDXRef-nuget-Microsoft.AspNetCore.Metadata-8.0.15-2a8ab9",
+ "versionInfo": "8.0.15",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1989, 1993 The Regents of the University of California, Copyright (c) 1990, 1993 The Regents of the University of California, Copyright (c) 1996-1998 John D. Polstra, Copyright (c) 1998 John D. Polstra, Copyright (c) 2000-2013 Julian Seward, Copyright (c) 2007 James Newton-King, Copyright (c) 2007 John Birrell (jb@freebsd.org), Copyright (c) 2010-2019 Google LLC. http://angular.io/license, Copyright (c) 2011 Alex MacCaw (info@eribium.org), Copyright (c) 2011 Nicolas Gallagher (nicolas@nicolasgallagher.com), Copyright (c) 2011-2021 The Bootstrap Authors, Copyright (c) 2011-2021 Twitter, Inc., Copyright (c) 2013 - 2018 AngleSharp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014-2018 Michael Daines, Copyright (c) 2015, Google Inc., Copyright (c) 2016 Richard Morris, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2019 David Fowler, Copyright (c) 2019-2020 West Wind Technologies, Copyright (c) 2019-2023 The Bootstrap Authors, Copyright (c) Andrew Arnott, Copyright (c) HTML5 Boilerplate, Copyright (c) Microsoft Corporation, Copyright (c) Nicolas Gallagher and Jonathan Neal, Copyright (c) Sindre Sorhus \u003csindresorhus@gmail.com\u003e (https://sindresorhus.com), Copyright 2019 The gRPC Authors, Copyright Jorn Zaefferer, Copyright OpenJS Foundation and other contributors, https://openjsf.org",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.AspNetCore.Metadata@8.0.15"
+ }
+ ]
+ },
+ {
+ "name": "IPAddressRange",
+ "SPDXID": "SPDXRef-nuget-IPAddressRange-6.2.0-c9d65a",
+ "versionInfo": "6.2.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MPL-2.0",
+ "copyrightText": "Copyright (c) 2012-2024 J.Sakamoto, Mozilla, Copyright 2012-2024 J.Sakamoto, Mozilla",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/IPAddressRange@6.2.0"
+ }
+ ]
+ },
+ {
+ "name": "PuppeteerSharp",
+ "SPDXID": "SPDXRef-nuget-PuppeteerSharp-20.2.2-18c6e7",
+ "versionInfo": "20.2.2",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/PuppeteerSharp@20.2.2"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.DependencyInjection",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection-8.0.0-03eab0",
+ "versionInfo": "8.0.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization, Portions (c) International Organization for Standardization 1986",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.DependencyInjection@8.0.0"
+ }
+ ]
+ },
+ {
+ "name": "BlazorTable",
+ "SPDXID": "SPDXRef-nuget-BlazorTable-1.17.0-647113",
+ "versionInfo": "1.17.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 2008 VeriSign, Inc.",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/BlazorTable@1.17.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.AspNetCore.Components.Analyzers",
+ "SPDXID": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Analyzers-8.0.15-a1b7ad",
+ "versionInfo": "8.0.15",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation Contributors, Copyright (c) 1989, 1993 The Regents of the University of California, Copyright (c) 1990, 1993 The Regents of the University of California, Copyright (c) 1996-1998 John D. Polstra, Copyright (c) 1998 John D. Polstra, Copyright (c) 2000-2013 Julian Seward, Copyright (c) 2007 John Birrell (jb@freebsd.org), Copyright (c) 2011-2021 The Bootstrap Authors, Copyright (c) 2011-2021 Twitter, Inc., Copyright (c) 2013 - 2018 AngleSharp, Copyright (c) 2019-2023 The Bootstrap Authors",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.AspNetCore.Components.Analyzers@8.0.15"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Logging",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Logging-8.0.0-7574df",
+ "versionInfo": "8.0.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization, Portions (c) International Organization for Standardization 1986",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Logging@8.0.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.JSInterop",
+ "SPDXID": "SPDXRef-nuget-Microsoft.JSInterop-3.1.18-ec5757",
+ "versionInfo": "3.1.18",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "Apache-2.0",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.JSInterop@3.1.18"
+ }
+ ]
+ },
+ {
+ "name": "LinqKit.Core",
+ "SPDXID": "SPDXRef-nuget-LinqKit.Core-1.1.26-8271d4",
+ "versionInfo": "1.1.26",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/LinqKit.Core@1.1.26"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Localization",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Localization-3.1.18-8fe31d",
+ "versionInfo": "3.1.18",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "Apache-2.0",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Localization@3.1.18"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.AspNetCore.Components.Forms",
+ "SPDXID": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Forms-3.1.18-aa28bb",
+ "versionInfo": "3.1.18",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "Apache-2.0",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.AspNetCore.Components.Forms@3.1.18"
+ }
+ ]
+ },
+ {
+ "name": "IPAddressRange",
+ "SPDXID": "SPDXRef-nuget-IPAddressRange-6.2.0-3030d8",
+ "versionInfo": "6.2.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MPL-2.0",
+ "copyrightText": "Copyright (c) 2012-2024 J.Sakamoto, Mozilla, Copyright 2012-2024 J.Sakamoto, Mozilla",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/IPAddressRange@6.2.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Primitives",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Primitives-8.0.0-fb04bb",
+ "versionInfo": "8.0.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Primitives@8.0.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.AspNetCore.Authorization",
+ "SPDXID": "SPDXRef-nuget-Microsoft.AspNetCore.Authorization-8.0.15-3bb48c",
+ "versionInfo": "8.0.15",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1989, 1993 The Regents of the University of California, Copyright (c) 1990, 1993 The Regents of the University of California, Copyright (c) 1996-1998 John D. Polstra, Copyright (c) 1998 John D. Polstra, Copyright (c) 2000-2013 Julian Seward, Copyright (c) 2007 James Newton-King, Copyright (c) 2007 John Birrell (jb@freebsd.org), Copyright (c) 2010-2019 Google LLC. http://angular.io/license, Copyright (c) 2011 Alex MacCaw (info@eribium.org), Copyright (c) 2011 Nicolas Gallagher (nicolas@nicolasgallagher.com), Copyright (c) 2011-2021 The Bootstrap Authors, Copyright (c) 2011-2021 Twitter, Inc., Copyright (c) 2013 - 2018 AngleSharp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014-2018 Michael Daines, Copyright (c) 2015, Google Inc., Copyright (c) 2016 Richard Morris, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2019 David Fowler, Copyright (c) 2019-2020 West Wind Technologies, Copyright (c) 2019-2023 The Bootstrap Authors, Copyright (c) Andrew Arnott, Copyright (c) HTML5 Boilerplate, Copyright (c) Microsoft Corporation, Copyright (c) Nicolas Gallagher and Jonathan Neal, Copyright (c) Sindre Sorhus \u003csindresorhus@gmail.com\u003e (https://sindresorhus.com), Copyright 2019 The gRPC Authors, Copyright Jorn Zaefferer, Copyright OpenJS Foundation and other contributors, https://openjsf.org",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.AspNetCore.Authorization@8.0.15"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Logging.Abstractions",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Logging.Abstractions-8.0.3-318844",
+ "versionInfo": "8.0.3",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization for Standardization 1986",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Logging.Abstractions@8.0.3"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.AspNetCore.Components",
+ "SPDXID": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-2782e6",
+ "versionInfo": "8.0.15",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation Contributors, Copyright (c) 1989, 1993 The Regents of the University of California, Copyright (c) 1990, 1993 The Regents of the University of California, Copyright (c) 1996-1998 John D. Polstra, Copyright (c) 1998 John D. Polstra, Copyright (c) 2000-2013 Julian Seward, Copyright (c) 2007 John Birrell (jb@freebsd.org), Copyright (c) 2011-2021 The Bootstrap Authors, Copyright (c) 2011-2021 Twitter, Inc., Copyright (c) 2013 - 2018 AngleSharp, Copyright (c) 2019-2023 The Bootstrap Authors",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.AspNetCore.Components@8.0.15"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.AspNetCore.Components.Web",
+ "SPDXID": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Web-3.1.18-2b319e",
+ "versionInfo": "3.1.18",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "Apache-2.0",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.AspNetCore.Components.Web@3.1.18"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Localization.Abstractions",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Localization.Abstractions-3.1.18-552d34",
+ "versionInfo": "3.1.18",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "Apache-2.0",
+ "copyrightText": "(c) 2008 VeriSign, Inc., (c) Microsoft Corporation.",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Localization.Abstractions@3.1.18"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Options",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Options-8.0.2-43de69",
+ "versionInfo": "8.0.2",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Options@8.0.2"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.AspNetCore.Metadata",
+ "SPDXID": "SPDXRef-nuget-Microsoft.AspNetCore.Metadata-8.0.15-d16054",
+ "versionInfo": "8.0.15",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1989, 1993 The Regents of the University of California, Copyright (c) 1990, 1993 The Regents of the University of California, Copyright (c) 1996-1998 John D. Polstra, Copyright (c) 1998 John D. Polstra, Copyright (c) 2000-2013 Julian Seward, Copyright (c) 2007 James Newton-King, Copyright (c) 2007 John Birrell (jb@freebsd.org), Copyright (c) 2010-2019 Google LLC. http://angular.io/license, Copyright (c) 2011 Alex MacCaw (info@eribium.org), Copyright (c) 2011 Nicolas Gallagher (nicolas@nicolasgallagher.com), Copyright (c) 2011-2021 The Bootstrap Authors, Copyright (c) 2011-2021 Twitter, Inc., Copyright (c) 2013 - 2018 AngleSharp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014-2018 Michael Daines, Copyright (c) 2015, Google Inc., Copyright (c) 2016 Richard Morris, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2019 David Fowler, Copyright (c) 2019-2020 West Wind Technologies, Copyright (c) 2019-2023 The Bootstrap Authors, Copyright (c) Andrew Arnott, Copyright (c) HTML5 Boilerplate, Copyright (c) Microsoft Corporation, Copyright (c) Nicolas Gallagher and Jonathan Neal, Copyright (c) Sindre Sorhus \u003csindresorhus@gmail.com\u003e (https://sindresorhus.com), Copyright 2019 The gRPC Authors, Copyright Jorn Zaefferer, Copyright OpenJS Foundation and other contributors, https://openjsf.org",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.AspNetCore.Metadata@8.0.15"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.DependencyInjection.Abstractions",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection.Abstractions-8.0.2-f0b5b3",
+ "versionInfo": "8.0.2",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.DependencyInjection.Abstractions@8.0.2"
+ }
+ ]
+ },
+ {
+ "name": "GraphQL.Client.Abstractions",
+ "SPDXID": "SPDXRef-nuget-GraphQL.Client.Abstractions-6.1.0-94d0f3",
+ "versionInfo": "6.1.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "Copyright (c) 2017 graphql-dotnet",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/GraphQL.Client.Abstractions@6.1.0"
+ }
+ ]
+ },
+ {
+ "name": "GraphQL.Primitives",
+ "SPDXID": "SPDXRef-nuget-GraphQL.Primitives-6.1.0-0e1997",
+ "versionInfo": "6.1.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "Copyright (c) 2017 graphql-dotnet",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/GraphQL.Primitives@6.1.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Primitives",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Primitives-8.0.0-f4af79",
+ "versionInfo": "8.0.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Primitives@8.0.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.DependencyInjection.Abstractions",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection.Abstractions-8.0.2-32a665",
+ "versionInfo": "8.0.2",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.DependencyInjection.Abstractions@8.0.2"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Options",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Options-8.0.2-4cac30",
+ "versionInfo": "8.0.2",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Options@8.0.2"
+ }
+ ]
+ },
+ {
+ "name": "GraphQL.Client.Abstractions.Websocket",
+ "SPDXID": "SPDXRef-nuget-GraphQL.Client.Abstractions.Websocket-6.1.0-9c814e",
+ "versionInfo": "6.1.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "Copyright (c) 2017 graphql-dotnet",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/GraphQL.Client.Abstractions.Websocket@6.1.0"
+ }
+ ]
+ },
+ {
+ "name": "GraphQL.Client.Serializer.Newtonsoft",
+ "SPDXID": "SPDXRef-nuget-GraphQL.Client.Serializer.Newtonsoft-6.1.0-af029d",
+ "versionInfo": "6.1.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "Copyright (c) 2017 graphql-dotnet",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/GraphQL.Client.Serializer.Newtonsoft@6.1.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.AspNetCore.Components",
+ "SPDXID": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-869df9",
+ "versionInfo": "8.0.15",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation Contributors, Copyright (c) 1989, 1993 The Regents of the University of California, Copyright (c) 1990, 1993 The Regents of the University of California, Copyright (c) 1996-1998 John D. Polstra, Copyright (c) 1998 John D. Polstra, Copyright (c) 2000-2013 Julian Seward, Copyright (c) 2007 John Birrell (jb@freebsd.org), Copyright (c) 2011-2021 The Bootstrap Authors, Copyright (c) 2011-2021 Twitter, Inc., Copyright (c) 2013 - 2018 AngleSharp, Copyright (c) 2019-2023 The Bootstrap Authors",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.AspNetCore.Components@8.0.15"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Logging.Abstractions",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Logging.Abstractions-8.0.3-bc822b",
+ "versionInfo": "8.0.3",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization for Standardization 1986",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Logging.Abstractions@8.0.3"
+ }
+ ]
+ },
+ {
+ "name": "Newtonsoft.Json",
+ "SPDXID": "SPDXRef-nuget-Newtonsoft.Json-13.0.3-88677a",
+ "versionInfo": "13.0.3",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "Copyright (c) 2007 James Newton-King, Copyright (c) James Newton-King 2008, Copyright James Newton-King 2008, Copyright James Newton-King 2008 Json.NET",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Newtonsoft.Json@13.0.3"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.AspNetCore.Components.Analyzers",
+ "SPDXID": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Analyzers-8.0.15-99d18e",
+ "versionInfo": "8.0.15",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation Contributors, Copyright (c) 1989, 1993 The Regents of the University of California, Copyright (c) 1990, 1993 The Regents of the University of California, Copyright (c) 1996-1998 John D. Polstra, Copyright (c) 1998 John D. Polstra, Copyright (c) 2000-2013 Julian Seward, Copyright (c) 2007 John Birrell (jb@freebsd.org), Copyright (c) 2011-2021 The Bootstrap Authors, Copyright (c) 2011-2021 Twitter, Inc., Copyright (c) 2013 - 2018 AngleSharp, Copyright (c) 2019-2023 The Bootstrap Authors",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.AspNetCore.Components.Analyzers@8.0.15"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.AspNetCore.Metadata",
+ "SPDXID": "SPDXRef-nuget-Microsoft.AspNetCore.Metadata-8.0.15-38c0cd",
+ "versionInfo": "8.0.15",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1989, 1993 The Regents of the University of California, Copyright (c) 1990, 1993 The Regents of the University of California, Copyright (c) 1996-1998 John D. Polstra, Copyright (c) 1998 John D. Polstra, Copyright (c) 2000-2013 Julian Seward, Copyright (c) 2007 James Newton-King, Copyright (c) 2007 John Birrell (jb@freebsd.org), Copyright (c) 2010-2019 Google LLC. http://angular.io/license, Copyright (c) 2011 Alex MacCaw (info@eribium.org), Copyright (c) 2011 Nicolas Gallagher (nicolas@nicolasgallagher.com), Copyright (c) 2011-2021 The Bootstrap Authors, Copyright (c) 2011-2021 Twitter, Inc., Copyright (c) 2013 - 2018 AngleSharp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014-2018 Michael Daines, Copyright (c) 2015, Google Inc., Copyright (c) 2016 Richard Morris, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2019 David Fowler, Copyright (c) 2019-2020 West Wind Technologies, Copyright (c) 2019-2023 The Bootstrap Authors, Copyright (c) Andrew Arnott, Copyright (c) HTML5 Boilerplate, Copyright (c) Microsoft Corporation, Copyright (c) Nicolas Gallagher and Jonathan Neal, Copyright (c) Sindre Sorhus \u003csindresorhus@gmail.com\u003e (https://sindresorhus.com), Copyright 2019 The gRPC Authors, Copyright Jorn Zaefferer, Copyright OpenJS Foundation and other contributors, https://openjsf.org",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.AspNetCore.Metadata@8.0.15"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.AspNetCore.Authorization",
+ "SPDXID": "SPDXRef-nuget-Microsoft.AspNetCore.Authorization-8.0.15-4be166",
+ "versionInfo": "8.0.15",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1989, 1993 The Regents of the University of California, Copyright (c) 1990, 1993 The Regents of the University of California, Copyright (c) 1996-1998 John D. Polstra, Copyright (c) 1998 John D. Polstra, Copyright (c) 2000-2013 Julian Seward, Copyright (c) 2007 James Newton-King, Copyright (c) 2007 John Birrell (jb@freebsd.org), Copyright (c) 2010-2019 Google LLC. http://angular.io/license, Copyright (c) 2011 Alex MacCaw (info@eribium.org), Copyright (c) 2011 Nicolas Gallagher (nicolas@nicolasgallagher.com), Copyright (c) 2011-2021 The Bootstrap Authors, Copyright (c) 2011-2021 Twitter, Inc., Copyright (c) 2013 - 2018 AngleSharp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014-2018 Michael Daines, Copyright (c) 2015, Google Inc., Copyright (c) 2016 Richard Morris, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2019 David Fowler, Copyright (c) 2019-2020 West Wind Technologies, Copyright (c) 2019-2023 The Bootstrap Authors, Copyright (c) Andrew Arnott, Copyright (c) HTML5 Boilerplate, Copyright (c) Microsoft Corporation, Copyright (c) Nicolas Gallagher and Jonathan Neal, Copyright (c) Sindre Sorhus \u003csindresorhus@gmail.com\u003e (https://sindresorhus.com), Copyright 2019 The gRPC Authors, Copyright Jorn Zaefferer, Copyright OpenJS Foundation and other contributors, https://openjsf.org",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.AspNetCore.Authorization@8.0.15"
+ }
+ ]
+ },
+ {
+ "name": "Swashbuckle.AspNetCore",
+ "SPDXID": "SPDXRef-nuget-Swashbuckle.AspNetCore-8.1.1-96b60e",
+ "versionInfo": "8.1.1",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Swashbuckle.AspNetCore@8.1.1"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.IdentityModel.Logging",
+ "SPDXID": "SPDXRef-nuget-Microsoft.IdentityModel.Logging-8.14.0-c2340a",
+ "versionInfo": "8.14.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) Microsoft Corporation",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.IdentityModel.Logging@8.14.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.DependencyInjection.Abstractions",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection.Abstractions-8.0.2-15e0f0",
+ "versionInfo": "8.0.2",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.DependencyInjection.Abstractions@8.0.2"
+ }
+ ]
+ },
+ {
+ "name": "PuppeteerSharp",
+ "SPDXID": "SPDXRef-nuget-PuppeteerSharp-20.2.2-336c02",
+ "versionInfo": "20.2.2",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/PuppeteerSharp@20.2.2"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.ApiDescription.Server",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.ApiDescription.Server-6.0.5-1864e8",
+ "versionInfo": "6.0.5",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 2008 VeriSign, Inc., (c) Microsoft Corporation, Copyright James Newton-King 2008, Copyright James Newton-King 2008 Json.NET",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.ApiDescription.Server@6.0.5"
+ }
+ ]
+ },
+ {
+ "name": "Swashbuckle.AspNetCore.Swagger",
+ "SPDXID": "SPDXRef-nuget-Swashbuckle.AspNetCore.Swagger-8.1.1-46d10b",
+ "versionInfo": "8.1.1",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 2016-2025 Richard Morris, Copyright (c) 2016-2025 Richard Morris",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Swashbuckle.AspNetCore.Swagger@8.1.1"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.IdentityModel.Protocols",
+ "SPDXID": "SPDXRef-nuget-Microsoft.IdentityModel.Protocols-7.1.2-dacada",
+ "versionInfo": "7.1.2",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.IdentityModel.Protocols@7.1.2"
+ }
+ ]
+ },
+ {
+ "name": "System.IdentityModel.Tokens.Jwt",
+ "SPDXID": "SPDXRef-nuget-System.IdentityModel.Tokens.Jwt-8.14.0-e897cc",
+ "versionInfo": "8.14.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) Microsoft Corporation",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/System.IdentityModel.Tokens.Jwt@8.14.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.IdentityModel.Abstractions",
+ "SPDXID": "SPDXRef-nuget-Microsoft.IdentityModel.Abstractions-8.14.0-65944f",
+ "versionInfo": "8.14.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) Microsoft Corporation",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.IdentityModel.Abstractions@8.14.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Primitives",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Primitives-8.0.0-8da07a",
+ "versionInfo": "8.0.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Primitives@8.0.0"
+ }
+ ]
+ },
+ {
+ "name": "Novell.Directory.Ldap.NETStandard",
+ "SPDXID": "SPDXRef-nuget-Novell.Directory.Ldap.NETStandard-4.0.0-1ecb72",
+ "versionInfo": "4.0.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 2003 Novell, Inc, 2016, Copyright (c) 2000 - 2017 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org)",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Novell.Directory.Ldap.NETStandard@4.0.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.IdentityModel.Tokens",
+ "SPDXID": "SPDXRef-nuget-Microsoft.IdentityModel.Tokens-8.14.0-7bf14e",
+ "versionInfo": "8.14.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) Microsoft Corporation",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.IdentityModel.Tokens@8.14.0"
+ }
+ ]
+ },
+ {
+ "name": "Swashbuckle.AspNetCore.SwaggerGen",
+ "SPDXID": "SPDXRef-nuget-Swashbuckle.AspNetCore.SwaggerGen-8.1.1-1e762c",
+ "versionInfo": "8.1.1",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 2016-2025 Richard Morris, Copyright (c) 2016-2025 Richard Morris",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Swashbuckle.AspNetCore.SwaggerGen@8.1.1"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.DependencyInjection",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection-8.0.0-a9400f",
+ "versionInfo": "8.0.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization, Portions (c) International Organization for Standardization 1986",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.DependencyInjection@8.0.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.IdentityModel.Protocols.OpenIdConnect",
+ "SPDXID": "SPDXRef-nuget-Microsoft.IdentityModel.Protocols.OpenIdConnect-7.1.2-99e23d",
+ "versionInfo": "7.1.2",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.IdentityModel.Protocols.OpenIdConnect@7.1.2"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Logging",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Logging-8.0.0-e38db4",
+ "versionInfo": "8.0.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization, Portions (c) International Organization for Standardization 1986",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Logging@8.0.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Logging.Abstractions",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Logging.Abstractions-8.0.3-ae48ad",
+ "versionInfo": "8.0.3",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization for Standardization 1986",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Logging.Abstractions@8.0.3"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.AspNetCore.Authentication.JwtBearer",
+ "SPDXID": "SPDXRef-nuget-Microsoft.AspNetCore.Authentication.JwtBearer-8.0.15-6252d7",
+ "versionInfo": "8.0.15",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1989, 1993 The Regents of the University of California, Copyright (c) 1990, 1993 The Regents of the University of California, Copyright (c) 1996-1998 John D. Polstra, Copyright (c) 1998 John D. Polstra, Copyright (c) 2000-2013 Julian Seward, Copyright (c) 2007 James Newton-King, Copyright (c) 2007 John Birrell (jb@freebsd.org), Copyright (c) 2010-2019 Google LLC. http://angular.io/license, Copyright (c) 2011 Alex MacCaw (info@eribium.org), Copyright (c) 2011 Nicolas Gallagher (nicolas@nicolasgallagher.com), Copyright (c) 2011-2021 The Bootstrap Authors, Copyright (c) 2011-2021 Twitter, Inc., Copyright (c) 2013 - 2018 AngleSharp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014-2018 Michael Daines, Copyright (c) 2015, Google Inc., Copyright (c) 2016 Richard Morris, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2019 David Fowler, Copyright (c) 2019-2020 West Wind Technologies, Copyright (c) 2019-2023 The Bootstrap Authors, Copyright (c) Andrew Arnott, Copyright (c) HTML5 Boilerplate, Copyright (c) Microsoft Corporation, Copyright (c) Nicolas Gallagher and Jonathan Neal, Copyright (c) Sindre Sorhus \u003csindresorhus@gmail.com\u003e (https://sindresorhus.com), Copyright 2019 The gRPC Authors, Copyright Jorn Zaefferer, Copyright OpenJS Foundation and other contributors, https://openjsf.org",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.AspNetCore.Authentication.JwtBearer@8.0.15"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.IdentityModel.JsonWebTokens",
+ "SPDXID": "SPDXRef-nuget-Microsoft.IdentityModel.JsonWebTokens-8.14.0-1249a1",
+ "versionInfo": "8.14.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) Microsoft Corporation",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.IdentityModel.JsonWebTokens@8.14.0"
+ }
+ ]
+ },
+ {
+ "name": "Swashbuckle.AspNetCore.SwaggerUI",
+ "SPDXID": "SPDXRef-nuget-Swashbuckle.AspNetCore.SwaggerUI-8.1.1-e01486",
+ "versionInfo": "8.1.1",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 2016-2025 Richard Morris Middleware, Copyright (c) 2016-2025 Richard Morris, Copyright 2020-2021 SmartBear Software Inc.",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Swashbuckle.AspNetCore.SwaggerUI@8.1.1"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.OpenApi",
+ "SPDXID": "SPDXRef-nuget-Microsoft.OpenApi-1.6.23-a09a48",
+ "versionInfo": "1.6.23",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "LicenseRef-scancode-generic-cla AND MIT",
+ "copyrightText": "(c) Microsoft Corporation",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.OpenApi@1.6.23"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Options",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Options-8.0.2-ce1910",
+ "versionInfo": "8.0.2",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Options@8.0.2"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.ObjectPool",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.ObjectPool-8.0.11-da583c",
+ "versionInfo": "8.0.11",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1989, 1993 The Regents of the University of California, Copyright (c) 1990, 1993 The Regents of the University of California, Copyright (c) 1996-1998 John D. Polstra, Copyright (c) 1998 John D. Polstra, Copyright (c) 2000-2013 Julian Seward, Copyright (c) 2007 James Newton-King, Copyright (c) 2007 John Birrell (jb@freebsd.org), Copyright (c) 2010-2019 Google LLC. http://angular.io/license, Copyright (c) 2011 Alex MacCaw (info@eribium.org), Copyright (c) 2011 Nicolas Gallagher (nicolas@nicolasgallagher.com), Copyright (c) 2011-2021 The Bootstrap Authors, Copyright (c) 2011-2021 Twitter, Inc., Copyright (c) 2013 - 2018 AngleSharp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014-2018 Michael Daines, Copyright (c) 2015, Google Inc., Copyright (c) 2016 Richard Morris, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2019 David Fowler, Copyright (c) 2019-2020 West Wind Technologies, Copyright (c) 2019-2023 The Bootstrap Authors, Copyright (c) Andrew Arnott, Copyright (c) HTML5 Boilerplate, Copyright (c) Microsoft Corporation, Copyright (c) Nicolas Gallagher and Jonathan Neal, Copyright (c) Sindre Sorhus \u003csindresorhus@gmail.com\u003e (https://sindresorhus.com), Copyright 2019 The gRPC Authors, Copyright Jorn Zaefferer, Copyright OpenJS Foundation and other contributors, https://openjsf.org",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.ObjectPool@8.0.11"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Net.Http.Headers",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Net.Http.Headers-2.3.0-6f214b",
+ "versionInfo": "2.3.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "Apache-2.0 AND LicenseRef-scancode-unknown",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation and Contributors",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Net.Http.Headers@2.3.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.AspNetCore.WebUtilities",
+ "SPDXID": "SPDXRef-nuget-Microsoft.AspNetCore.WebUtilities-2.3.0-b47477",
+ "versionInfo": "2.3.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "Apache-2.0 AND LicenseRef-scancode-unknown",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation and Contributors",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.AspNetCore.WebUtilities@2.3.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Options",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Options-8.0.2-c1e33e",
+ "versionInfo": "8.0.2",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Options@8.0.2"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.Primitives",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.Primitives-8.0.0-6087da",
+ "versionInfo": "8.0.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.Primitives@8.0.0"
+ }
+ ]
+ },
+ {
+ "name": "BouncyCastle.Cryptography",
+ "SPDXID": "SPDXRef-nuget-BouncyCastle.Cryptography-2.5.1-980798",
+ "versionInfo": "2.5.1",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "Apache-2.0 AND LicenseRef-scancode-unknown-license-reference AND MIT",
+ "copyrightText": "Copyright (c) 2000-2024 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org), Copyright (c) Legion of the Bouncy Castle Inc. 2000-2024, Copyright Legion of the Bouncy Castle Inc. 2000-2024",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/BouncyCastle.Cryptography@2.5.1"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.AspNetCore.Http.Abstractions",
+ "SPDXID": "SPDXRef-nuget-Microsoft.AspNetCore.Http.Abstractions-2.3.0-795f67",
+ "versionInfo": "2.3.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "Apache-2.0 AND LicenseRef-scancode-unknown",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation and Contributors",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.AspNetCore.Http.Abstractions@2.3.0"
+ }
+ ]
+ },
+ {
+ "name": "System.Security.Cryptography.Pkcs",
+ "SPDXID": "SPDXRef-nuget-System.Security.Cryptography.Pkcs-8.0.1-0bc701",
+ "versionInfo": "8.0.1",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization, Portions (c) International Organization for Standardization 1986",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/System.Security.Cryptography.Pkcs@8.0.1"
+ }
+ ]
+ },
+ {
+ "name": "MimeKit",
+ "SPDXID": "SPDXRef-nuget-MimeKit-4.13.0-23ac4f",
+ "versionInfo": "4.13.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "Copyright (c) 2012-2025 .NET Foundation and Contributors, Copyright 2013-2025 .NET Foundation and Contributors",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/MimeKit@4.13.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.Extensions.DependencyInjection.Abstractions",
+ "SPDXID": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection.Abstractions-8.0.0-18c45d",
+ "versionInfo": "8.0.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.Extensions.DependencyInjection.Abstractions@8.0.0"
+ }
+ ]
+ },
+ {
+ "name": "MailKit",
+ "SPDXID": "SPDXRef-nuget-MailKit-4.13.0-b26bfd",
+ "versionInfo": "4.13.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "Copyright (c) 2013-2024 .NET Foundation and Contributors, Copyright 2013-2025 .NET Foundation and Contributors",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/MailKit@4.13.0"
+ }
+ ]
+ },
+ {
+ "name": "System.Buffers",
+ "SPDXID": "SPDXRef-nuget-System.Buffers-4.6.0-da25bf",
+ "versionInfo": "4.6.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) Microsoft Corporation",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/System.Buffers@4.6.0"
+ }
+ ]
+ },
+ {
+ "name": "System.Text.Encodings.Web",
+ "SPDXID": "SPDXRef-nuget-System.Text.Encodings.Web-9.0.8-7001c6",
+ "versionInfo": "9.0.8",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1995-2024 Jean-loup Gailly and Mark Adler, (c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1998 Microsoft, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 Andrew Gallant, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2015-2018, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2018 Nemanja Mijailovic, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021, Copyright (c) 2022 FormatJS, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization for Standardization 1986",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/System.Text.Encodings.Web@9.0.8"
+ }
+ ]
+ },
+ {
+ "name": "System.Formats.Asn1",
+ "SPDXID": "SPDXRef-nuget-System.Formats.Asn1-8.0.1-037f22",
+ "versionInfo": "8.0.1",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "MIT",
+ "copyrightText": "(c) 1997-2005 Sean Eron Anderson, (c) Microsoft Corporation, Copyright (c) .NET Foundation, Copyright (c) .NET Foundation and Contributors, Copyright (c) .NET Foundation Contributors, Copyright (c) 1980, 1986, 1993 The Regents of the University of California, Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. \u0026 Digital Equipment Corporation, Maynard, Mass, Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc., Copyright (c) 1991-2022 Unicode, Inc., Copyright (c) 1995-2022 Jean-loup Gailly and Mark Adler, Copyright (c) 1998 Microsoft. To, Copyright (c) 1999 Lucent Technologies, Copyright (c) 2004-2006 Intel Corporation, Copyright (c) 2005-2007, Nick Galbreath, Copyright (c) 2005-2020 Rich Felker, Copyright (c) 2006 Jb Evain (jbevain@gmail.com), Copyright (c) 2007 James Newton-King, Copyright (c) 2008-2016, Wojciech Mula, Copyright (c) 2008-2020 Advanced Micro Devices, Inc., Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors, Copyright (c) 2011 Novell, Inc (http://www.novell.com), Copyright (c) 2011-2015 Intel Corporation, Copyright (c) 2011-2020 Microsoft Corp, Copyright (c) 2011, Google Inc., Copyright (c) 2012 - present, Victor Zverovich, Copyright (c) 2012-2021 Yann Collet, Copyright (c) 2013-2017, Alfred Klomp, Copyright (c) 2013-2017, Milosz Krajewski, Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com, Copyright (c) 2015 The Chromium Authors, Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip, Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com), Copyright (c) 2015-2017, Wojciech Mula, Copyright (c) 2016-2017, Matthieu Darbois, Copyright (c) 2017 Yoshifumi Kawai, Copyright (c) 2018 Alexander Chermyanin, Copyright (c) 2019 Microsoft Corporation, Daan Leijen, Copyright (c) 2020 Dan Shechter, Copyright (c) 2020 Mara Bos \u003cm-ou.se@m-ou.se\u003e, Copyright (c) 2021 csFastFloat authors, Copyright (c) 2022, Geoff Langdale, Copyright (c) 2022, Wojciech Mula, Copyright (c) Andrew Arnott, Copyright (c) Microsoft Corporation, Copyright (c) Six Labors, Copyright (c) The Internet Society (2003), Copyright (c) The Internet Society 1997, Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers, Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California, Copyright 2012 the V8 project authors, Copyright 2018 Daniel Lemire, Copyright 2019 LLVM Project, Portions (c) International Organization",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/System.Formats.Asn1@8.0.1"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.AspNetCore.Http",
+ "SPDXID": "SPDXRef-nuget-Microsoft.AspNetCore.Http-2.3.0-239946",
+ "versionInfo": "2.3.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "Apache-2.0 AND LicenseRef-scancode-unknown",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation and Contributors",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.AspNetCore.Http@2.3.0"
+ }
+ ]
+ },
+ {
+ "name": "Microsoft.AspNetCore.Http.Features",
+ "SPDXID": "SPDXRef-nuget-Microsoft.AspNetCore.Http.Features-2.3.0-1fa4f7",
+ "versionInfo": "2.3.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "licenseConcluded": "Apache-2.0 AND LicenseRef-scancode-unknown",
+ "copyrightText": "(c) Microsoft Corporation, Copyright (c) .NET Foundation and Contributors",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:nuget/Microsoft.AspNetCore.Http.Features@2.3.0"
+ }
+ ]
+ },
+ {
+ "name": "actions/checkout",
+ "SPDXID": "SPDXRef-githubactions-actions-checkout-4..-75c946",
+ "versionInfo": "4.*.*",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:githubactions/actions/checkout@4.%2A.%2A"
+ }
+ ]
+ },
+ {
+ "name": "actions/setup-dotnet",
+ "SPDXID": "SPDXRef-githubactions-actions-setup-dotnet-4..-75c946",
+ "versionInfo": "4.*.*",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:githubactions/actions/setup-dotnet@4.%2A.%2A"
+ }
+ ]
+ },
+ {
+ "name": "actions/checkout",
+ "SPDXID": "SPDXRef-githubactions-actions-checkout-3-75c946",
+ "versionInfo": "3",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:githubactions/actions/checkout@3"
+ }
+ ]
+ },
+ {
+ "name": "github/codeql-action/analyze",
+ "SPDXID": "SPDXRef-githubactions-githubcodeql-action-analyze-2-75c946",
+ "versionInfo": "2",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:githubactions/github/codeql-action/analyze@2"
+ }
+ ]
+ },
+ {
+ "name": "github/codeql-action/autobuild",
+ "SPDXID": "SPDXRef-githubactions-githubcodeql-action-autobuild-2-75c946",
+ "versionInfo": "2",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:githubactions/github/codeql-action/autobuild@2"
+ }
+ ]
+ },
+ {
+ "name": "github/codeql-action/init",
+ "SPDXID": "SPDXRef-githubactions-githubcodeql-action-init-2-75c946",
+ "versionInfo": "2",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:githubactions/github/codeql-action/init@2"
+ }
+ ]
+ },
+ {
+ "name": "gitpython",
+ "SPDXID": "SPDXRef-pypi-gitpython-75c946",
+ "versionInfo": "\u003e= 3.1.0",
+ "downloadLocation": "NOASSERTION",
+ "filesAnalyzed": false,
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:pypi/gitpython"
+ }
+ ]
+ },
+ {
+ "name": "com.github.CactuseSecurity/firewall-orchestrator",
+ "SPDXID": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "versionInfo": "main",
+ "downloadLocation": "git+https://github.com/CactuseSecurity/firewall-orchestrator",
+ "filesAnalyzed": false,
+ "licenseDeclared": "Apache-2.0",
+ "externalRefs": [
+ {
+ "referenceCategory": "PACKAGE-MANAGER",
+ "referenceType": "purl",
+ "referenceLocator": "pkg:github/CactuseSecurity/firewall-orchestrator@main"
+ }
+ ]
+ }
+ ],
+ "relationships": [
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Authorization-8.0.15-58c86a",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Primitives-8.0.0-973e61",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Authorization-8.0.15-58c86a",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Options-8.0.2-ed918b",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Authorization-8.0.15-58c86a",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection.Abstractions-8.0.2-934d86",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Authorization-8.0.15-58c86a",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Logging.Abstractions-8.0.3-0d07ba",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Authorization-8.0.15-58c86a",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Authorization-8.0.15-59a19e",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Authorization-8.0.15-58c86a",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Metadata-8.0.15-f13caf",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Authorization-8.0.15-58c86a",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-154912",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Authorization-8.0.15-58c86a",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Analyzers-8.0.15-75b14f",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-154912",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Primitives-8.0.0-973e61",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-154912",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Options-8.0.2-ed918b",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-154912",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection.Abstractions-8.0.2-934d86",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-154912",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Logging.Abstractions-8.0.3-0d07ba",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-154912",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Authorization-8.0.15-59a19e",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-154912",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Metadata-8.0.15-f13caf",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-154912",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Analyzers-8.0.15-75b14f",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.NET.Test.Sdk-17.14.1-86ab34",
+ "relatedSpdxElement": "SPDXRef-nuget-Newtonsoft.Json-13.0.3-7c98bf",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.NET.Test.Sdk-17.14.1-86ab34",
+ "relatedSpdxElement": "SPDXRef-nuget-System.Reflection.Metadata-8.0.0-2fa35d",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.NET.Test.Sdk-17.14.1-86ab34",
+ "relatedSpdxElement": "SPDXRef-nuget-System.Collections.Immutable-8.0.0-00b56b",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.NET.Test.Sdk-17.14.1-86ab34",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.TestPlatform.ObjectModel-17.14.1-341464",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.NET.Test.Sdk-17.14.1-86ab34",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.TestPlatform.TestHost-17.14.1-5a76b2",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.NET.Test.Sdk-17.14.1-86ab34",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.CodeCoverage-17.14.1-1dfedf",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-d179a3",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Primitives-8.0.0-7bb3ef",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-d179a3",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Options-8.0.2-553265",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-d179a3",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection.Abstractions-8.0.2-5a630c",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-d179a3",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Logging.Abstractions-8.0.3-f5387c",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-d179a3",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Authorization-8.0.15-457dff",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-d179a3",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Metadata-8.0.15-7088bb",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-d179a3",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Analyzers-8.0.15-67a21b",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-PuppeteerSharp-20.2.2-21f7e5",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Primitives-8.0.0-7bb3ef",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-PuppeteerSharp-20.2.2-21f7e5",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Options-8.0.2-553265",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-PuppeteerSharp-20.2.2-21f7e5",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection.Abstractions-8.0.2-5a630c",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-PuppeteerSharp-20.2.2-21f7e5",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Logging.Abstractions-8.0.3-f5387c",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-PuppeteerSharp-20.2.2-21f7e5",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Logging-8.0.1-d391ad",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-PuppeteerSharp-20.2.2-21f7e5",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection-8.0.1-276050",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.IdentityModel.Tokens-8.14.0-a883d6",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.IdentityModel.Abstractions-8.14.0-6e2c7d",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.IdentityModel.Tokens-8.14.0-a883d6",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection.Abstractions-8.0.2-5a630c",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.IdentityModel.Tokens-8.14.0-a883d6",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.IdentityModel.Logging-8.14.0-44c2b4",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.IdentityModel.Tokens-8.14.0-a883d6",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Logging.Abstractions-8.0.3-f5387c",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Primitives-8.0.0-7bb3ef",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Options-8.0.2-553265",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection.Abstractions-8.0.2-5a630c",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Logging.Abstractions-8.0.3-f5387c",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Authorization-8.0.15-457dff",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Metadata-8.0.15-7088bb",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-d179a3",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Analyzers-8.0.15-67a21b",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Authorization-8.0.15-c2e5e2",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Configuration.Binder-8.0.2-bb6751",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Configuration-8.0.0-dce331",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Logging-8.0.1-d391ad",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.FileProviders.Abstractions-8.0.0-3e71db",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Localization.Abstractions-8.0.11-4cb515",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection-8.0.1-276050",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-System.IO.Pipelines-8.0.0-f9572b",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.JSInterop-8.0.11-5ea157",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Configuration.FileExtensions-8.0.1-6b3a2e",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-System.Text.Json-8.0.5-74b06c",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Caching.Memory-8.0.1-24ebac",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Web-8.0.11-3380f9",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-bunit.core-1.40.0-44d971",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-AngleSharp.Diffing-1.0.0-08ab89",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Configuration.Abstractions-8.0.0-639bd0",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Components.WebAssembly-8.0.11-b60482",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.FileSystemGlobbing-8.0.0-df8146",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.JSInterop.WebAssembly-8.0.11-7d7f03",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.FileProviders.Physical-8.0.0-628894",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Configuration.Json-8.0.1-bd4bb5",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Components.WebAssembly.Authentication-8.0.11-d84320",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-AngleSharp-1.2.0-ec1ca1",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-AngleSharp.Css-1.0.0-beta.154-30e617",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Caching.Abstractions-8.0.0-82b542",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Forms-8.0.11-b3ce0c",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relatedSpdxElement": "SPDXRef-nuget-bunit.web-1.40.0-b4ac35",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Authorization-8.0.15-c2e5e2",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Primitives-8.0.0-7bb3ef",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Authorization-8.0.15-c2e5e2",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Options-8.0.2-553265",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Authorization-8.0.15-c2e5e2",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection.Abstractions-8.0.2-5a630c",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Authorization-8.0.15-c2e5e2",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Logging.Abstractions-8.0.3-f5387c",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Authorization-8.0.15-c2e5e2",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Authorization-8.0.15-457dff",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Authorization-8.0.15-c2e5e2",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Metadata-8.0.15-7088bb",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Authorization-8.0.15-c2e5e2",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-d179a3",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Authorization-8.0.15-c2e5e2",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Analyzers-8.0.15-67a21b",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-NUnit3TestAdapter-5.1.0-5f80ff",
+ "relatedSpdxElement": "SPDXRef-nuget-System.Diagnostics.DiagnosticSource-5.0.0-31c6d8",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-NUnit3TestAdapter-5.1.0-5f80ff",
+ "relatedSpdxElement": "SPDXRef-nuget-System.Reflection.Metadata-8.0.0-2fa35d",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-NUnit3TestAdapter-5.1.0-5f80ff",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.TestPlatform.AdapterUtilities-17.13.0-22a1b0",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-NUnit3TestAdapter-5.1.0-5f80ff",
+ "relatedSpdxElement": "SPDXRef-nuget-System.Collections.Immutable-8.0.0-00b56b",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-NUnit3TestAdapter-5.1.0-5f80ff",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Testing.Platform.MSBuild-1.7.3-c5050d",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-NUnit3TestAdapter-5.1.0-5f80ff",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Testing.Extensions.VSTestBridge-1.7.3-e3b7da",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-NUnit3TestAdapter-5.1.0-5f80ff",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Testing.Extensions.TrxReport.Abstractions-1.7.3-14ef65",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-NUnit3TestAdapter-5.1.0-5f80ff",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Testing.Extensions.Telemetry-1.7.3-380d69",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-NUnit3TestAdapter-5.1.0-5f80ff",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.TestPlatform.ObjectModel-17.14.1-341464",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-NUnit3TestAdapter-5.1.0-5f80ff",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Testing.Platform-1.7.3-984f53",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-NUnit3TestAdapter-5.1.0-5f80ff",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.ApplicationInsights-2.23.0-92127e",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-PuppeteerSharp-20.2.2-d76daa",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Primitives-8.0.0-0353ed",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-PuppeteerSharp-20.2.2-d76daa",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Options-8.0.2-f33fde",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-PuppeteerSharp-20.2.2-d76daa",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection-8.0.0-76ea4d",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-PuppeteerSharp-20.2.2-d76daa",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Logging-8.0.0-c71dd9",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-PuppeteerSharp-20.2.2-d76daa",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection.Abstractions-8.0.2-fa587a",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-PuppeteerSharp-20.2.2-d76daa",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Logging.Abstractions-8.0.3-c90f02",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.IdentityModel.Tokens-8.14.0-8284c6",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.IdentityModel.Abstractions-8.14.0-dada85",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.IdentityModel.Tokens-8.14.0-8284c6",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.IdentityModel.Logging-8.14.0-b11f58",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-System.IdentityModel.Tokens.Jwt-8.14.0-62ac6a",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.IdentityModel.Abstractions-8.14.0-dada85",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-System.IdentityModel.Tokens.Jwt-8.14.0-62ac6a",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.IdentityModel.JsonWebTokens-8.14.0-3e0bac",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-System.IdentityModel.Tokens.Jwt-8.14.0-62ac6a",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.IdentityModel.Tokens-8.14.0-8284c6",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-System.IdentityModel.Tokens.Jwt-8.14.0-62ac6a",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.IdentityModel.Logging-8.14.0-b11f58",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-System.IdentityModel.Tokens.Jwt-8.14.0-62ac6a",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection.Abstractions-8.0.0-88c4e5",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-System.IdentityModel.Tokens.Jwt-8.14.0-62ac6a",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Logging.Abstractions-8.0.0-145c39",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.Rest.ClientRuntime-2.3.24-508401",
+ "relatedSpdxElement": "SPDXRef-nuget-Newtonsoft.Json-13.0.3-631925",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-RestSharp.Serializers.NewtonsoftJson-112.1.0-1f0a6b",
+ "relatedSpdxElement": "SPDXRef-nuget-RestSharp-112.1.0-839660",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-RestSharp.Serializers.NewtonsoftJson-112.1.0-1f0a6b",
+ "relatedSpdxElement": "SPDXRef-nuget-Newtonsoft.Json-13.0.3-631925",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-GraphQL.Client-6.1.0-7c0c75",
+ "relatedSpdxElement": "SPDXRef-nuget-GraphQL.Primitives-6.1.0-8b54bf",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-GraphQL.Client-6.1.0-7c0c75",
+ "relatedSpdxElement": "SPDXRef-nuget-System.Reactive-6.0.0-f0ef58",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-GraphQL.Client-6.1.0-7c0c75",
+ "relatedSpdxElement": "SPDXRef-nuget-GraphQL.Client.Abstractions-6.1.0-132a88",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-GraphQL.Client-6.1.0-7c0c75",
+ "relatedSpdxElement": "SPDXRef-nuget-GraphQL.Client.Abstractions.Websocket-6.1.0-604e8e",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-259cdf",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Primitives-8.0.0-07a9d5",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-259cdf",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Options-8.0.2-3ddafa",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-259cdf",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection.Abstractions-8.0.2-b43535",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-259cdf",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Logging.Abstractions-8.0.3-854476",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-259cdf",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Authorization-8.0.15-6b8922",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-259cdf",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Metadata-8.0.15-2a8ab9",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-259cdf",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Analyzers-8.0.15-89dc0e",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-GraphQL.Client.Serializer.Newtonsoft-6.1.0-9b01fa",
+ "relatedSpdxElement": "SPDXRef-nuget-GraphQL.Primitives-6.1.0-8b54bf",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-GraphQL.Client.Serializer.Newtonsoft-6.1.0-9b01fa",
+ "relatedSpdxElement": "SPDXRef-nuget-GraphQL.Client.Abstractions-6.1.0-132a88",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-GraphQL.Client.Serializer.Newtonsoft-6.1.0-9b01fa",
+ "relatedSpdxElement": "SPDXRef-nuget-GraphQL.Client.Abstractions.Websocket-6.1.0-604e8e",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-GraphQL.Client.Serializer.Newtonsoft-6.1.0-9b01fa",
+ "relatedSpdxElement": "SPDXRef-nuget-Newtonsoft.Json-13.0.3-631925",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-GraphQL.Client.Serializer.SystemTextJson-6.1.0-c25e33",
+ "relatedSpdxElement": "SPDXRef-nuget-GraphQL.Primitives-6.1.0-8b54bf",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-GraphQL.Client.Serializer.SystemTextJson-6.1.0-c25e33",
+ "relatedSpdxElement": "SPDXRef-nuget-GraphQL.Client.Abstractions-6.1.0-132a88",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-GraphQL.Client.Serializer.SystemTextJson-6.1.0-c25e33",
+ "relatedSpdxElement": "SPDXRef-nuget-GraphQL.Client.Abstractions.Websocket-6.1.0-604e8e",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-PuppeteerSharp-20.2.2-18c6e7",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Primitives-8.0.0-fb04bb",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-PuppeteerSharp-20.2.2-18c6e7",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Options-8.0.2-43de69",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-PuppeteerSharp-20.2.2-18c6e7",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection-8.0.0-03eab0",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-PuppeteerSharp-20.2.2-18c6e7",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Logging-8.0.0-7574df",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-PuppeteerSharp-20.2.2-18c6e7",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection.Abstractions-8.0.2-f0b5b3",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-PuppeteerSharp-20.2.2-18c6e7",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Logging.Abstractions-8.0.3-318844",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-BlazorTable-1.17.0-647113",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Primitives-8.0.0-fb04bb",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-BlazorTable-1.17.0-647113",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Options-8.0.2-43de69",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-BlazorTable-1.17.0-647113",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection-8.0.0-03eab0",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-BlazorTable-1.17.0-647113",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection.Abstractions-8.0.2-f0b5b3",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-BlazorTable-1.17.0-647113",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Logging.Abstractions-8.0.3-318844",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-BlazorTable-1.17.0-647113",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Authorization-8.0.15-3bb48c",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-BlazorTable-1.17.0-647113",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Metadata-8.0.15-d16054",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-BlazorTable-1.17.0-647113",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-2782e6",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-BlazorTable-1.17.0-647113",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Analyzers-8.0.15-a1b7ad",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-BlazorTable-1.17.0-647113",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Localization.Abstractions-3.1.18-552d34",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-BlazorTable-1.17.0-647113",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Localization-3.1.18-8fe31d",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-BlazorTable-1.17.0-647113",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Web-3.1.18-2b319e",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-BlazorTable-1.17.0-647113",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Forms-3.1.18-aa28bb",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-BlazorTable-1.17.0-647113",
+ "relatedSpdxElement": "SPDXRef-nuget-LinqKit.Core-1.1.26-8271d4",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-BlazorTable-1.17.0-647113",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.JSInterop-3.1.18-ec5757",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-2782e6",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Primitives-8.0.0-fb04bb",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-2782e6",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Options-8.0.2-43de69",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-2782e6",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection.Abstractions-8.0.2-f0b5b3",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-2782e6",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Logging.Abstractions-8.0.3-318844",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-2782e6",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Authorization-8.0.15-3bb48c",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-2782e6",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Metadata-8.0.15-d16054",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-2782e6",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Analyzers-8.0.15-a1b7ad",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-GraphQL.Client.Serializer.Newtonsoft-6.1.0-af029d",
+ "relatedSpdxElement": "SPDXRef-nuget-GraphQL.Primitives-6.1.0-0e1997",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-GraphQL.Client.Serializer.Newtonsoft-6.1.0-af029d",
+ "relatedSpdxElement": "SPDXRef-nuget-GraphQL.Client.Abstractions-6.1.0-94d0f3",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-GraphQL.Client.Serializer.Newtonsoft-6.1.0-af029d",
+ "relatedSpdxElement": "SPDXRef-nuget-GraphQL.Client.Abstractions.Websocket-6.1.0-9c814e",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-GraphQL.Client.Serializer.Newtonsoft-6.1.0-af029d",
+ "relatedSpdxElement": "SPDXRef-nuget-Newtonsoft.Json-13.0.3-88677a",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-869df9",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Primitives-8.0.0-f4af79",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-869df9",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Options-8.0.2-4cac30",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-869df9",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection.Abstractions-8.0.2-32a665",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-869df9",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Logging.Abstractions-8.0.3-bc822b",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-869df9",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Authorization-8.0.15-4be166",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-869df9",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Metadata-8.0.15-38c0cd",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-869df9",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Analyzers-8.0.15-99d18e",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Swashbuckle.AspNetCore-8.1.1-96b60e",
+ "relatedSpdxElement": "SPDXRef-nuget-Swashbuckle.AspNetCore.Swagger-8.1.1-46d10b",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Swashbuckle.AspNetCore-8.1.1-96b60e",
+ "relatedSpdxElement": "SPDXRef-nuget-Swashbuckle.AspNetCore.SwaggerUI-8.1.1-e01486",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Swashbuckle.AspNetCore-8.1.1-96b60e",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.OpenApi-1.6.23-a09a48",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Swashbuckle.AspNetCore-8.1.1-96b60e",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.ApiDescription.Server-6.0.5-1864e8",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Swashbuckle.AspNetCore-8.1.1-96b60e",
+ "relatedSpdxElement": "SPDXRef-nuget-Swashbuckle.AspNetCore.SwaggerGen-8.1.1-1e762c",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-PuppeteerSharp-20.2.2-336c02",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Primitives-8.0.0-8da07a",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-PuppeteerSharp-20.2.2-336c02",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Options-8.0.2-ce1910",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-PuppeteerSharp-20.2.2-336c02",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection-8.0.0-a9400f",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-PuppeteerSharp-20.2.2-336c02",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Logging-8.0.0-e38db4",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-PuppeteerSharp-20.2.2-336c02",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection.Abstractions-8.0.2-15e0f0",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-PuppeteerSharp-20.2.2-336c02",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Logging.Abstractions-8.0.3-ae48ad",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-System.IdentityModel.Tokens.Jwt-8.14.0-e897cc",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.IdentityModel.Abstractions-8.14.0-65944f",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-System.IdentityModel.Tokens.Jwt-8.14.0-e897cc",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.IdentityModel.JsonWebTokens-8.14.0-1249a1",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-System.IdentityModel.Tokens.Jwt-8.14.0-e897cc",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.IdentityModel.Tokens-8.14.0-7bf14e",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-System.IdentityModel.Tokens.Jwt-8.14.0-e897cc",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.IdentityModel.Logging-8.14.0-c2340a",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Novell.Directory.Ldap.NETStandard-4.0.0-1ecb72",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection.Abstractions-8.0.2-15e0f0",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Novell.Directory.Ldap.NETStandard-4.0.0-1ecb72",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Logging.Abstractions-8.0.3-ae48ad",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.IdentityModel.Tokens-8.14.0-7bf14e",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.IdentityModel.Abstractions-8.14.0-65944f",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.IdentityModel.Tokens-8.14.0-7bf14e",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection.Abstractions-8.0.2-15e0f0",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.IdentityModel.Tokens-8.14.0-7bf14e",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.IdentityModel.Logging-8.14.0-c2340a",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.IdentityModel.Tokens-8.14.0-7bf14e",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Logging.Abstractions-8.0.3-ae48ad",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Authentication.JwtBearer-8.0.15-6252d7",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.IdentityModel.Abstractions-8.14.0-65944f",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Authentication.JwtBearer-8.0.15-6252d7",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.IdentityModel.JsonWebTokens-8.14.0-1249a1",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Authentication.JwtBearer-8.0.15-6252d7",
+ "relatedSpdxElement": "SPDXRef-nuget-System.IdentityModel.Tokens.Jwt-8.14.0-e897cc",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Authentication.JwtBearer-8.0.15-6252d7",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.IdentityModel.Tokens-8.14.0-7bf14e",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Authentication.JwtBearer-8.0.15-6252d7",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.IdentityModel.Protocols-7.1.2-dacada",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Authentication.JwtBearer-8.0.15-6252d7",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.IdentityModel.Protocols.OpenIdConnect-7.1.2-99e23d",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Authentication.JwtBearer-8.0.15-6252d7",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection.Abstractions-8.0.2-15e0f0",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Authentication.JwtBearer-8.0.15-6252d7",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.IdentityModel.Logging-8.14.0-c2340a",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Authentication.JwtBearer-8.0.15-6252d7",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Logging.Abstractions-8.0.3-ae48ad",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-MimeKit-4.13.0-23ac4f",
+ "relatedSpdxElement": "SPDXRef-nuget-System.Security.Cryptography.Pkcs-8.0.1-0bc701",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-MimeKit-4.13.0-23ac4f",
+ "relatedSpdxElement": "SPDXRef-nuget-BouncyCastle.Cryptography-2.5.1-980798",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-MailKit-4.13.0-b26bfd",
+ "relatedSpdxElement": "SPDXRef-nuget-System.Security.Cryptography.Pkcs-8.0.1-0bc701",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-MailKit-4.13.0-b26bfd",
+ "relatedSpdxElement": "SPDXRef-nuget-BouncyCastle.Cryptography-2.5.1-980798",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-MailKit-4.13.0-b26bfd",
+ "relatedSpdxElement": "SPDXRef-nuget-MimeKit-4.13.0-23ac4f",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-MailKit-4.13.0-b26bfd",
+ "relatedSpdxElement": "SPDXRef-nuget-System.Formats.Asn1-8.0.1-037f22",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Http-2.3.0-239946",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Primitives-8.0.0-6087da",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Http-2.3.0-239946",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.Options-8.0.2-c1e33e",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Http-2.3.0-239946",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.DependencyInjection.Abstractions-8.0.0-18c45d",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Http-2.3.0-239946",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Http.Features-2.3.0-1fa4f7",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Http-2.3.0-239946",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Extensions.ObjectPool-8.0.11-da583c",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Http-2.3.0-239946",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Net.Http.Headers-2.3.0-6f214b",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Http-2.3.0-239946",
+ "relatedSpdxElement": "SPDXRef-nuget-System.Buffers-4.6.0-da25bf",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Http-2.3.0-239946",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.WebUtilities-2.3.0-b47477",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Http-2.3.0-239946",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Http.Abstractions-2.3.0-795f67",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-nuget-Microsoft.AspNetCore.Http-2.3.0-239946",
+ "relatedSpdxElement": "SPDXRef-nuget-System.Text.Encodings.Web-9.0.8-7001c6",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Components.Authorization-8.0.15-58c86a",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.NET.Test.Sdk-17.14.1-86ab34",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-nuget-PuppeteerSharp-20.2.2-21f7e5",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-nuget-NUnit-4.4.0-d51504",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.IdentityModel.Tokens-8.14.0-a883d6",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-nuget-bunit-1.40.0-4dce12",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-nuget-NUnit3TestAdapter-5.1.0-5f80ff",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-nuget-PuppeteerSharp-20.2.2-d76daa",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-nuget-HtmlAgilityPack-1.12.2-e4ed5d",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-nuget-IPAddressRange-6.2.0-96c860",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-nuget-System.IdentityModel.Tokens.Jwt-8.14.0-62ac6a",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.Rest.ClientRuntime-2.3.24-508401",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-nuget-RestSharp.Serializers.NewtonsoftJson-112.1.0-1f0a6b",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-nuget-RestSharp-112.1.0-839660",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-nuget-GraphQL.Client-6.1.0-7c0c75",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-259cdf",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-nuget-GraphQL.Client.Serializer.Newtonsoft-6.1.0-9b01fa",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-nuget-GraphQL.Client.Serializer.SystemTextJson-6.1.0-c25e33",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-nuget-IPAddressRange-6.2.0-2c2fd5",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-nuget-IPAddressRange-6.2.0-c9d65a",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-nuget-PuppeteerSharp-20.2.2-18c6e7",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-nuget-BlazorTable-1.17.0-647113",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-nuget-IPAddressRange-6.2.0-3030d8",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-nuget-GraphQL.Client.Serializer.Newtonsoft-6.1.0-af029d",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Components-8.0.15-869df9",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-nuget-Swashbuckle.AspNetCore-8.1.1-96b60e",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-nuget-PuppeteerSharp-20.2.2-336c02",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-nuget-Novell.Directory.Ldap.NETStandard-4.0.0-1ecb72",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Authentication.JwtBearer-8.0.15-6252d7",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-nuget-MimeKit-4.13.0-23ac4f",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-nuget-MailKit-4.13.0-b26bfd",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-nuget-System.Text.Encodings.Web-9.0.8-7001c6",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-nuget-Microsoft.AspNetCore.Http-2.3.0-239946",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-githubactions-actions-checkout-4..-75c946",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-githubactions-actions-setup-dotnet-4..-75c946",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-githubactions-actions-checkout-3-75c946",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-githubactions-githubcodeql-action-analyze-2-75c946",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-githubactions-githubcodeql-action-autobuild-2-75c946",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-githubactions-githubcodeql-action-init-2-75c946",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relatedSpdxElement": "SPDXRef-pypi-gitpython-75c946",
+ "relationshipType": "DEPENDS_ON"
+ },
+ {
+ "spdxElementId": "SPDXRef-DOCUMENT",
+ "relatedSpdxElement": "SPDXRef-github-CactuseSecurity-firewall-orchestrator-main-0c7c2b",
+ "relationshipType": "DESCRIBES"
+ }
+ ]
+}
diff --git a/documentation/certificates.md b/documentation/certificates.md
index e2a9e17437..a5d9a5c5ee 100644
--- a/documentation/certificates.md
+++ b/documentation/certificates.md
@@ -12,7 +12,8 @@ After the change restart apache2
sudo systemctl restart apache2
```
-## Change Root Certificate
+
+## Change Roor Certificate
Copy root cert to
diff --git a/documentation/developer-docs/UI/csharp/data-filtering.md b/documentation/developer-docs/UI/csharp/data-filtering.md
index 3739195f7d..a2dac0c351 100644
--- a/documentation/developer-docs/UI/csharp/data-filtering.md
+++ b/documentation/developer-docs/UI/csharp/data-filtering.md
@@ -5,7 +5,7 @@ first version:
```csharp
$"{rule.DeviceName} - Rule {rule.Id} {rule.Name}")
+ NameExtractor=@(rule => $"{rule.RulebaseName} - Rule {rule.Id} {rule.Name}")
NetworkObjectExtractor="rule => Array.ConvertAll(rule.Froms.Concat(rule.Tos).GroupBy(x => x.Object?.Id).Select(x => x.FirstOrDefault()).ToArray(), location => location.Object)"
NetworkServiceExtractor="rule => Array.ConvertAll(rule.Services, wrapper => wrapper.Content)"
NetworkUserExtractor="rule => Array.FindAll(Array.ConvertAll(rule.Froms.Concat(rule.Tos).GroupBy(x => x.User?.Id).Select(x => x.FirstOrDefault()).ToArray(), location => location.User), user => user != null)" />
diff --git a/documentation/developer-docs/importer/FWO-import-api.md b/documentation/developer-docs/importer/FWO-import-api.md
deleted file mode 100644
index 0072a32f18..0000000000
--- a/documentation/developer-docs/importer/FWO-import-api.md
+++ /dev/null
@@ -1,543 +0,0 @@
-# Importing firewall configs via API
-
-## JSON structure overview
-
-### Config in table import_config
-```json
-{
- "import_id": 1,
- "mgm_id": 12,
- "config": "config2import"
-}
-```
-### config2import
-```json
-{
- "network_objects": [...],
- "service_objects": [...],
- "user_objects": [...],
- "zone_objects": [...],
- "rules": [...],
- "routing": [],
- "interfaces": []
-}
-```
-
-## Explanations
-- In the following, all fields that are null, are optional.
-- Note that you need some of them depending on the object type (e.g. group needs members).
-- The control_id needs to be present in every single object for technical postgresql reasons.
-- group delimiter is the pipe ("|")
-- _refs fields contain a string with the uids of the referenced objects separated by "|"
-- the order of fields is arbitrary
-- _color can be any one listed in roles/database/files/csv/color.csv
-- action and track types can be found (and enhanced) in roles/database/files/sql/creation/fworch-fill-stm.sql
-
-## network_objects
-
-here we describe a single network object:
-```json
-{
- "control_id": 1, // bigint: ID of the current import
- "obj_ip": "1.5.1.1", // string: ipv4 or v6 address (in CIDR format)
- "obj_typ": "host", // string: see types below
- "obj_uid": "8a5bc8fb-8a10-4fd2-b4b7-825ee57bd6bd", // string: with unique network object id
- "obj_name": "host112", // string: name of network object
- "obj_color": null, // string: color of object, see Explanations
- "obj_ip_end": "1.5.1.1", // string: last ipv4 or v6 address (for ranges)
- "obj_comment": null, // string: comment
- "obj_member_names": null, // string: names of the group members separated by "|"
- "obj_member_refs": null, // string: uids of the referenced objects separated by "|"
- "obj_zone": null // string: name of the object's zone (e.g. for fortinet)
-}
-```
-- obj_typ can be any of the following (see roles/database/files/sql/creation/fworch-fill-stm.sql):
- network, group, host, machines_range, dynamic_net_obj, sofaware_profiles_security_level, gateway, cluster_member, gateway_cluster, domain, group_with_exclusion, ip_range, uas_collection, sofaware_gateway, voip_gk, gsn_handover_group, voip_sip, simple-gateway
-
-
-## service_objects
-here we describe a single service object:
-
-```json
-{
- "control_id": 1, // bigint: ID of the current import
- "svc_name": "AOL", // string: name of the service
- "svc_typ": "simple", // string: type of service (see below)
- "svc_uid": "97aeb44f-9aea-11d5-bd16-0090272ccb30", // string: unique service id
- "ip_proto": "6", // integer: 0-255 procol number
- "svc_port": "5190", // string: 1-65535 (or range)
- "svc_timeout": null, // integer: idle timeout in seconds
- "svc_port_end": null, // string: to be replaced by range in svc_port
- "svc_source_port": null, // string: optional source port restrictions
- "svc_source_port_end": null, // string: to be replaced by range in svc_source_port
- "svc_color": null, // string: color of object, see Explanations
- "svc_comment": null, // string: comment
- "svc_member_names": null, // string: names of the group members separated by "|"
- "svc_member_refs": null, // string: uids of the referenced service objects separated by "|"
- "rpc_nr": null // string: for rpc service the rpc id
-},
-```
-- svc_type can be any of the following: simple, group, rpc (see roles/database/files/sql/creation/fworch-fill-stm.sql)
-
-## user_objects
-
-here we describe a single user object:
-
-```json
-{
- "control_id": 1, // bigint: ID of the current import
- "user_typ": "simple", // string: either "group" or "simple"
- "user_uid": "c4d28191-bd44-4d45-8887-df94f594a8ef", // string: unique user id
- "user_name": "IA_User1", // string: user name
- "user_member_names": null, // string: names of the group members separated by "|"
- "user_member_refs": null, // string: uids of the referenced users separated by "|"
- "user_color": null, // string: color of object, see Explanations
- "user_comment": null, // string: comment
- "user_valid_until": null // string: user's "sell-by" date
-}
-```
-
-## zone_objects
-
-here we describe a single zone object:
-
-```json
-{
- "control_id": 1, // bigint: ID of the current import
- "zone_name": "zone1" // string: name of the zone
-}
-```
-
-## rules
-
-here we describe a single rule:
-
-```json
-{
- "control_id": 1, // bigint: ID of the current import
- "rulebase_name": "FirstLayer shared with inline layer", // string: specifies the rulebase name (all rules are contained in a single json struct)
- "rule_num": 1, // integer: rule number for ordering
- "rule_uid": "acc044f6-2a4f-459b-b78c-9e7afee92621", // string: unique rule id
- "rule_src": "user1@obj1|obj2", // string: list of source object names (if it contains user, use "@" as delimiter)
- "rule_dst": "Any", // string: list of destination network object names
- "rule_svc": "Any", // string: list of service names
- "rule_track": "None", // string: logging options, see below
- "rule_action": "Inner Layer", // string: rule action options, see below
- "rule_implied": false, // boolean: is it an implied (check point) rule derived from settings
- "rule_src_neg": false, // boolean: is the source field negated
- "rule_dst_neg": false, // boolean: is the destination field negated
- "rule_svc_neg": false, // boolean: is the service field negated
- "rule_disabled": false, // boolean: is the whole rule disabled
- "rule_src_refs": "9aa8e391-f811-4067-86f3-82646bd47d47@9aa8e391-f811-4067-86f3-82646bd47d40|2aa8e391-f811-4067-86f3-82646bd47d41", // string: source references
- "rule_dst_refs": "97aeb369-9aea-11d5-bd16-0090272ccb30",// string: destination references
- "rule_svc_refs": "97aeb369-9aea-11d5-bd16-0090272ccb30",// string: service references
- "rule_time": null, // string: any time restrictions of the rule
- "rule_installon": null, // string: list of gateways this rule should be applied to
- "parent_rule_uid": null, // string: for layers, the uid of the rule of layer above
- "rule_ruleid": null, // string: rule id (unique within gateway, but not globally)
- "rule_name": null, // string: optional name of the rule
- "rule_comment": null, // string: optional rule comment
- "rule_head_text": null, // string: for section headers this is the field to use
- "rule_from_zone": null, // string: source zone (if applicable) of the rule
- "rule_to_zone": null, // string: destination zone (if applicable) of the rule
- "rule_type": "access", // string: type of the nat rule: "access|combined|original|xlate", default "access"
- "rule_custom_fields": "{\"field1\": \"value1\"}" // string: json serialized user defined fields
-}
-```
-- rule_track can be any of log, none, alert, userdefined, mail, account, userdefined 1, userdefined 2, userdefined 3, snmptrap, log count, count, log alert, log alert count, log alert count alarm, log count alarm, count alarm, all, all start, utm, utm start, network log
-- rule_action can be any of accept, drop, deny, access, client encrypt, client auth, reject, encrypt, user auth, session auth, permit, permit webauth, redirect, map, permit auth, tunnel l2tp, tunnel vpn-group, tunnel vpn, actionlocalredirect, inner layer
-
-## Putting it all together 1
-
-This is a complete example of a config which may be imported:
-
-```json
-{
- "rules": [
- {
- "rulebase_name": "FirstLayer shared with inline layer",
- "control_id": 1074,
- "rule_num": 0,
- "rule_uid": "828b0f42-4b18-4352-8bdf-c9c864d692eb",
- "rule_name": null,
- "rule_comment": "test comment",
- "rule_src": "test-ext-vpn-gw|test-interop-device|BeeW10|wsus",
- "rule_dst": "sting-gw",
- "rule_svc": "IPSEC",
- "rule_time": "Any",
- "rule_from_zone": null,
- "rule_to_zone": null,
- "rule_track": "Log",
- "rule_action": "Drop",
- "rule_implied": false,
- "rule_src_neg": false,
- "rule_dst_neg": false,
- "rule_svc_neg": false,
- "rule_disabled": true,
- "rule_src_refs": "a580c5a3-379c-479b-b49d-487faba2442e|98bc04fc-b88b-4283-83ad-7b6899bc1876|2ad18398-e004-4324-af79-634be66941d6|2661ec9f-293f-4c82-8150-4bb6c883ca79",
- "rule_dst_refs": "cbdd1e35-b6e9-4ead-b13f-fd6389e34987",
- "rule_svc_refs": "97aeb475-9aea-11d5-bd16-0090272ccb30",
- "rule_installon": "Policy Targets",
- "parent_rule_uid": null,
- "rule_ruleid": null,
- "rule_type": "access",
- "rule_last_change_admin": null
- }
- ],
- "user_objects": [
- {
- "user_typ": "simple",
- "user_uid": "aae47c39-f416-4b32-801d-af53adfa1939",
- "user_name": "test-user1",
- "control_id": 1074,
- "user_color": "black",
- "user_comment": ""
- },
- {
- "user_typ": "group",
- "user_uid": "227d1a80-cc1e-4cd4-9576-4d46f271402f",
- "user_name": "test-group",
- "control_id": 1074,
- "user_color": "black",
- "user_comment": ""
- }
- ],
- "network_objects": [
- {
- "obj_ip": "22.55.200.192/26",
- "obj_typ": "network",
- "obj_uid": "5368caf0-d192-457b-9c86-5d5f9e5dc199",
- "obj_name": "Net_22.55.200.192-2",
- "obj_color": "black",
- "control_id": 1074,
- "obj_ip_end": "22.55.200.192/26",
- "obj_comment": null,
- "obj_member_refs": null,
- "obj_member_names": null
- }
- ],
- "service_objects": [
- {
- "rpc_nr": null,
- "svc_typ": "simple",
- "svc_uid": "97aeb44f-9aea-11d5-bd16-0090272ccb30",
- "ip_proto": "6",
- "svc_name": "AOL",
- "svc_port": "5190",
- "svc_color": "red",
- "control_id": 1074,
- "svc_comment": "AOL Instant Messenger. Also used by: ICQ & Apple iChat",
- "svc_timeout": "3600",
- "svc_port_end": "5190",
- "svc_member_refs": null,
- "svc_member_names": null
- }
- ],
- "zone_objects": [
- {
- "zone_name": "test-zone",
- "zone_uid": "98aeb44f-9aea-11d5-bd16-0090272ccb30",
- "control_id": 1074,
- "zone_comment": "just a test"
- }
- ],
- "routing": [
- {
- "destination": "10.0.3.38/32",
- "distance": 0,
- "interface": "port5.1524",
- "ip_version": 4,
- "metric": 0,
- "routing_device": 10,
- "source": null,
- "static": true,
- "target_gateway": "0.0.0.0"
- }
- ],
- "interfaces": [
- {
- "ip": "10.0.14.66",
- "ip_version": 4,
- "name": "port5.1524",
- "netmask_bits": 32,
- "routing_device": 10,
- "state_up": true
- }
- ]
-}
-```
-
-The following shows an example of how to import nat rules that are combined access/nat rules, here only translating destination (not showing irrelevant fields for brevity's sake):
-
-```json
-{
- "rules": [
- {
- "rule_num": 0,
- "rule_uid": "828b0f42-4b18-4352-8bdf-c9c864d692eb",
- "rule_src": "test-ext-vpn-gw|test-interop-device|BeeW10|wsus",
- "rule_dst": "sting-gw",
- "rule_svc": "IPSEC",
- "rule_src_refs": "a580c5a3-379c-479b-b49d-487faba2442e|98bc04fc-b88b-4283-83ad-7b6899bc1876|2ad18398-e004-4324-af79-634be66941d6|2661ec9f-293f-4c82-8150-4bb6c883ca79",
- "rule_dst_refs": "cbdd1e35-b6e9-4ead-b13f-fd6389e34987",
- "rule_svc_refs": "97aeb475-9aea-11d5-bd16-0090272ccb30",
- "rule_type": "combined", // this rule is both an access and a nat rule
- "xlate_rule: "123abcdef-4b18-4352-8bdf-c9c864d692eb"
- },
- {
- "rule_num": 1,
- "rule_uid": "123abcdef-4b18-4352-8bdf-c9c864d692eb",
- "rule_src": "test-ext-vpn-gw|test-interop-device|BeeW10|wsus",
- "rule_dst": "sting-gw_xlate",
- "rule_svc": "IPSEC",
- "rule_src_refs": "a580c5a3-379c-479b-b49d-487faba2442e|98bc04fc-b88b-4283-83ad-7b6899bc1876|2ad18398-e004-4324-af79-634be66941d6|2661ec9f-293f-4c82-8150-4bb6c883ca79",
- "rule_dst_refs": "123d1e35-b6e9-4ead-b13f-fd6389e34987",
- "rule_svc_refs": "97aeb475-9aea-11d5-bd16-0090272ccb30",
- "rule_type": "xlate",
- }
-
- ],
- ```
-
-
-## NAT Rules
-
-here we describe the representation of the rule in the rule table
-
-- "original" is the keyword for no translation - a special object needs to be created for this
-- algorithm for importing NAT rules:
- - a NAT rule is stored as two rules, for both of which nat_rule is set to "true"
- - the first rule contains the packet match information (original packet) and a pointer (in xlate_rule) to the translation rule
- - the translation rule has access_rule==false and xlate_rule==null set and defines how the packet is to be translated
- - for check point the NAT rules are pure NAT rules, meaning that access_rule is false
- - for other systems a rule can both be an access and an access rule
-- rule_action for the xlate_rule can be any of the following (new) values: hide, hide_pool, static_src, static_dst, static_src_and_dst
-
-- all existing reporting needs to be restricted to access rules (exception receritfication)
-- recertification should be possible for both NAT and access rules (should be configurable both per tenant and globally)
-
-#### final db rule tables
-
-The following gives an overview of the nat rule presentation as read via FWO API:
-
-```json
-{
- "control_id": 1, // bigint: ID of the current import
- "rulebase_name": "FirstLayer shared with inline layer", // string: specifies the rulebase name (all rules are contained in a single json struct)
- "rule_num": 1, // integer: rule number for ordering
- "rule_uid": "acc044f6-2a4f-459b-b78c-9e7afee92621", // string: unique rule id
- "rule_src": "user1@obj1|obj2", // string: list of source object names (if it contains user, use "@" as delimiter)
- "rule_dst": "Any", // string: list of destination network object names
- "rule_svc": "Any", // string: list of service names
- "rule_track": "None", // string: logging options, see below
- "rule_action": "Inner Layer", // string: rule action options, see below
- "rule_implied": false, // boolean: is it an implied (check point) rule derived from settings
- "rule_src_neg": false, // boolean: is the source field negated
- "rule_dst_neg": false, // boolean: is the destination field negated
- "rule_svc_neg": false, // boolean: is the service field negated
- "rule_disabled": false, // boolean: is the whole rule disabled
- "rule_src_refs": "9aa8e391-f811-4067-86f3-82646bd47d47@9aa8e391-f811-4067-86f3-82646bd47d40|2aa8e391-f811-4067-86f3-82646bd47d41", // string: source references
- "rule_dst_refs": "97aeb369-9aea-11d5-bd16-0090272ccb30",// string: destination references
- "rule_svc_refs": "97aeb369-9aea-11d5-bd16-0090272ccb30",// string: service references
- "rule_time": null, // string: any time restrictions of the rule
- "rule_installon": null, // string: list of gateways this rule should be applied to
- "parent_rule_uid": null, // string: for layers, the uid of the rule of layer above
- "rule_ruleid": null, // string: rule id (unique within gateway, but not globally)
- "rule_name": null, // string: optional name of the rule
- "rule_comment": null, // string: optional rule comment
- "rule_head_text": null, // string: for section headers this is the field to use
- "rule_from_zone": null, // string: source zone (if applicable) of the rule
- "rule_to_zone": null, // string: destination zone (if applicable) of the rule
- "access_rule": true,
- "nat_rule": true,
- "xlate_packet": {
- "source": "ip_123.1.0.1",
- "source_refs": "76aeb369-9aea-11d5-bd16-0090272ccb33",
- "destination": "original",
- "destination_refs": "original",
- "service": "original",
- "service_refs": "original"
- },
-}
-```
-
-#### final db rule tables with self ref to xlate rule
-```json
-{
- "control_id": 1, // bigint: ID of the current import
- "rulebase_name": "FirstLayer shared with inline layer", // string: specifies the rulebase name (all rules are contained in a single json struct)
- "rule_num": 1, // integer: rule number for ordering
- "rule_uid": "acc044f6-2a4f-459b-b78c-9e7afee92621", // string: unique rule id
- "rule_src": "user1@obj1|obj2", // string: list of source object names (if it contains user, use "@" as delimiter)
- "rule_dst": "Any", // string: list of destination network object names
- "rule_svc": "Any", // string: list of service names
- "rule_track": "None", // string: logging options, see below
- "rule_action": "Inner Layer", // string: rule action options, see below
- "rule_implied": false, // boolean: is it an implied (check point) rule derived from settings
- "rule_src_neg": false, // boolean: is the source field negated
- "rule_dst_neg": false, // boolean: is the destination field negated
- "rule_svc_neg": false, // boolean: is the service field negated
- "rule_disabled": false, // boolean: is the whole rule disabled
- "rule_src_refs": "9aa8e391-f811-4067-86f3-82646bd47d47@9aa8e391-f811-4067-86f3-82646bd47d40|2aa8e391-f811-4067-86f3-82646bd47d41", // string: source references
- "rule_dst_refs": "97aeb369-9aea-11d5-bd16-0090272ccb30",// string: destination references
- "rule_svc_refs": "97aeb369-9aea-11d5-bd16-0090272ccb30",// string: service references
- "rule_time": null, // string: any time restrictions of the rule
- "rule_installon": null, // string: list of gateways this rule should be applied to
- "parent_rule_uid": null, // string: for layers, the uid of the rule of layer above
- "rule_ruleid": null, // string: rule id (unique within gateway, but not globally)
- "rule_name": null, // string: optional name of the rule
- "rule_comment": null, // string: optional rule comment
- "rule_head_text": null, // string: for section headers this is the field to use
- "rule_from_zone": null, // string: source zone (if applicable) of the rule
- "rule_to_zone": null, // string: destination zone (if applicable) of the rule
- "access_rule": true, // string: is this an access rule
- "nat_rule": true, // string: is this a NAT rule
- "xlate_rule": 1234 // string: for NAT rules this is the link to the xlate rule which contains the translated fileds (src, dst, svc)
-}
-```
-- additional values for rule_action for xlate rules: hide, hide_pool, static_src, static_dst, static_src_and_dst
-
-## Envisioned Future Changes
-- network_services.ip_proto should be integer instead of string
-- network_objects.obj_ip_end can be null for single ip objects instead of repeating the same ip
-- The following fields might be dropped in later versions:
- - network_objects.obj_sw
- - network_objects.obj_location
- - network_objects.obj_member_names - could be derived from obj_member_refs
- - network_objects.last_change_admin
- - network_objects.last_change_time
- - network_services.svc_prod_specific - seems to be just a redundant copy of svc_typ
- - network_services.last_change_admin
- - network_services.last_change_time
- - network_services.svc_port_end - range could be entered as string "112-123"
- - network_services.svc_source_port_end - range could be entered as string "112-123"
- - users.last_change_admin
- - rules.rule_last_change_admin
-
-## 11/2022 target config
-
-```json
-{
- "control_id": 1074,
- "mgm_id": 221,
- "devices": [
- {
- "name": "abc",
- "id": 123,
- "rulebase_name": "FirstLayer shared with inline layer",
- "rules": [
- {
- "rulebase_name": "FirstLayer shared with inline layer",
- "control_id": 1074,
- "rule_num": 0,
- "rule_uid": "828b0f42-4b18-4352-8bdf-c9c864d692eb",
- "rule_name": null,
- "rule_comment": "test comment",
- "rule_src": "test-ext-vpn-gw|test-interop-device|BeeW10|wsus",
- "rule_dst": "sting-gw",
- "rule_svc": "IPSEC",
- "rule_time": "Any",
- "rule_from_zone": null,
- "rule_to_zone": null,
- "rule_track": "Log",
- "rule_action": "Drop",
- "rule_implied": false,
- "rule_src_neg": false,
- "rule_dst_neg": false,
- "rule_svc_neg": false,
- "rule_disabled": true,
- "rule_src_refs": "a580c5a3-379c-479b-b49d-487faba2442e|98bc04fc-b88b-4283-83ad-7b6899bc1876|2ad18398-e004-4324-af79-634be66941d6|2661ec9f-293f-4c82-8150-4bb6c883ca79",
- "rule_dst_refs": "cbdd1e35-b6e9-4ead-b13f-fd6389e34987",
- "rule_svc_refs": "97aeb475-9aea-11d5-bd16-0090272ccb30",
- "rule_installon": "Policy Targets",
- "parent_rule_uid": null,
- "rule_ruleid": null,
- "rule_type": "access",
- "rule_last_change_admin": null
- }
- ],
- "routing": [
- {
- "destination": "10.0.3.38/32",
- "distance": 0,
- "interface": "port5.1524",
- "ip_version": 4,
- "metric": 0,
- "routing_device": 10,
- "source": null,
- "static": true,
- "target_gateway": "0.0.0.0"
- }
- ],
- "interfaces": [
- {
- "ip": "10.0.14.66",
- "ip_version": 4,
- "name": "port5.1524",
- "netmask_bits": 32,
- "routing_device": 10,
- "state_up": true
- }
- ]
- }
- ],
- "user_objects": [
- {
- "user_typ": "simple",
- "user_uid": "aae47c39-f416-4b32-801d-af53adfa1939",
- "user_name": "test-user1",
- "control_id": 1074,
- "user_color": "black",
- "user_comment": ""
- },
- {
- "user_typ": "group",
- "user_uid": "227d1a80-cc1e-4cd4-9576-4d46f271402f",
- "user_name": "test-group",
- "control_id": 1074,
- "user_color": "black",
- "user_comment": ""
- }
- ],
- "network_objects": [
- {
- "obj_ip": "22.55.200.192/26",
- "obj_typ": "network",
- "obj_uid": "5368caf0-d192-457b-9c86-5d5f9e5dc199",
- "obj_name": "Net_22.55.200.192-2",
- "obj_color": "black",
- "control_id": 1074,
- "obj_ip_end": "22.55.200.192/26",
- "obj_comment": null,
- "obj_member_refs": null,
- "obj_member_names": null
- }
- ],
- "service_objects": [
- {
- "rpc_nr": null,
- "svc_typ": "simple",
- "svc_uid": "97aeb44f-9aea-11d5-bd16-0090272ccb30",
- "ip_proto": "6",
- "svc_name": "AOL",
- "svc_port": "5190",
- "svc_color": "red",
- "control_id": 1074,
- "svc_comment": "AOL Instant Messenger. Also used by: ICQ & Apple iChat",
- "svc_timeout": "3600",
- "svc_port_end": "5190",
- "svc_member_refs": null,
- "svc_member_names": null
- }
- ],
- "zone_objects": [
- {
- "zone_name": "test-zone",
- "svc_typ": "simple",
- "zone_uid": "98aeb44f-9aea-11d5-bd16-0090272ccb30",
- "control_id": 1074,
- "zone_comment": "just a test"
- }
- ]
-}
-```
diff --git a/documentation/developer-docs/importer/firewall-APIs/checkpoint/api-calls-used.md b/documentation/developer-docs/importer/firewall-APIs/checkpoint/api-calls-used.md
deleted file mode 100644
index 1b6c523f76..0000000000
--- a/documentation/developer-docs/importer/firewall-APIs/checkpoint/api-calls-used.md
+++ /dev/null
@@ -1,38 +0,0 @@
-# API feature support
-
-Firewall Orchestrator uses the following Check Point API calls, which are available from CP API 1.3 - R80.20,
-which is the earliest version supported by Firewall Orchestrator.
-
-See also https://sc1.checkpoint.com/documents/latest/APIs/
-
-```
-show-api-versions
-show-access-rulebase
-show-address-ranges
-show-application-site-categories
-show-application-sites
-show-changes
-show-dns-domains
-show-gateways-and-servers
-show-groups
-show-groups-with-exclusion
-show-hosts
-show-multicast-address-ranges
-show-nat-rulebase
-show-networks
-show-object
-show-packages
-show-services-tcp
-show-services-udp
-show-services-dce-rpc
-show-services-rpc
-show-services-other
-show-services-icmp
-show-services-icmp6
-show-services-sctp
-show-services-gtp
-show-service-groups
-show-simple-gateways
-show-task
-show-updatable-objects-repository-content
-```
diff --git a/documentation/developer-docs/importer/firewall-APIs/checkpoint/checkpoint-r8x-import-process.png b/documentation/developer-docs/importer/firewall-APIs/checkpoint/checkpoint-r8x-import-process.png
deleted file mode 100644
index c9ac36d47a..0000000000
Binary files a/documentation/developer-docs/importer/firewall-APIs/checkpoint/checkpoint-r8x-import-process.png and /dev/null differ
diff --git a/documentation/developer-docs/importer/firewall-APIs/checkpoint/hits.md b/documentation/developer-docs/importer/firewall-APIs/checkpoint/hits.md
deleted file mode 100644
index b5a36a62e4..0000000000
--- a/documentation/developer-docs/importer/firewall-APIs/checkpoint/hits.md
+++ /dev/null
@@ -1,43 +0,0 @@
-# The data format of hits
-## source
-
-## command
-
- show-access-rule
-
-with header
-
- {
- "name" : "Rule 1",
- "layer" : "Network",
- "show-hits" : true,
- "hits-settings" : {"from-date" : "2014-01-01", "to-date" : "2014-12-31T23:59"}
- }
-
-hits-settings is optional
-
-## output
-
- percentage: str "x%" with x in [0-100]
- level: str "x" with x in {zero, low, medium, high, very high}
- value: int x
- first date: obj X with X object that contains posix and iso-8601 dates, only available if at least one hit occured
- last date: obj X see first date
-
-# example
-
-An example output in json format is
-
- "hits": {
- "percentage": "0%",
- "level": "low",
- "value": 2,
- "first-date": {
- "posix": 1602857214000,
- "iso-8601": "2020-10-16T07:06-0700"
- },
- "last-date": {
- "posix": 1602857214000,
- "iso-8601": "2020-10-16T07:06-0700"
- }
- }
diff --git a/documentation/developer-docs/importer/firewall-APIs/checkpoint/importer-CP-R8x-Cert-Export.md b/documentation/developer-docs/importer/firewall-APIs/checkpoint/importer-CP-R8x-Cert-Export.md
deleted file mode 100644
index 147ecb40b5..0000000000
--- a/documentation/developer-docs/importer/firewall-APIs/checkpoint/importer-CP-R8x-Cert-Export.md
+++ /dev/null
@@ -1,10 +0,0 @@
-# How to export certificates from Check Point R8x management server
-
-Necessary for importing into FW-Orch importer to get rid of cert warnings
-
-Source: https://sc1.checkpoint.com/documents/R81/WebAdminGuides/EN/CP_R81_CLI_ReferenceGuide/Topics-CLIG/MDSG/fwm-printcert.htm?tocpath=Multi-Domain%20Security%20Management%20Commands%7Cfwm%7C_____10
-
-
-Example:
-
- fwm printcert -ca internal_ca -x509 out.cert -p >cp.crt
\ No newline at end of file
diff --git a/documentation/developer-docs/importer/firewall-APIs/checkpoint/install-r8x-test-system.md b/documentation/developer-docs/importer/firewall-APIs/checkpoint/install-r8x-test-system.md
deleted file mode 100644
index c54e9490a3..0000000000
--- a/documentation/developer-docs/importer/firewall-APIs/checkpoint/install-r8x-test-system.md
+++ /dev/null
@@ -1,26 +0,0 @@
-# install R8x test system
-
-- download r80.40 all-in-one system from
-- install on virtual box machine
- - bridged fixed ip virtual box using linux 64-bit kernel 2.4/2.6/3/4
- - at least 8 GB RAM
- - at least 2 CPUs (Mgmt-Server and API won't run witha single CPU)
- - during install dialogue, choose mgmt + gateway install (or later try multi domain mgmt)
-- connect to mgmt interface via https and run first time wizard (keep clients can connect from any host), restart
-- connect via ssh and set expert password
-- also run
-
- add user fworch userid 1000 homedir /home/fworch
- add rba user fworch roles monitorRole
- set user fworch password 'secret'
- # add allowed-client network ipv4-address 10.8.6.0 mask-length 24
-
-## Tell api to listen for all gui clients without GUI client
- mgmt_cli -r true --domain MDS set api-settings accepted-api-calls-from "All IP addresses"
- api reconf
-
-## useful commands for later usage
- show rba all
- show route all
- show route static all
-
diff --git a/documentation/developer-docs/importer/firewall-APIs/checkpoint/r8x-parse-only-testing.md b/documentation/developer-docs/importer/firewall-APIs/checkpoint/r8x-parse-only-testing.md
deleted file mode 100644
index 7ae0740a81..0000000000
--- a/documentation/developer-docs/importer/firewall-APIs/checkpoint/r8x-parse-only-testing.md
+++ /dev/null
@@ -1,16 +0,0 @@
-# how to test with a config file without contact to the original manager
-
-```
-fworch@ubu18test:~$ python3 importer/checkpointR8x/parse_config.py -f /tmp/isotmp/3/cfg/xxx11.cfg -r 'gMgmt-Cluster_Internet-internal Security' -d 1
-2021-05-04 06:04:07,232 - DEBUG - debug_level: 1
-2021-05-04 06:04:07,418 - DEBUG - parse_config - args
-f:/tmp/isotmp/3/cfg/xxx11.cfg
-i: 0
-m:
-r: gMgmt-Cluster_Internet-internal Security
-n: False
-s: False
-u: False
-d: 1
-"0"%"0"%"gMgmt-Cluster_Internet-internal Security"%%"false"%"False"%"Any"%"97aeb369-9aea-11d5-bd16-0090272ccb30"%"False"%"Any"%"97aeb369-9aea-11d5-bd16-0090272ccb30"%"False"%"Any"%"97aeb369-9aea-11d5-bd16-0090272ccb30"%"Accept"%"Log"%"Policy Targets"%"Any"%""%%"24d3149b-7be1-49a3-addb-190e8271baac"%"FW INTERNAL COMMUNICATION"%%%
-```
diff --git a/documentation/developer-docs/importer/firewall-APIs/checkpoint/readme.md b/documentation/developer-docs/importer/firewall-APIs/checkpoint/readme.md
deleted file mode 100644
index 3a0bed0360..0000000000
--- a/documentation/developer-docs/importer/firewall-APIs/checkpoint/readme.md
+++ /dev/null
@@ -1,132 +0,0 @@
-# Integrating Check Point R8x via API
-
-## initial CP API connect
-First connect to api should result in the following:
-```console
-tim@acantha:~$ wget --no-check-certificate https://192.168.100.110/web_api/
-Connecting to 192.168.100.110:443... connected.
-WARNING: cannot verify 192.168.100.110's certificate, issued by ‘unstructuredName=An optional company name,emailAddress=Email Address,CN=192.168.100.110,L=Locality Name (eg\\, city)’:
- Self-signed certificate encountered.
-HTTP request sent, awaiting response... 401 Unauthorized
-Username/Password Authentication Failed.
-```
-
-if you get the following:
-```console
-tim@acantha:~$ wget --no-check-certificate https://192.168.9.99/web_api/
-HTTP request sent, awaiting response... 403 Forbidden
-2020-06-03 12:56:12 ERROR 403: Forbidden.
-```
-make sure the api server is up and running and accepting connections from your ip address:
-(taken from )
-```console
-mgmt_cli -r true --domain MDS set api-settings accepted-api-calls-from "All IP addresses"`
-api restart
-```
-### create api user profile
-
-```console
-```
-
-### Create read-only api user
-```console
-```
-
-### Create full access api user
-```console
-```
-
-## login
-```console
-curl --insecure --request POST --url https://192.168.100.88/web_api/login --header 'Content-Type: application/json' --data '{"user": "apiuser", "password" : "xxx"}'
-```
-
-gives the sid (session id) which can then be used to authenticate for further api calls
-```json
-{
- "sid" : "PhTmI9SD02MTtCWCcTHpc8FsIlX63icc9CvF19PB3qo",
- "url" : "https://192.168.100.88:443/web_api",
- "session-timeout" : 600,
- "last-login-was-at" : {
- "posix" : 1631716365111,
- "iso-8601" : "2021-09-15T16:32+0200"
- },
- "read-only" : true,
- "api-server-version" : "1.8",
- "user-name" : "apiuser",
- "user-uid" : "ba2038a1-437f-45ef-8ea5-c8785cdad9a7"
-}```
-
-## login to a domain of an MDS
-```console
-curl --insecure --request POST --url https://192.168.100.88/web_api/login --header 'Content-Type: application/json' --data '{"user": "apiuser", "password" : "xxx", "domain": "yyy"}'
-```
-
-gives the sid (session id) which can then be used to authenticate for further api calls
-```json
-{
- "sid" : "PhTmI9SD02MTtCWCcTHpc8FsIlX63icc9CvF19PB3qo",
- "url" : "https://192.168.100.88:443/web_api",
- "session-timeout" : 600,
- "last-login-was-at" : {
- "posix" : 1631716365111,
- "iso-8601" : "2021-09-15T16:32+0200"
- },
- "read-only" : true,
- "api-server-version" : "1.8",
- "user-name" : "apiuser",
- "user-uid" : "ba2038a1-437f-45ef-8ea5-c8785cdad9a7"
-}
-```
-
-
-## logout
-
-```console
-```
-
-## get a rulebase (part)
-
-```console
-curl --insecure --request POST \
- --url https://192.168.100.88/web_api/show-access-rulebase \
- --header 'Content-Type: application/json' \
- --header 'X-chkp-sid: PhTmI9SD02MTtCWCcTHpc8FsIlX63icc9CvF19PB3qo' \
- --data '{"name": "FirstLayer shared with inline layer"}'
-```
-
-## get an arbitrary object by UID
-
-```console
-curl --insecure --request POST --url https://192.168.100.88/web_api/show-object --header 'Content-Type: application/json' --header 'X-chkp-sid: KJC5pzFMSRINoVTSByVhUq1xdEE33WD0uy9iXl-cG-4' --data '{"uid": "dd699ecd-1420-41a0-931f-de7f55f799b6", "details-level": "full"}'
-```
-results in
-```console
-{
- "object" : {
- "uid" : "d699ecd-1420-41a0-931f-de7f55f799b6",
- "type" : "access-section",
- "domain" : {
- "uid" : "3981ee76-52c3-1744-bf5b-75fe309b1ed9",
- "name" : "dom-name1",
- "domain-type" : "domain"
- },
- "tags" : [ ],
- "meta-info" : {
- "lock" : "unlocked",
- "validation-state" : "ok",
- "last-modify-time" : {
- "posix" : 1668506934927,
- "iso-8601" : "2022-11-15T11:08+0100"
- },
- "last-modifier" : "admin-user-1234",
- "creation-time" : {
- "posix" : 1668506934927,
- "iso-8601" : "2022-11-15T11:08+0100"
- },
- "creator" : "admin-user-3433"
- },
- "read-only" : false
- }
-}
-```
diff --git a/documentation/developer-docs/importer/firewall-APIs/cisco-aci/cisco-aci-api.md b/documentation/developer-docs/importer/firewall-APIs/cisco-aci/cisco-aci-api.md
deleted file mode 100644
index 87775e759f..0000000000
--- a/documentation/developer-docs/importer/firewall-APIs/cisco-aci/cisco-aci-api.md
+++ /dev/null
@@ -1,193 +0,0 @@
-# Integrating Cisco ACI
-
-- Introduction on ACI reg. firewalling:
-
-
-- API references:
-
-- ACI Fabric Network Access Security Policy Model (Contracts):
-
-## online sandbox APIC
-
-- base (UI) URL: https://sandboxapicdc.cisco.com
-- doc URL: https://sandboxapicdc.cisco.com/doc/html
-- API URL: https://sandboxapicdc.cisco.com/api/xxx.json
-- user: admin
-- password: !v3G@!4@Y https://sandboxapicdc.cisco.com
-
-## user setup
-
-## login
-```console
-curl --request POST \
- --url https://sandboxapicdc.cisco.com/api/aaaLogin.json \
- --header 'Content-Type: application/json' \
- --data '{
- "aaaUser" : {
- "attributes" : {
- "name" : "admin",
- "pwd" : "!v3G@!4@Y"
- }
- }
-}'
-```
-gives
-```json
-{
- "totalCount": "1",
- "imdata": [
- {
- "aaaLogin": {
- "attributes": {
- "token": "xxx",
- "siteFingerprint": "dlcoaz3nzznwlp4f3icvj69l11fp0mys",
- "userName": "admin",
- "version": "5.2(1g)",
- }
- }
- }
- ]
-}
-```
-
-## logout
-From the documentation at :
-
- At this time, the aaaLogout method returns a response but does not end a session. Your session ends after a refresh timeout when you stop sending aaaRefresh messages.
-
-## get various config parts (classes)
-
-All of the below classes "CLASS" can be retrieved as follows:
-```console
-curl --request GET \
- --url https://sandboxapicdc.cisco.com/api/class/CLASS.json \
- --header 'Content-Type: application/json' \
- --cookie APIC-cookie=xxx \
-```
-
-### Basic config
-- fvBD - bridge domains
-- aaaDomain - Domains
-- fvEPg - End Point Groups (EPGs)
-- fvAEPg - Application endpoint groups
-- aaaUser - users
-- fvTenant - tenants
-- application profiles
-- vzGraphCont, vzGraphDef - service graphs
-- outside connections - L3Out external EPG (l3extInstP)
-- End Point Security Groups (ESGs)
-
-### Contract information
-- vzFilter - filters?
-- vzACtrct - contracts
-- vzACtrctEpgDef - contracts with EPGs?
-- vzBrCP - Binary Contract Profile
-- vzABrCP - abstraction of Binary Contract Profile
-- vzACollection - ???
-- vzConsDef - Consumer EPg
-- vzProvDef - Provider EPg
-- vzCtrctEPgCont - Contract EPG Container
-- vzCtrctEntityDef - Summary of EPg: Contains All Info to Create ProvDef/ConsDef
-- vzRsDenyRule - deny rule (cleanup)
-- vzRuleOwner gives an exhaustive list of what looks like fw rules
-```json
-{
- "totalCount": "55",
- "imdata": [
- {
- "vzRuleOwner": {
- "attributes": {
- "action": "permit",
- "childAction": "",
- "creatorDn": "topology/pod-1/node-101/sys/ctx-[vxlan-16777200]",
- "ctrctName": "",
- "direction": "uni-dir",
- "dn": "topology/pod-1/node-101/sys/actrl/scope-16777200/rule-16777200-s-any-d-any-f-implarp/own-[topology/pod-1/node-101/sys/ctx-[vxlan-16777200]]-tag-vrf",
- "intent": "install",
- "lcOwn": "local",
- "markDscp": "unspecified",
- "modTs": "2021-09-20T14:07:18.567+00:00",
- "monitorDn": "uni/tn-common/monepg-default",
- "name": "",
- "nameAlias": "",
- "prio": "any_any_filter",
- "qosGrp": "unspecified",
- "status": "",
- "tag": "vrf",
- "type": "tenant"
- }
- }
- },
-```
-- vzToEPg
-```json
-{
- "totalCount": "12",
- "imdata": [
- {
- "vzToEPg": {
- "attributes": {
- "bdDefDn": "uni/bd-[uni/tn-Tenant_ISK/BD-BD_ISK]-isSvc-no",
- "bdDefStQual": "none",
- "bgpDomainId": "10671",
- "childAction": "",
- "ctxDefDn": "uni/ctx-[uni/tn-Tenant_ISK/ctx-VRF_ISK]",
- "ctxDefStQual": "none",
- "ctxPcTag": "49153",
- "ctxSeg": "2883584",
- "descr": "",
- "dn": "cdef-[uni/tn-common/brc-app_to_web]/epgCont-[uni/tn-Tenant_ISK/ap-AP_ISK/epg-web]/fr-[uni/tn-common/brc-app_to_web/dirass/prov-[uni/tn-Tenant_ISK/ap-AP_ISK/epg-web]-any-no]/to-[uni/tn-common/brc-app_to_web/dirass/cons-[uni/tn-Tenant_ISK/ap-AP_ISK/epg-app]-any-no]",
- "epgDefDn": "uni/tn-common/brc-app_to_web/dirass/cons-[uni/tn-Tenant_ISK/ap-AP_ISK/epg-app]-any-no",
- "epgDn": "uni/tn-Tenant_ISK/ap-AP_ISK/epg-app",
- "intent": "install",
- "isCtxPcTagInUseForRules": "no",
- "isGraph": "no",
- "l3CtxEncap": "unknown",
- "lcOwn": "local",
- "modTs": "2021-09-20T14:21:02.498+00:00",
- "monPolDn": "",
- "name": "",
- "nameAlias": "",
- "ownerKey": "",
- "ownerTag": "",
- "pcTag": "16387",
- "prefGrMemb": "exclude",
- "scopeId": "2883584",
- "servicePcTag": "any",
- "status": "",
- "txId": "17870283321406128551"
- }
- }
- },
-```
-
-## get change notifications
-
-- vzReeval?
-
-see
-
-Subscribing to Query Results - can be used to be informated about any changes. This needs a websocket connection.
-
-## get a list of fw rules
-
-## get nat rules
-
-
-## Example ansible task to add an EPG
-
-```ansible
- - name: Add static EPG
- aci_rest:
- host: "{{host}}"
- username: "{{username}}"
- password: "{{password}}"
- method: post
- path: /api/node/mo/uni/tn-{{item.tenant}}/ap-{{item.approfile}}/epg-{{item.epgname}}.json
- content: "{{ lookup('template', 'templates/epg1.j2') }}"
- validate_certs: no
- with_items: '{{ ports }}'
- ignore_errors: yes
- loop_control:
- pause: 1
-```
\ No newline at end of file
diff --git a/documentation/developer-docs/importer/firewall-APIs/cisco-firepower/readme.md b/documentation/developer-docs/importer/firewall-APIs/cisco-firepower/readme.md
deleted file mode 100644
index 880c73fe75..0000000000
--- a/documentation/developer-docs/importer/firewall-APIs/cisco-firepower/readme.md
+++ /dev/null
@@ -1,18 +0,0 @@
-# Cisco FirePower Documentation
-
-## Recipe
-1. Get devnet access
-2. Reserve Environment for 4h
-3. Test API Calls using Insomnia or similar
-4. Write importer module
-
-## Resources
-### Public
-- https://www.cisco.com/c/en/us/support/security/defense-center/products-programming-reference-guides-list.html
-- PDF: https://www.cisco.com/c/en/us/td/docs/security/firepower/720/api/REST/secure_firewall_management_center_rest_api_quick_start_guide_720.html
-- https://github.com/veeratcisco/postman-fmc/blob/main/collection.zip
-
-### Devnet
-- https://developer.cisco.com/learning/labs/FMC-RESTAPI-02/introduction/
-- https://devnetsandbox.cisco.com/
-- https://fmcrestapisandbox.cisco.com/api/api-explorer/
diff --git a/documentation/developer-docs/importer/firewall-APIs/cisco-sda/cisco-sda-api.md b/documentation/developer-docs/importer/firewall-APIs/cisco-sda/cisco-sda-api.md
deleted file mode 100644
index 44df93fb7e..0000000000
--- a/documentation/developer-docs/importer/firewall-APIs/cisco-sda/cisco-sda-api.md
+++ /dev/null
@@ -1,75 +0,0 @@
-# Integrating Cisco ACI
-
-## Acronyms
-- Cisco Identity Services Engine (ISE)
-- Scalable Group Tag (SGT)
-- Scalable Group Access Control Entries (SGACE's)
-
-## intro
-Cisco DNA Center uses SSH to establish a trust relationship with ISE. It also uses RestAPI to configure the contracts and policies into ISE. ISE pxGrid information bus is used by Cisco DNA Center to retrieve context information like the SGT's available.
-
-
-## online sandbox APIC
-
-source: https://devnetsandbox.cisco.com/RM/Diagram/Index/c3c949dc-30af-498b-9d77-4f1c07d835f9?diagramType=Topology
-
-- SDA policy management: https://community.cisco.com/t5/security-documents/policy-provisioning-and-operation-in-sda/ta-p/3712744
-- base (UI) URL: https://sandboxdnac.cisco.com/
-- doc URL: https://developer.cisco.com/docs/dna-center/api/1-3-3-x/
-- API reference: https://developer.cisco.com/docs/dna-center/#!cisco-dna-2-2-2-api-api-sda-get-sda-fabric-info
-- doc API: https://developer.cisco.com/docs/dna-center/#!software-defined-access-sda/software-defined-access-api
-- API URL: https://sandboxdnac.cisco.com/dna/intent/api/v1/...
-- Community: https://community.cisco.com/t5/networking-blogs/welcome-to-the-dna-center-api-support-community/ba-p/3663632
-- user: devnetuser
-- password: Cisco123!
-
-## user setup
-
-## login
-
-base64 encode the credentials:
-
- tim@acantha:~$ echo devnetuser:Cisco123! | base64
- ZGV2bmV0dXNlcjpDaXNjbzEyMyEK
- tim@acantha:~$
-
-For some reason the authentication only works when you replace the final K with padding character "=".
-
-API call:
-
-```console
-curl --request POST \
- --url https://sandboxdnac.cisco.com/dna/system/api/v1/auth/token \
- --header 'Authorization: Basic ZGV2bmV0dXNlcjpDaXNjbzEyMyE=' \
- --header 'Content-Type: application/json' \
-```
-gives
-
-```json
-{
- "Token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI2MGVjNGU0ZjRjYTdmOTIyMmM4MmRhNjYiLCJhdXRoU291cmNlIjoiaW50ZXJuYWwiLCJ0ZW5hbnROYW1lIjoiVE5UMCIsInJvbGVzIjpbIjVlOGU4OTZlNGQ0YWRkMDBjYTJiNjQ4ZSJdLCJ0ZW5hbnRJZCI6IjVlOGU4OTZlNGQ0YWRkMDBjYTJiNjQ4NyIsImV4cCI6MTYzMjU2NDU0NSwiaWF0IjoxNjMyNTYwOTQ1LCJqdGkiOiJmMDQyNGIwZi05NTE1LTQxYTctYjVjZC03ODUyNThlZjYzMzYiLCJ1c2VybmFtZSI6ImRldm5ldHVzZXIifQ.ptbCQ4JN6TRyD1ufmI0evKcrNlwthO071YIIISGBOXbxT2VvRTq5x7EPF16gk98N1hYKN80Sx3Q5-XUcwH0TUNoPS7X7kKh7bZmRxWxO59GhKCjtdGgoZmc3sz2jNf1q6CQzsh4gnwQypUUpZq57U6Dwa1RqpY-O7Mtp69akJW7P40_Zz68_vNyS610xkUNiLt4IeVw6ZavblJ2pchyiFJ9tp5rBPMMPpVCl7X63UDSjgWn1echV0Vs3rpq140ZjrWHEsb8DE2LLXG0aWcLcHaXYRkgmueJmbipkAj2KdYE2CsBRPP3naf_-I1JBN6cmFfMkpKH6GWWqT8eYV3BGGA"
-}
-```
-
-## logout
-
-## get network devices
-
-```console
-curl --request GET \
- --url https://sandboxdnac.cisco.com/dna/intent/api/v1/network-device \
- --header 'Content-Type: application/json' \
- --header 'X-Auth-Token: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI2MGVjNGU0ZjRjYTdmOTIyMmM4MmRhNjYiLCJhdXRoU291cmNlIjoiaW50ZXJuYWwiLCJ0ZW5hbnROYW1lIjoiVE5UMCIsInJvbGVzIjpbIjVlOGU4OTZlNGQ0YWRkMDBjYTJiNjQ4ZSJdLCJ0ZW5hbnRJZCI6IjVlOGU4OTZlNGQ0YWRkMDBjYTJiNjQ4NyIsImV4cCI6MTYzMjU2NDU0NSwiaWF0IjoxNjMyNTYwOTQ1LCJqdGkiOiJmMDQyNGIwZi05NTE1LTQxYTctYjVjZC03ODUyNThlZjYzMzYiLCJ1c2VybmFtZSI6ImRldm5ldHVzZXIifQ.ptbCQ4JN6TRyD1ufmI0evKcrNlwthO071YIIISGBOXbxT2VvRTq5x7EPF16gk98N1hYKN80Sx3Q5-XUcwH0TUNoPS7X7kKh7bZmRxWxO59GhKCjtdGgoZmc3sz2jNf1q6CQzsh4gnwQypUUpZq57U6Dwa1RqpY-O7Mtp69akJW7P40_Zz68_vNyS610xkUNiLt4IeVw6ZavblJ2pchyiFJ9tp5rBPMMPpVCl7X63UDSjgWn1echV0Vs3rpq140ZjrWHEsb8DE2LLXG0aWcLcHaXYRkgmueJmbipkAj2KdYE2CsBRPP3naf_-I1JBN6cmFfMkpKH6GWWqT8eYV3BGGA'
-```
-
-## get sda information
-
-unfortunately the sandbox does not seam to conain any sda config:
-
-
-
-## get a list of fw rules
-
-## get nat rules
-
-
diff --git a/documentation/developer-docs/importer/firewall-APIs/fortinet/fortiManager-api-howto.md b/documentation/developer-docs/importer/firewall-APIs/fortinet/fortiManager-api-howto.md
deleted file mode 100644
index c93c5a908e..0000000000
--- a/documentation/developer-docs/importer/firewall-APIs/fortinet/fortiManager-api-howto.md
+++ /dev/null
@@ -1,189 +0,0 @@
-# Integrating Fortinet FortiManager 7.x
-
-## API Docs
-
-(need account)
-
-https://fndn.fortinet.net/index.php?/fortiapi/5-fortimanager/&_fromLogin=1
-
-
-
-## user setup
-
-connect to FM via ssh (admin/empty password) and add the following config
-
-### make existing admin api ready
-
-```console
-config system admin user
- edit admin
- set rpc-permit read-write
-end
-```
-
-### create api user profile
-
-```console
-config system admin profile
- edit "apiuserprofile"
- set super-user-profile enable
- next
-```
-enter yes here!
-
-NB: the user will have full rw access via UI but can be restricted to read-only via API as shown below.
-Need to find out if there is a more secure way to create an all-read-only api user
-
-### Create read-only api user
-```console
-config system admin user
- edit "apiuser"
- set password xxx
- set adom "all_adoms"
- set profileid "apiuserprofile"
- set rpc-permit read
- end
-```
-
-### Create full access api user
-```console
-config system admin user
- edit "apiuser"
- set password xxx
- set adom "all_adoms"
- set profileid "apiuserprofile"
- set rpc-permit read-write
-end
-```
-
-## login
-```console
-curl --request POST \
- --url https://10.5.1.55/jsonrpc \
- --header 'Content-Type: application/json' \
- --data '{
- "id": 1,
- "method": "exec",
- "params": [
- {
- "data": [
- {
- "passwd": "xxx",
- "user": "apiuser"
- }
- ],
- "url": "sys/login/user"
- }
- ]
-}'
-```
-
-gives
-```json
-{
- "id": 1,
- "result": [
- {
- "status": {
- "code": 0,
- "message": "OK"
- },
- "url": "sys\/login\/user"
- }
- ],
- "session": "KCOuhqKTFt3ISXKntpIVO2kA5GJ+QcorMoxm8xLGru0HxrwwpgWuTtRcU8P9XCpbIRlDjjSv2+lzTYYIt1bSzw=="
-}
-```
-
-## logout
-
-```console
-curl --request POST \
- --url https://10.5.1.55/jsonrpc \
- --header 'Content-Type: application/json' \
- --data '{
- "id": 1,
- "jsonrpc": "1.0",
- "method": "exec",
- "params": [
- {
- "url": "sys/logout"
- }
- ],
- "verbose": 1,
- "session": "BJG0kh4qBopxgjJ+DwEyxJSWCl3MzHdeeympX4GJqw50EoLIjXoLH3+7W3e4N9EqtWb5IhGKBJugGKS6HQrDUg=="
-}'
-```
-
-## get a list of all ADOMs
-
-```console
-curl --request POST \
- --url https://10.5.1.55/jsonrpc \
- --header 'Content-Type: application/json' \
- --data '{
- "method": "get",
- "params": [
- {
- "fields": ["name", "oid", "uuid"],
- "url": "/dvmdb/adom"
- }
- ],
- "session": "lieHUJqiA0VldI45nVh8K2o0kP2XRm7NrrayIL1t977BG78\/wukwCgnFnpClbH9A6rAQbCPVjcGVFOw1VwULLQ=="
-}
-'
-```
-
-## get a list of fw rules
-
-```console
-curl --request POST \
- --url https://10.5.1.55/jsonrpc \
- --header 'Content-Type: application/json' \
- --data '{
- "method": "get",
- "params": [
- {
- "url": "/pm/config/adom/my_adom/pkg/mypkg/firewall/policy"
- }
- ],
- "verbose": 1,
- "id": 2,
- "session": "++7z161rod0cGMaStLUWohDpyUnsyT030tNuLPyVYvIhd0GCLXwp9vCJRKnYV4I0Q\/di1bSL3Wf7o8oNnWu6cw=="
-}'
-```
-
-## get nat rules
-
-### get snat rules
-```console
-{
- "method": "get",
- "params": [
- {
- "url": "/pm/config/adom/my_adom/pkg/mypkg/firewall/central-snat-map"
- }
- ],
- "verbose": 1,
- "id": 2,
- "session": "++7z161rod0cGMaStLUWohDpyUnsyT030tNuLPyVYvIhd0GCLXwp9vCJRKnYV4I0Q\/di1bSL3Wf7o8oNnWu6cw=="
-}'
-```
-### get dnat rules
-
-```console
-curl --request POST \
- --url https://10.5.1.55/jsonrpc \
- --header 'Content-Type: application/json' \
- --data '{
- "method": "get",
- "params": [
- {
- "url": "/pm/config/adom/my_adom/pkg/mypkg/firewall/central/dnat"
- }
- ],
- "verbose": 1,
- "id": 2,
- "session": "++7z161rod0cGMaStLUWohDpyUnsyT030tNuLPyVYvIhd0GCLXwp9vCJRKnYV4I0Q\/di1bSL3Wf7o8oNnWu6cw=="
-}'
-```
\ No newline at end of file
diff --git a/documentation/developer-docs/importer/firewall-APIs/illumio/0-API.md b/documentation/developer-docs/importer/firewall-APIs/illumio/0-API.md
deleted file mode 100644
index cdd2370aee..0000000000
--- a/documentation/developer-docs/importer/firewall-APIs/illumio/0-API.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# API documentation
-
-see
-
- The $KEY and $TOKEN constants in the code examples represent authentication credentials (session and token, API key and token, or username and password as appropriate).
-
-This is confusing!
-
-Detailed doc:
diff --git a/documentation/developer-docs/importer/firewall-APIs/illumio/1-install-pce.md b/documentation/developer-docs/importer/firewall-APIs/illumio/1-install-pce.md
deleted file mode 100644
index 413baebfe7..0000000000
--- a/documentation/developer-docs/importer/firewall-APIs/illumio/1-install-pce.md
+++ /dev/null
@@ -1,198 +0,0 @@
-# installing on-prem system
-
-install guide:
-
-## download software
-- login to illumio support portal and navigate to:
-- download core & ui rpms and follow instructions on
-
-```console
- /opt/illumio-pce/illumio-pce-env setup --generate-cert
-
- Illumio PCE Runtime Setup (new configuration)
-
- # The Fully Qualified Domain Name (FQDN) of this PCE.
- #
- # For example, pce.mycompany.com
-
- pce_fqdn: illumio.cactus.de
-
- # Optional message to display to users on the login page to the PCE web console.
- #
- # Leave blank if you do not want to configure a login banner.
-
- login_banner: cactus test system
-
- # The hostname or IP address of the first node of the PCE cluster.
-
- service_discovery_fqdn: illumio.cactus.de # auto
-
- # The public IP addresses associated with the PCE FQDN.
- #
- # The PCE will automatically program firewall rules with these addresses
- # on VENs to allow outbound connections to the PCE.
- #
- # If cluster load balancing is performed by an L4 load balancer,
- # then these addresses will be the VIP(s) of the virtual servers.
-
- cluster_public_ips:
- cluster_fqdn:
- - 10.0.2.15 # auto
-
- # The node type for this PCE node.
- #
- # Supported values:
- # core
- # data0
- # data1
- # citus_coordinator
- # citus_worker
- # snc0
-
- node_type: snc0
-
- datacenter: dc1 # default
-
- # The port used for HTTPS connections to the PCE.
- #
- # If cluster load balancing is performed by an L4 load balancer,
- # then the load balancer must be configured to forward this port.
-
- front_end_https_port: 8443 # default
-
- # The port used for the PCE web console and API.
- #
- # If this parameter is not defined (left empty), the PCE will use the
- # port defined for the 'front_end_https_port' parameter.
- #
- # Illumio security best practice is to configure a separate HTTPS
- # port for the PCE web console and API.
-
- front_end_management_https_port:
-
- # The port used for persistent connections between VENs and the PCE
- # event service.
- #
- # If cluster load balancing is performed by an L4 load balancer,
- # then the load balancer must be configured to forward this port
- # and its idle connection timeout must be set to 20 minutes.
-
- front_end_event_service_port: 8444 # default
-
- # The full path to the RSA private key that matches the public certificate.
- #
- # The private key must be PEM encoded in PKCS#5 format without a password.
-
- web_service_private_key: /var/lib/illumio-pce/cert/server.key
-
- # The full path to a X.509 public certificate used for TLS.
- #
- # The certificate must match the PCE FQDN.
- # The certificate must support both Server and Client authentication.
- # The file must contain the server certificate (first) and all
- # intermediate CAs needed to establish the chain of trust.
- # The certificates must be PEM encoded.
-
- web_service_certificate: /var/lib/illumio-pce/cert/server.crt
-
- # The full path to the trusted root certificate bundle.
-
- trusted_ca_bundle: /etc/ssl/certs/ca-bundle.crt # default
-
- # The source email address used when sending email from the PCE.
-
- email_address: illumio@cactus.de
-
- # The display name used when sending email from the PCE.
-
- email_display_name: noreply # default
-
- # A 16 byte key used to encrypt service discovery traffic.
- #
- # The key must be base64 encoded and must be the same on all nodes in
- # the cluster.
-
- service_discovery_encryption_key: ******** # auto
-
- # The SMTP relay used by the PCE to send email; for example, to send
- # invitations and notifications.
- #
- # If no port is specified, the default of 587 is used.
-
- smtp_relay_address: 127.0.0.1:587 # default
-
- # Specifies the root directory to use for data storage. Defaults to persistent_data_root if not defined.
-
-
- # Specifies the root directory to use for data storage. Defaults to persistent_data_root/traffic_datastore if not defined.
-
-
- # The output format for audit and traffic events written to syslog.
- #
- # Supported values:
- # json
- # cef
- # leef
-
- syslog_event_export_format: json # default
-
- # Allow weaker ciphers, include CBC, for older clients. WARNING: The use of this feature decreases the security of the system, and should only be used with full understanding of the ramifications.
-
- insecure_tls_weak_ciphers_enabled: true # default
-
-Wrote configuration /etc/illumio-pce/runtime_env.yml
-```
-
-Check config
-```console
-[root@illumio ~]# sudo -u ilo-pce illumio-pce-env check
-Checking PCE runtime environment.
-Warning: Found 1 warning in PCE runtime environment
- 1: Maximum possible log usage (8.42G) could eventually exceed 25% of partition size (5.15G).
-OK
-[root@illumio ~]#
-```
-
-setup up aliases:
-
- alias ctl='sudo -u ilo-pce /opt/illumio-pce/illumio-pce-ctl'
- alias ctldb='sudo -u ilo-pce /opt/illumio-pce/illumio-pce-db-management'
- alias ctlenv='sudo -u ilo-pce /opt/illumio-pce/illumio-pce-env'
-
-Start service:
-
-```console
-[tim@illumio ~]$ sudo -u ilo-pce /usr/bin/illumio-pce-ctl start --runlevel 1
-Reading /etc/illumio-pce/runtime_env.yml.
-Checking PCE runtime environment.
-Warning: Found 1 warning in PCE runtime environment
- 1: Maximum possible log usage (8.42G) could eventually exceed 25% of partition size (5.15G).
-OK
-Starting Illumio Runtime STARTING 6.06s
-[tim@illumio ~]$
-
-
-[tim@illumio ~]$ sudo -u ilo-pce /usr/bin/illumio-pce-ctl status -svw
-Checking Illumio Runtime
-...
-Illumio Runtime System RUNNING [1] 2.33s
-[tim@illumio ~]$
-```
-Start master backend and ui
-
-```console
-ctldb setup
-ctl set-runlevel 5
-ctl status -svw
-ctl cluster-status
-```
-
-Create UI user:
-
- ctldb create-domain --user-name tmp@cactus.de --full-name 'tim' --org-name 'Cactus'
-
-Create API user:
-
- ctldb create-domain --user-name apiuser@cactus.de --full-name 'api'
-
-
diff --git a/documentation/developer-docs/importer/firewall-APIs/illumio/2-pair-workloads.md b/documentation/developer-docs/importer/firewall-APIs/illumio/2-pair-workloads.md
deleted file mode 100644
index 4ea7fa95ca..0000000000
--- a/documentation/developer-docs/importer/firewall-APIs/illumio/2-pair-workloads.md
+++ /dev/null
@@ -1,8 +0,0 @@
-# Pair workloads
-
-To use the API, you will need an API key. Which is generated from the PCE’s UI - User - My API keys - Create new key
-
- Username: api_1fbeb4416acd509bc
- Secret: ba9f6db768a875aedd6a6c6bba4fc86c023cd1710698079fb676fe9ffe297221
-
-- workload is a tool that uses the API: doc:
diff --git a/documentation/developer-docs/importer/firewall-APIs/openstack/openstack.md b/documentation/developer-docs/importer/firewall-APIs/openstack/openstack.md
deleted file mode 100644
index 9575537b09..0000000000
--- a/documentation/developer-docs/importer/firewall-APIs/openstack/openstack.md
+++ /dev/null
@@ -1,239 +0,0 @@
-# OpenStack API
-
-Start here:
-
-## online sandbox
-
-https://www.openstack.org/passport/
-
-
-## openstack test installation
-
-on Ubuntu 20.04 (4 GB RAM is not enough, 8 GB seems ok, better 10 GB).
-
-Make sure simplejson ist not installed:
-
- sudo apt purge python3-simplejson
-
-Prepare system:
-
- sudo useradd -s /bin/bash -d /opt/stack -m stack
- echo "stack ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/stack
- sudo -u stack -i
- git clone https://opendev.org/openstack/devstack
- cd devstack
-
-create local.conf file with the following content (choosing a suitable password for XXX):
-
- [[local|localrc]]
- ADMIN_PASSWORD=XXX
- DATABASE_PASSWORD=$ADMIN_PASSWORD
- RABBIT_PASSWORD=$ADMIN_PASSWORD
- SERVICE_PASSWORD=$ADMIN_PASSWORD
-
-
-To avoid error
-
- Found existing installation: simplejson 3.16.0
- ERROR: Cannot uninstall 'simplejson'. It is a distutils installed project and thus we cannot accurately determine which files belong to it which would lead to only a partial uninstall.
-
-in inc/python CHANGE FROM
-
- $cmd_pip $upgrade \
-
-TO
-
- $cmd_pip $upgrade --ignore-installed \
-
-Then run the installation
-
- ./stack.sh
-
-
-## accessing the API
-
-see
-
-
-### user setup
-
-### login
-```console
-curl -i \
- -H "Content-Type: application/json" \
- -d '
-{ "auth": {
- "identity": {
- "methods": ["password"],
- "password": {
- "user": {
- "name": "admin",
- "domain": { "id": "default" },
- "password": "adminpwd"
- }
- }
- }
- }
-}' \
- "http://localhost:5000/v3/auth/tokens" ;
-```
-gives
-```json
-{
- "totalCount": "1",
- "imdata": [
- {
- "aaaLogin": {
- "attributes": {
- "token": "xxx",
- "siteFingerprint": "dlcoaz3nzznwlp4f3icvj69l11fp0mys",
- "userName": "admin",
- "version": "5.2(1g)",
- }
- }
- }
- ]
-}
-```
-
-## logout
-From the documentation at :
-
- At this time, the aaaLogout method returns a response but does not end a session. Your session ends after a refresh timeout when you stop sending aaaRefresh messages.
-
-## get various config parts (classes)
-
-All of the below classes "CLASS" can be retrieved as follows:
-```console
-curl --request GET \
- --url https://sandboxapicdc.cisco.com/api/class/CLASS.json \
- --header 'Content-Type: application/json' \
- --cookie APIC-cookie=xxx \
-```
-
-### Basic config
-- fvBD - bridge domains
-- aaaDomain - Domains
-- fvEPg - End Point Groups (EPGs)
-- fvAEPg - Application endpoint groups
-- aaaUser - users
-- fvTenant - tenants
-- application profiles
-- vzGraphCont, vzGraphDef - service graphs
-- outside connections - L3Out external EPG (l3extInstP)
-- End Point Security Groups (ESGs)
-
-### Contract information
-- vzFilter - filters?
-- vzACtrct - contracts
-- vzACtrctEpgDef - contracts with EPGs?
-- vzBrCP - Binary Contract Profile
-- vzABrCP - abstraction of Binary Contract Profile
-- vzACollection - ???
-- vzConsDef - Consumer EPg
-- vzProvDef - Provider EPg
-- vzCtrctEPgCont - Contract EPG Container
-- vzCtrctEntityDef - Summary of EPg: Contains All Info to Create ProvDef/ConsDef
-- vzRsDenyRule - deny rule (cleanup)
-- vzRuleOwner gives an exhaustive list of what looks like fw rules
-```json
-{
- "totalCount": "55",
- "imdata": [
- {
- "vzRuleOwner": {
- "attributes": {
- "action": "permit",
- "childAction": "",
- "creatorDn": "topology/pod-1/node-101/sys/ctx-[vxlan-16777200]",
- "ctrctName": "",
- "direction": "uni-dir",
- "dn": "topology/pod-1/node-101/sys/actrl/scope-16777200/rule-16777200-s-any-d-any-f-implarp/own-[topology/pod-1/node-101/sys/ctx-[vxlan-16777200]]-tag-vrf",
- "intent": "install",
- "lcOwn": "local",
- "markDscp": "unspecified",
- "modTs": "2021-09-20T14:07:18.567+00:00",
- "monitorDn": "uni/tn-common/monepg-default",
- "name": "",
- "nameAlias": "",
- "prio": "any_any_filter",
- "qosGrp": "unspecified",
- "status": "",
- "tag": "vrf",
- "type": "tenant"
- }
- }
- },
-```
-- vzToEPg
-```json
-{
- "totalCount": "12",
- "imdata": [
- {
- "vzToEPg": {
- "attributes": {
- "bdDefDn": "uni/bd-[uni/tn-Tenant_ISK/BD-BD_ISK]-isSvc-no",
- "bdDefStQual": "none",
- "bgpDomainId": "10671",
- "childAction": "",
- "ctxDefDn": "uni/ctx-[uni/tn-Tenant_ISK/ctx-VRF_ISK]",
- "ctxDefStQual": "none",
- "ctxPcTag": "49153",
- "ctxSeg": "2883584",
- "descr": "",
- "dn": "cdef-[uni/tn-common/brc-app_to_web]/epgCont-[uni/tn-Tenant_ISK/ap-AP_ISK/epg-web]/fr-[uni/tn-common/brc-app_to_web/dirass/prov-[uni/tn-Tenant_ISK/ap-AP_ISK/epg-web]-any-no]/to-[uni/tn-common/brc-app_to_web/dirass/cons-[uni/tn-Tenant_ISK/ap-AP_ISK/epg-app]-any-no]",
- "epgDefDn": "uni/tn-common/brc-app_to_web/dirass/cons-[uni/tn-Tenant_ISK/ap-AP_ISK/epg-app]-any-no",
- "epgDn": "uni/tn-Tenant_ISK/ap-AP_ISK/epg-app",
- "intent": "install",
- "isCtxPcTagInUseForRules": "no",
- "isGraph": "no",
- "l3CtxEncap": "unknown",
- "lcOwn": "local",
- "modTs": "2021-09-20T14:21:02.498+00:00",
- "monPolDn": "",
- "name": "",
- "nameAlias": "",
- "ownerKey": "",
- "ownerTag": "",
- "pcTag": "16387",
- "prefGrMemb": "exclude",
- "scopeId": "2883584",
- "servicePcTag": "any",
- "status": "",
- "txId": "17870283321406128551"
- }
- }
- },
-```
-
-## get change notifications
-
-- vzReeval?
-
-see
-
-Subscribing to Query Results - can be used to be informated about any changes. This needs a websocket connection.
-
-## get a list of fw rules
-
-## get nat rules
-
-
-## Example ansible task to add an EPG
-
-```ansible
- - name: Add static EPG
- aci_rest:
- host: "{{host}}"
- username: "{{username}}"
- password: "{{password}}"
- method: post
- path: /api/node/mo/uni/tn-{{item.tenant}}/ap-{{item.approfile}}/epg-{{item.epgname}}.json
- content: "{{ lookup('template', 'templates/epg1.j2') }}"
- validate_certs: no
- with_items: '{{ ports }}'
- ignore_errors: yes
- loop_control:
- pause: 1
-```
\ No newline at end of file
diff --git a/documentation/developer-docs/importer/firewall-APIs/paloAlto/paloAltoApi.md b/documentation/developer-docs/importer/firewall-APIs/paloAlto/paloAltoApi.md
deleted file mode 100644
index c257c99faa..0000000000
--- a/documentation/developer-docs/importer/firewall-APIs/paloAlto/paloAltoApi.md
+++ /dev/null
@@ -1,556 +0,0 @@
-# Integrating Palo Alto firewall
-
-All examples here are given for PanOS 9.0. PAN-OS has two APIs: XML and REST. There is also an extra REST API for the central management server Panorama. The XML-API can be used to get the whole config in one go.
-
-## Create api user
-see
-
-## login
-```console
-curl --insecure --request GET --url 'https://PAN-IP/api/?type=keygen&user=fwo&password=xxx'
-```
-gets us a session key in XML format which seems to be valid indefinetly?!:
-```xml
-
-
- LUFRPT1Tb2xDZnk0R25WbDJONWJNMmlEMHNpS0Y2d1U9T3ZLZFhydER6SDZKYk9OQit2cmVTVUtEb2MyMVBDUkdBOGY3UzlDS0VrTT0=
-
-
-```
-
-More secure:
-see , but note: You cannot use basic authentication when you Get Your API Key.
-
-
-## get API version
-
-`curl -X GET "https:///api/?type=version&key="'
-
-## Get all network objects
-The session key can be used to get objects as follows (for single fw, the name of the vsys seems to be vsys1):
-```console
-curl --insecure --request GET \
- --url 'https://PAN-IP/restapi/v9.1/Objects/Addresses?location=vsys&vsys=vsys1' \
- --header 'X-PAN-KEY: LUFRPT1JdHF6SnVndXNEU2VxVFIvNnZ1bG1yeFk0S2c9clVWeGhkdnNQNTBRK1BzNXBCeEMvNzdTSks1NWVDdzJLSmZXa1JsUkYzdW9OUnJSb1pDREdseitlVUtNc1VKSw=='
-```
-Gives us the network objects in JSON format:
-
-```json
-{
- "@status": "success",
- "@code": "19",
- "result": {
- "@total-count": "45",
- "@count": "45",
- "entry": [
- {
- "@name": "ext-interface-ip-10.9.8.2",
- "@location": "vsys",
- "@vsys": "vsys1",
- "ip-netmask": "10.9.8.2"
- },
- {
- "@name": "cactus-da",
- "@location": "vsys",
- "@vsys": "vsys1",
- "ip-netmask": "85.182.155.96\/27",
- "tag": {
- "member": [
- "cactus-DA"
- ]
- }
- },
- {
- "@name": "fb_inet_10.9.8.1",
- "@location": "vsys",
- "@vsys": "vsys1",
- "ip-netmask": "10.9.8.1"
- },
- {
- "@name": "gware.cactus.de_85.182.155.108",
- "@location": "vsys",
- "@vsys": "vsys1",
- "ip-netmask": "85.182.155.108\/32",
- "tag": {
- "member": [
- "cactus-DA"
- ]
- }
- }
- ]
- }
-}
-```
-To get address groups:
-
-```console
-curl --request GET \
- --url 'https://10.8.6.3/restapi/v9.1/Objects/AddressGroups?location=vsys&vsys=vsys1' \
- --header 'X-PAN-KEY: LUFRPT1zUmdXTlZjUFZPaWxmc0R2eHRPa1FvdmtlV009T3ZLZFhydER6SDZKYk9OQit2cmVTZHNYWDJrdHREWDVyN1VnZG01VXNKWT0=' \
-```
-
-Retrieves tag-based filters:
-```json
-{
- "@status": "success",
- "@code": "19",
- "result": {
- "@total-count": "3",
- "@count": "3",
- "entry": [
- {
- "@name": "GRP_tims-ip-adressen",
- "@location": "vsys",
- "@vsys": "vsys1",
- "dynamic": {
- "filter": "'tims-clients'"
- }
- },
- {
- "@name": "GRP_guest-ips",
- "@location": "vsys",
- "@vsys": "vsys1",
- "dynamic": {
- "filter": "'guests' "
- }
- },
- {
- "@name": "GRP_kids_ips",
- "@location": "vsys",
- "@vsys": "vsys1",
- "dynamic": {
- "filter": "'kids-ips'"
- }
- }
- ]
- }
-}
-```
-## get service objects
-
-first predefined services:
-
-```console
-curl --request GET \
- --url 'https://10.8.6.3/restapi/v9.1/Objects/Services?location=predefined' \
- --header 'X-PAN-KEY: LUFRPT1JdHF6SnVndXNEU2VxVFIvNnZ1bG1yeFk0S2c9clVWeGhkdnNQNTBRK1BzNXBCeEMvNzdTSks1NWVDdzJLSmZXa1JsUkYzdW9OUnJSb1pDREdseitlVUtNc1VKSw==' \
-```
-
-yields:
-
-```json
-{
- "@status": "success",
- "@code": "19",
- "result": {
- "@total-count": "2",
- "@count": "2",
- "entry": [
- {
- "@name": "service-http",
- "@location": "predefined",
- "protocol": {
- "tcp": {
- "port": "80,8080"
- }
- }
- },
- {
- "@name": "service-https",
- "@location": "predefined",
- "protocol": {
- "tcp": {
- "port": "443"
- }
- }
- }
- ]
- }
-}
-```
-
-Then self-defined:
-
-```console
-curl --insecure --request GET \
- --url 'https://PAN-IP/restapi/v9.1/Objects/Services?location=vsys&vsys=vsys1' \
- --header 'X-PAN-KEY: LUFRPT1JdHF6SnVndXNEU2VxVFIvNnZ1bG1yeFk0S2c9clVWeGhkdnNQNTBRK1BzNXBCeEMvNzdTSks1NWVDdzJLSmZXa1JsUkYzdW9OUnJSb1pDREdseitlVUtNc1VKSw=='
-```
-
-give us:
-
-```json
-{
- "@status": "success",
- "@code": "19",
- "result": {
- "@total-count": "42",
- "@count": "42",
- "entry": [
- {
- "@name": "tcp_64285",
- "@location": "vsys",
- "@vsys": "vsys1",
- "protocol": {
- "tcp": {
- "port": "64285",
- "override": {
- "no": []
- }
- }
- }
- },
- {
- "@name": "steam_27000-27100",
- "@location": "vsys",
- "@vsys": "vsys1",
- "protocol": {
- "udp": {
- "port": "27000-27100",
- "override": {
- "no": []
- }
- }
- }
- },
- {
- "@name": "svc_3000_tcp_hbci",
- "@location": "vsys",
- "@vsys": "vsys1",
- "protocol": {
- "tcp": {
- "port": "3000",
- "override": {
- "no": []
- }
- }
- }
- },
- {
- "@name": "fritzbox_tcp_14013",
- "@location": "vsys",
- "@vsys": "vsys1",
- "protocol": {
- "tcp": {
- "port": "14013",
- "override": {
- "no": []
- }
- }
- }
- }
- ]
- }
-}
-```
-
-
-## get (predefined) applications
-
-in order to get the application names we need API v9.1!
-
-with version 9.0:
-```console
-curl --insecure --request GET \
- --url 'https://10.8.6.3/restapi/9.0/Objects/Applications?location=predefined' \
- --header 'X-PAN-KEY: LUFRPT1zUmdXTlZjUFZPaWxmc0R2eHRPa1FvdmtlV009T3ZLZFhydER6SDZKYk9OQit2cmVTZHNYWDJrdHREWDVyN1VnZG01VXNKWT0=' \
-```
-```json
-{
- "@status": "success",
- "@code": "19",
- "result": {
- "@total-count": "3566",
- "@count": "3566",
- "entry": [
- {
- "default": {
- "port": {
- "member": [
- "tcp\/3468,6346,11300"
- ]
- }
- },
- "category": "general-internet",
- "subcategory": "file-sharing",
- "technology": "peer-to-peer",
- "risk": "5",
- "evasive-behavior": "yes",
- "consume-big-bandwidth": "yes",
- "used-by-malware": "yes",
- "able-to-transfer-file": "yes",
- "has-known-vulnerability": "yes",
- "tunnel-other-application": "no",
- "prone-to-misuse": "yes",
- "pervasive-use": "yes"
- }
- ]
- }
-}
-```
-With v9.1:
-
-```console
-curl --request GET \
- --url 'https://10.8.6.3/restapi/v9.1/Objects/Applications?location=predefined' \
- --header 'X-PAN-KEY: LUFRPT1zUmdXTlZjUFZPaWxmc0R2eHRPa1FvdmtlV009T3ZLZFhydER6SDZKYk9OQit2cmVTZHNYWDJrdHREWDVyN1VnZG01VXNKWT0='
-```
-
-```json
-{
- "@status": "success",
- "@code": "19",
- "result": {
- "@total-count": "3566",
- "@count": "3566",
- "entry": [
- {
- "@name": "100bao",
- "@location": "predefined",
- "default": {
- "port": {
- "member": [
- "tcp\/3468,6346,11300"
- ]
- }
- },
- "category": "general-internet",
- "subcategory": "file-sharing",
- "technology": "peer-to-peer",
- "risk": "5",
- "evasive-behavior": "yes",
- "consume-big-bandwidth": "yes",
- "used-by-malware": "yes",
- "able-to-transfer-file": "yes",
- "has-known-vulnerability": "yes",
- "tunnel-other-application": "no",
- "prone-to-misuse": "yes",
- "pervasive-use": "yes"
- },
- {
- "@name": "open-vpn",
- "@location": "predefined",
- "default": {
- "port": {
- "member": [
- "tcp\/1194",
- "tcp\/443",
- "udp\/1194"
- ]
- }
- },
- "category": "networking",
- "subcategory": "encrypted-tunnel",
- "technology": "client-server",
- "timeout": "3600",
- "risk": "3",
- "evasive-behavior": "no",
- "consume-big-bandwidth": "no",
- "used-by-malware": "no",
- "able-to-transfer-file": "yes",
- "has-known-vulnerability": "yes",
- "tunnel-other-application": "yes",
- "tunnel-applications": {
- "member": [
- "cyberghost-vpn",
- "frozenway",
- "hotspot-shield",
- "ipvanish",
- "spotflux"
- ]
- },
- "prone-to-misuse": "no",
- "pervasive-use": "yes"
- }
- ]
- }
-}
-```
-
-## get rules
-
-```console
-curl --insecure --request GET \
- --url 'https://PAN-IP/restapi/v9.1/Policies/SecurityRules?location=vsys&vsys=vsys1' \
- --header 'X-PAN-KEY: LUFRPT1JdHF6SnVndXNEU2VxVFIvNnZ1bG1yeFk0S2c9clVWeGhkdnNQNTBRK1BzNXBCeEMvNzdTSks1NWVDdzJLSmZXa1JsUkYzdW9OUnJSb1pDREdseitlVUtNc1VKSw==' \
-```
-
-gives us:
-
-```json
-{
- "@status": "success",
- "@code": "19",
- "result": {
- "@total-count": "85",
- "@count": "85",
- "entry": [
- {
- "@name": "special access tim-1",
- "@uuid": "ca58af60-05c3-4806-b7c6-aea7a1ddc70c",
- "@location": "vsys",
- "@vsys": "vsys1",
- "to": {
- "member": [
- "untrust"
- ]
- },
- "from": {
- "member": [
- "trust"
- ]
- },
- "source": {
- "member": [
- "GRP_tims-ip-adressen"
- ]
- },
- "destination": {
- "member": [
- "any"
- ]
- },
- "source-user": {
- "member": [
- "any"
- ]
- },
- "application": {
- "member": [
- "open-vpn",
- "ssh",
- "ssh-tunnel",
- "ssl",
- "web-browsing"
- ]
- },
- "service": {
- "member": [
- "any"
- ]
- },
- "hip-profiles": {
- "member": [
- "any"
- ]
- },
- "action": "allow",
- "rule-type": "interzone",
- "profile-setting": {
- "profiles": {
- "url-filtering": {
- "member": [
- "default"
- ]
- },
- "file-blocking": {
- "member": [
- "strict file blocking"
- ]
- },
- "virus": {
- "member": [
- "test-av-profile"
- ]
- },
- "spyware": {
- "member": [
- "strict"
- ]
- },
- "vulnerability": {
- "member": [
- "default"
- ]
- },
- "wildfire-analysis": {
- "member": [
- "default"
- ]
- }
- }
- },
- "tag": {
- "member": [
- "tims-clients"
- ]
- },
- "log-start": "yes",
- "category": {
- "member": [
- "any"
- ]
- },
- "disabled": "no",
- "log-setting": "forwarder-traffic-log",
- "group-tag": "tims-clients"
- },
- {
- "@name": "DMZ minecraft",
- "@uuid": "ac91834b-0ac3-4d9a-abcd-3ad69075bed7",
- "@location": "vsys",
- "@vsys": "vsys1",
- "to": {
- "member": [
- "any"
- ]
- },
- "from": {
- "member": [
- "untrust"
- ]
- },
- "source": {
- "member": [
- "any"
- ]
- },
- "destination": {
- "member": [
- "ext-interface-ip-10.9.8.2"
- ]
- },
- "source-user": {
- "member": [
- "any"
- ]
- },
- "category": {
- "member": [
- "any"
- ]
- },
- "application": {
- "member": [
- "any"
- ]
- },
- "service": {
- "member": [
- "tcp_60999"
- ]
- },
- "hip-profiles": {
- "member": [
- "any"
- ]
- },
- "action": "allow",
- "log-start": "yes",
- "rule-type": "universal",
- "log-setting": "forwarder-traffic-log",
- "profile-setting": {
- "profiles": {
- "vulnerability": {
- "member": [
- "strict"
- ]
- }
- }
- },
- "disabled": "no"
- }
- ]
- }
-}
-```
\ No newline at end of file
diff --git a/documentation/developer-docs/importer/legacy-importer-csv-interface.md b/documentation/developer-docs/importer/legacy-importer-csv-interface.md
deleted file mode 100644
index e81adcc70f..0000000000
--- a/documentation/developer-docs/importer/legacy-importer-csv-interface.md
+++ /dev/null
@@ -1,139 +0,0 @@
-# CSV interface of importer
-
-## adjusting parser interface
-
-All data is currently exchanged via a CSV interface.
-
-### perl-based (parse-config file) import modules (old)
-If a field needs to be added to the CSV interface, for all old perl-based parsers
-you need to add the name of the field to the respective _outlist in roles/importer/files/importer/CACTUS/FWORCH/import.pm, e.g.
- @rule_outlist =(qw ( rule_id disabled src.op src src.refs dst.op dst dst.refs services.op services services.refs
- action track install time comments name UID header_text src.zone dst.zone last_change_admin parent_rule_uid));
-
-### python-based (API access) import modules (new)
-
-For python based importers, you need to adjust the roles/importer/files/importer/checkpointR8x/*_parser.py files, e.g.
- csv_dump_svc_obj.py in parse_service.py
-
-## General
-
-```console
-- boolean fields (negated, disabled) can contain either 0/1 or true/false?
-- directory:
-- import arrays/fields are defined in CACTUS/FWORCH/import.pm:
- our @obj_import_fields
- our @svc_import_fields
- our @user_import_fields
- our @zone_import_fields
- our @rule_import_fields
- our @auditlog_import_fields
-```
-
-## import_ tables
-
-### rule.csv
-
-```console
-name: _rulebase.csv
-fields (total=26):
- control_id - bigint - id of currently running import (identical for all rules of an import)
- rule_num - integer - number of the rule relative to the current import - used for sorting rules within this import
- rulebase_name - string - name of the rulebase (used for matching rule to gateway)
- rule_ruleid - string - id (unique within gateway, but not globally)
- rule_disabled - boolean - is the rule disabled (inactive)?
- rule_src_neg - boolean - is the source of the rule negated?
- rule_src - string of CSVs with source objects of the rule (including users in format "user[-group]@object")
- rule_src_refs - string of CSVs with UIDs of source objects of the rule (including users in format "user[-group]-uid@object-uid")
- rule_dst_neg - boolean - is the destination of the rule negated?
- rule_dst - string of CSVs with destination objects of the rule
- rule_dst_refs - string of CSVs with UIDs of destination objects of the rule
- rule_svc_neg - boolean - is the service of the rule negated?
- rule_svc
- rule_svc_refs
- rule_action
- rule_track
- rule_installon
- rule_time
- rule_comment
- rule_name
- rule_uid
- rule_head_text
- rule_from_zone
- rule_to_zone
- last_change_admin - string containing name of the last admin having changed this rule (optional, checkpoint only)
- parent_rule_uid - string - UID of a rule this rule belongs to (either layer, domain rules or section)
-```
-
-### network_objects.csv
-
-```console
-name: _netzobjekte.csv
-fields (total=15):
- control_id
- obj_name
- obj_typ
- obj_member_names
- obj_member_refs
- obj_sw
- obj_ip
- obj_ip_end
- obj_color
- obj_comment
- obj_location
- obj_zone
- obj_uid
- last_change_admin
- last_change_time
-```
-
-### services.csv
-
-```console
-name: _services.csv
-fields (total=19):
- control_id
- svc_name
- svc_typ
- svc_prod_specific --> note: this seems to be just a redundant copy of svc_typ!
- svc_member_names
- svc_member_refs
- svc_color
- ip_proto
- svc_port
- svc_port_end
- svc_source_port
- svc_source_port_end
- svc_comment
- rpc_nr
- svc_timeout_std
- svc_timeout
- svc_uid
- last_change_admin
- last_change_time
-```
-
-### users.csv
-
-```console
-name: _users.csv
-fields (total=10):
- control_id
- user_name
- user_typ
- user_member_names
- user_member_refs
- user_color
- user_comment
- user_uid
- user_valid_until
- last_change_admin
-```
-
-### zones.csv
-
-```console
-name: _zones.csv
-fields (total=2):
- control_id
- zone_name
-```
diff --git a/documentation/developer-docs/importer/readme.md b/documentation/developer-docs/importer/readme.md
deleted file mode 100644
index 0097a1b9ce..0000000000
--- a/documentation/developer-docs/importer/readme.md
+++ /dev/null
@@ -1,77 +0,0 @@
-# Importing firewall configs via API
-
-## Code structure and entry point
-
-We use the FWO API to write the whole firewall config as JSON into the import_config table. The format is described below.
-The main import script is [/roles/importer/import_mgm.py](/roles/importer/import_mgm.py).
-It does not need to be changed when adding new import modules.
-Within this file we have the following calls which deal with firewall product specific stuff:
-```python
- # import product-specific importer module:
-fw_module = importlib.import_module("." + fw_module_name, pkg_name)
-
-# get config from FW API and write it into config2import if it has changed:
-
- config_changed_since_last_import = fw_module.has_config_changed(mgm_details, debug_level=debug_level, ssl_verification=ssl, force=force)
-
- if config_changed_since_last_import:
- get_config_response = fw_module.get_config(
- config2import, full_config_json, current_import_id, mgm_details, debug_level=debug_level,
- ssl_verification=ssl, limit=limit, force=force, jwt=jwt)
-
-# now we import config2import via the FWO API:
-error_count += fwo_api.import_json_config(fwo_api_base_url, jwt, args.mgm_id, {
- "importId": current_import_id, "mgmId": args.mgm_id, "config": config2import})
-```
-The above describes the new 2021 import method. In addition we also have some legacy import modules.
-Regarding legacy import modules see [/documentation/developer-docs/importer](/documentation/developer-docs/importer/legacy-importer-csv-interface.md).
-For an overview see next section.
-
-### Import module status
-- CPR8x - Currently CPR8x can be importer both via legacy (perl with CSV) and python importer (API2API)
-- FortiManager - can be importer using the new API-based method
-- FortiGate - can be importer via old legacy method (ssh-based)
-- Barracuda - can be importer via old legacy method (ssh-based)
-- Juniper JunOS - can be importer via old legacy method (ssh-based)
-- Juniper ScreenOS - can be importer via old legacy method (ssh-based)
-- Cisco ASA/Pix - can be importer via old legacy method (ssh-based) - work in progress - needs to be tested first.
-
-## Adding a new import module
-
-- It is highly recommend to stop the import process before making any changes (don't forget to restart it after successful integration):
-```bash
- sudo systemctl stop fworch-importer
-```
-- create a new firewall type by adding it to table stm_dev_type. To make this change permanent, this has to be added to [/roles/database/files/sql/creation/fworch-fill-stm.sql](/roles/database/files/sql/creation/fworch-fill-stm.sql) as follows:
-```sql
-insert into stm_dev_typ (dev_typ_name,dev_typ_version,dev_typ_manufacturer) VALUES ('','','');
-```
-- Note that the version should be short and open ended, e.g. "7ff". If there is a major breaking change, a new version, e.g. 10ff will have to be created.
-- For a smooth upgrade path for existing installations, a new FWO version needs to be created and sql statement above also needs to be added to the upgrade script. For upgrade information see (documentation/developer-docs/installer/upgrading.md).
-- Create a sub-directory beneath /usr/local/fworch/importer/ called "dev_typ_name" + "dev_typ_version"
-- Within this directory there has be a module called 'fwcommon.py' containing a function get_config using the parameters above
-- The config needs to be returned in the config2import variable as a json dict using the syntax described in [/documentation/developer-docs/importer/FWO-import-api.md](/documentation/developer-docs/importer/FWO-import-api.md)
-- For testing the new import module, you need to add a management and a device via the UI (see help section for details on this).
-
-### Data handling rules
-- If there are no (e.g. user or zone) objects of a kind in the config, the respective *_objects arrays can simply be ommited.
-- Group delimiter is the pipe (|).
-- All non-integer values need to be enclosed in quotes (").
-- All values need to be sanitized, removing quotes (single and double) as well as replacing line breaks with space chars.
-
-## Testing & Tools
-
-We recommend using a tool like insomnia for testing API stuff.
-
-## Debugging python
-
-importer files end up in different directories during installation process (not the same as in the source/installer code). For debugging use something like:
-
-```bash
-sudo ln -s /home/tim/dev/tpur-fwo-june/firewall-orchestrator/roles/importer/files/importer /usr/local/fworch/importer
-```
-or the following in vscode
-
-```console
-sys.path.append(r"/home/tim/dev/tpur-fwo-june/firewall-orchestrator/roles/importer/files/importer")
-```
diff --git a/documentation/importer/readme.md b/documentation/importer/readme.md
index da60c9873c..6a2e9715b5 100644
--- a/documentation/importer/readme.md
+++ b/documentation/importer/readme.md
@@ -50,3 +50,37 @@ optional arguments:
if set, the config will not be fetched from firewall but read from normalized json config file specified here; may also be an url.
user@test:~$
```
+
+
+### Lint Setup
+
+ruff und pre-commit sind in den requirements
+
+```
+pip install -r .\roles\importer\files\importer\requirements.txt
+```
+
+```
+pre-commit install
+```
+
+### Usage
+
+Ruff lint check
+
+```
+ruff check
+```
+
+Ruff lint check and auto fix
+
+```
+ruff check --fix
+```
+
+
+Ruff format ( only needed for VS, but also run pre-commit)
+
+```
+ruff format
+```
\ No newline at end of file
diff --git a/documentation/installer/basic-installation.md b/documentation/installer/basic-installation.md
index 8e4c4766b4..e1a84b2596 100644
--- a/documentation/installer/basic-installation.md
+++ b/documentation/installer/basic-installation.md
@@ -74,9 +74,9 @@ ok: [install-srv] => {
PLAY RECAP *********************************************************************
install-srv : ok=302 changed=171 unreachable=0 failed=0 skipped=127 rescued=0 ignored=0
```
-Simply navigate to and login with user 'admin' and the UI admin password.
+Simply navigate to and login with user 'admin' and the UI admin password displayed by the install script (see above).
-The api hasura admin secret can be used to access the API at .
+The api hasura admin secret can be used to access the API at .
If using the python venv method, you may now exit venv with:
diff --git a/documentation/installer/install-advanced.md b/documentation/installer/install-advanced.md
index a93dd13fff..376b25b801 100644
--- a/documentation/installer/install-advanced.md
+++ b/documentation/installer/install-advanced.md
@@ -75,7 +75,7 @@ If you use authentication:
Acquire::http::Proxy "http://user:password@proxy_server:port/";
-Note that the following domains must be reachable through the proxy:
+Note that the following domains (and their sub-domains) must be reachable through the proxy:
cactus.de (only for downloading test data, not needed if run with "--skip-tags test")
ubuntu.com
@@ -96,9 +96,15 @@ Note that the following domains must be reachable through the proxy:
snapcraft.io
snapcraftcontent.com (and sub-domains)
-NB: for vscode-debugging, you also need access to
+For vscode-debugging only - most are needed for downloading extensions
visualstudio.com
+ vsassets.io (and subdomains)
+ digicert.com (and subdomains)
+ dot.net (and subdomains)
+ windows.net (and subdomains)
+ applicationinsights.azure.com (and subdomains)
+ exp-tas.com (and subdomains)
#### Pyhton proxy config
@@ -119,8 +125,9 @@ In case of errors with existing pip config, do not use the script to create the
remove any local pip config and install manually:
rm -f $HOME/.config/pip/pip.conf
- python3 -m venv ansible-venv
- source ansible-venv/bin/activate
+ python3 -m venv installer-venv
+ source installer-venv/bin/activate
+ pip install -r requirements.txt
pip install ansible
### Parameter "api_no_metadata" to prevent meta data import
diff --git a/documentation/interface-descriptions-json/firewall-normalized-config-format.md b/documentation/interface-descriptions-json/firewall-normalized-config-format.md
new file mode 100644
index 0000000000..d3d7cc9cf7
--- /dev/null
+++ b/documentation/interface-descriptions-json/firewall-normalized-config-format.md
@@ -0,0 +1,121 @@
+# Normalized Firewall Config Interface
+
+Firewall configurations are imported through the normalized JSON format shown
+in the two samples inside this directory. Both the single-manager and
+multi-manager examples use the same schema, which is described below.
+
+## Top-Level Envelope
+
+```json
+{
+ "ConfigFormat": "NORMALIZED",
+ "ManagerSet": [ { /* manager */ } ],
+ "native_config": { }
+}
+```
+
+| Field | Type | Required | Description |
+|----------------|----------|----------|-------------|
+| `ConfigFormat` | string | yes | Always `NORMALIZED`. |
+| `ManagerSet` | object[] | yes | Collection of manager definitions. Each manager contains a batch of configuration objects. |
+| `native_config`| object | no | Raw vendor payload (kept empty in the samples). |
+
+## Manager Definition
+
+There may be multiple managers in a single import file which belong to the same super-manager (including the super-manager itself).
+
+| Field | Type | Required | Description |
+|-----------------|----------|----------|-------------|
+| `ManagerUid` | string | yes | Unique identifier for the manager/adom. |
+| `ManagerName` | string | yes | Display name (e.g., `forti-mgr cactus`). |
+| `IsSuperManager`| boolean | yes | `true` if the entry aggregates sub-managers. |
+| `DomainUid` | string | no | Vendor-specific domain identifier; empty if n/a. |
+| `DomainName` | string | no | Human-readable domain name. |
+| `SubManagerIds` | string[] | yes | IDs of delegated managers; empty array if none. |
+| `Configs` | object[] | yes | Configuration batches imported for the manager. |
+
+## Config Batch
+
+There may be multiple configs per manager in the future but at the moment the only supported case is a single config awith action 'INSERT'.
+
+| Field | Type | Required | Description |
+|-----------------|---------|----------|-------------|
+| `ConfigFormat` | string | yes | `NORMALIZED_LEGACY` in the sample payloads. |
+| `action` | string | yes | CRUD indicator (`INSERT`, `UPDATE`, ...). |
+| `network_objects` | object | yes | Map of network objects keyed by UID. |
+| `service_objects` | object | yes | Map of service objects keyed by UID. |
+| `users` | object | yes | User directory entries keyed by UID (empty in samples). |
+| `zone_objects` | object | yes | Map of zone definitions. |
+| `rulebases` | array | yes | Rulebase segments assigned to the manager. |
+| `gateways` | array | yes | Gateways/firewalls with rulebase links. |
+
+### `network_objects` entry
+
+| Field | Type | Description |
+|-------------------|--------|-------------|
+| `obj_uid` | string | Unique ID (matches the map key). |
+| `obj_name` | string | Readable name. |
+| `obj_ip` | string | Start IP or CIDR. |
+| `obj_ip_end` | string | End IP for ranges. |
+| `obj_color` | string | UI color metadata. |
+| `obj_typ` | string | Object type (`network`, `host`, etc.). |
+| `obj_member_refs` | array/string|null | References to nested objects. |
+| `obj_member_names`| array/string|null | Human readable member names. |
+| `obj_comment` | string|null | Optional comment. |
+
+### `service_objects` entry
+
+| Field | Type | Description |
+|-------------------|--------|-------------|
+| `svc_uid` | string | Unique ID (map key). |
+| `svc_name` | string | Display name. |
+| `svc_port` | integer|null | Start port. |
+| `svc_port_end` | integer|null | End port (equals start for single ports). |
+| `svc_color` | string | UI color metadata. |
+| `svc_typ` | string | Service type (`simple`, `group`, ...). |
+| `ip_proto` | integer | L4 protocol number. |
+| `svc_member_refs` | array/string|null | Child object references. |
+| `svc_member_names`| array/string|null | Child names. |
+| `svc_comment` | string|null | Optional comment. |
+| `svc_timeout` | integer|null | Optional timeout. |
+| `rpc_nr` | integer|null | RPC metadata. |
+
+### Rulebase Segment
+
+| Field | Type | Description |
+|-----------------|---------|-------------|
+| `uid` | string | Rulebase identifier (map target for gateway links). |
+| `name` | string | Display name. |
+| `mgm_uid` | string | Owning manager UID. |
+| `is_global` | boolean | Mark global sections. |
+| `Rules` | object | Map of rule objects keyed by `rule_uid`. |
+
+Each rule entry contains metadata such as `rule_src`, `rule_dst`, `rule_svc`,
+action, hit counters, zones, and audit fields (`last_change_admin`, etc.),
+mirroring the properties shown in the samples.
+
+### Gateways
+
+| Field | Type | Description |
+|----------------------|----------|-------------|
+| `Uid` | string | Firewall identifier. |
+| `Name` | string | Friendly name. |
+| `Routing` | array | Route table data (empty in samples). |
+| `Interfaces` | array | Interface metadata (empty in samples). |
+| `RulebaseLinks` | object[] | Links that bind the gateway to rulebases. |
+| `GlobalPolicyUid` | string | Optional reference. |
+| `EnforcedPolicyUids` | string[] | Enforced access policies. |
+| `EnforcedNatPolicyUids` | string[] | Enforced NAT policies. |
+| `ImportDisabled` | boolean | Skip flag for UI. |
+| `ShowInUI` | boolean | Whether to display the gateway. |
+
+`RulebaseLinks` specify the relationship between gateways and rulebases:
+`to_rulebase_uid` contains the rulebase UID, `link_type` indicates ordering,
+and the boolean flags describe whether the link is initial/global/section.
+
+## Operational Notes
+
+- Always deliver the full manager dataset; incremental imports are currently not supported.
+- UID keys should remain stable so repeated imports update the same records.
+- While the samples focus on IPv4 data, any object can contain IPv6 addresses
+ where the firmware supports them.
diff --git a/documentation/interface-descriptions-json/ip-area-data-format.md b/documentation/interface-descriptions-json/ip-area-data-format.md
new file mode 100644
index 0000000000..a652f32f26
--- /dev/null
+++ b/documentation/interface-descriptions-json/ip-area-data-format.md
@@ -0,0 +1,49 @@
+# IP Area Import Interface
+
+This interface loads network areas and their associated subnets. The payload
+is documented by `documentation/interface-descriptions-json/sample-ip-data-normalized.json`.
+
+## Envelope
+
+```json
+{
+ "areas": [
+ { /* area */ }
+ ]
+}
+```
+
+## Area Object
+
+| Field | Type | Required | Description |
+|-------------|----------|----------|-------------|
+| `name` | string | yes | Human readable area label (`AREA51`). |
+| `id_string` | string | yes | Unique technical identifier used for correlation. |
+| `subnets` | object[] | yes | List of subnet records attached to the area. |
+
+### Subnet Entry
+
+| Field | Type | Required | Description |
+|-------|--------|----------|-------------|
+| `ip` | string | yes | Network in CIDR notation (IPv4 or IPv6). |
+| `name`| string | yes | Friendly subnet name. |
+
+Example:
+
+```json
+{
+ "name": "AREA51",
+ "id_string": "NA51",
+ "subnets": [
+ { "ip": "10.10.10.0/24", "name": "Netz01" },
+ { "ip": "10.10.34.16/30", "name": "Netz02" }
+ ]
+}
+```
+
+## Validation Notes
+
+- Every area requires at least one subnet.
+- Subnet CIDR strings must be canonical; overlapping subnets are technically
+ allowed but should be avoided to keep reporting deterministic.
+- Imports replace the full dataset. Incremental subnet updates are not supported.
diff --git a/documentation/interface-descriptions-json/owner-data-format.md b/documentation/interface-descriptions-json/owner-data-format.md
new file mode 100644
index 0000000000..8fa0ccbce9
--- /dev/null
+++ b/documentation/interface-descriptions-json/owner-data-format.md
@@ -0,0 +1,90 @@
+# Owner Import Interface
+
+The owner interface supplies metadata about business applications, their
+responsible people, and the server addresses attached to every application.
+The data is exchanged as a JSON document that follows the structure shown in
+`documentation/interface-descriptions-json/sample-owner-data-normalized.json`.
+This document explains the structure so integrators can generate valid input.
+
+## Top-Level Envelope
+
+```json
+{
+ "owners": [
+ { /* owner definition */ }
+ ]
+}
+```
+
+The payload is a single object with one property:
+
+| Property | Type | Description |
+|----------|--------|-------------|
+| `owners` | array | All application owner records. The order is not relevant, but every object inside the array must follow the schema described below. |
+
+## Owner Object
+
+| Field | Type | Required | Description |
+|--------------------|----------|----------|-------------|
+| `name` | string | yes | Unique owner identifier inside the import. Usually matches the CMDB application name. |
+| `app_id_external` | string | yes | External system identifier (example `APP-4711`). Used to correlate repeated imports. |
+| `main_user` | string | yes | Distinguished Name (DN) of the responsible owner. |
+| `modellers` | string[] | yes | DNs of groups or users who can modify the application definition. |
+| `recert_interval` | integer | yes | Number of days between mandatory recertifications. |
+| `import_source` | string | yes | Origin of the record, e.g., `cmdb-export`. Helps auditors trace the data lineage. |
+| `app_servers` | object[] | yes | Server definitions that represent the application infrastructure. See below. |
+
+Example:
+
+```json
+{
+ "name": "owner-1",
+ "app_id_external": "APP-4711",
+ "main_user": "CN=tiso4711,OU=Benutzer,DC=company,DC=DE",
+ "modellers": ["CN=app-4711-eigner,OU=Gruppen,DC=company,DC=DE"],
+ "recert_interval": 180,
+ "import_source": "cmdb-export",
+ "app_servers": [ /* … */ ]
+}
+```
+
+## `app_servers` Entries
+
+`app_servers` is an array that enumerates every IP element (host, network, or
+range) assigned to an application owner. Each entry uses the following fields:
+
+| Field | Type | Required | Description |
+|----------|--------|----------|-------------|
+| `ip` | string | yes | Starting IP in IPv4 or IPv6 notation. |
+| `ip_end` | string | yes | Last IP in the block. Equal to `ip` for single hosts. |
+| `type` | string | yes | Defines how the IP range is interpreted. Supported values are listed below. |
+| `name` | string | no | Friendly identifier for the block. Mandatory only when the type is `host`. |
+
+### Supported `type` values
+
+| Value | Meaning |
+|----------|---------|
+| `host` | Single IP. `ip` and `ip_end` must be identical. `name` carries the host label (`host_10.12.16.88` in the sample). |
+| `network`| Subnet definition. `ip` is the network start and `ip_end` is the broadcast/high address. |
+| `range` | Arbitrary inclusive range from `ip` to `ip_end`. |
+
+Example block:
+
+```json
+{
+ "ip": "10.112.5.88",
+ "ip_end": "10.112.5.99",
+ "type": "range"
+}
+```
+
+## Validation Checklist
+
+- Every owner must have at least one `app_servers` entry.
+- `ip` and `ip_end` must be valid IPv4 or IPv6 addresses.
+- Use consistent casing for DNs to avoid duplicate detection issues.
+- Keep the input idempotent: repeatable imports must keep the same
+ `app_id_external` values.
+- Imports must contain the full dataset. Incremental updates are not supported;
+ each run replaces the previous definition.
+- Missing IP objects are not deleted but marked as removed and can be reactivated when contained in a consecutive import.
diff --git a/documentation/interface-descriptions-json/sample-firewall-normalized-config-multi-manager.json b/documentation/interface-descriptions-json/sample-firewall-normalized-config-multi-manager.json
new file mode 100644
index 0000000000..40527707d9
--- /dev/null
+++ b/documentation/interface-descriptions-json/sample-firewall-normalized-config-multi-manager.json
@@ -0,0 +1,177 @@
+{
+ "ConfigFormat": "NORMALIZED",
+ "ManagerSet": [
+ {
+ "ManagerUid": "forti-mgr cactus",
+ "ManagerName": "forti-mgr cactus",
+ "IsSuperManager": true,
+ "DomainUid": "",
+ "DomainName": "",
+ "SubManagerIds": [],
+ "Configs": [
+ {
+ "ConfigFormat": "NORMALIZED_LEGACY",
+ "action": "INSERT",
+ "network_objects": {
+ "fb6dc52c-a40e-51f0-58af-1e9bdb08123e": {
+ "obj_uid": "fb6dc52c-a40e-51f0-58af-1e9bdb08123e",
+ "obj_name": "gEMS_ALL_UNKNOWN_CLIENTS",
+ "obj_ip": "0.0.0.0/32",
+ "obj_ip_end": "255.255.255.255/32",
+ "obj_color": "black",
+ "obj_typ": "network",
+ "obj_member_refs": null,
+ "obj_member_names": null,
+ "obj_comment": null
+ }
+ },
+ "service_objects": {
+ "gblock-high-risk": {
+ "svc_uid": "gblock-high-risk",
+ "svc_name": "gblock-high-risk",
+ "svc_port": null,
+ "svc_port_end": null,
+ "svc_color": "foreground",
+ "svc_typ": "simple",
+ "ip_proto": 0,
+ "svc_member_refs": null,
+ "svc_member_names": null,
+ "svc_comment": null,
+ "svc_timeout": null,
+ "rpc_nr": null
+ }
+ },
+ "users": {},
+ "zone_objects": {
+ "any": {
+ "zone_name": "any"
+ },
+ "dmz": {
+ "zone_name": "dmz"
+ }
+ },
+ "rulebases": [],
+ "gateways": []
+ }
+ ]
+ },
+ {
+ "ManagerUid": "25952584-ae41-51f0-0c4e-cd435e7407a6",
+ "ManagerName": "forti-mgr cactus_my_adom",
+ "IsSuperManager": false,
+ "DomainUid": "",
+ "DomainName": "my_adom",
+ "SubManagerIds": [],
+ "Configs": [
+ {
+ "ConfigFormat": "NORMALIZED_LEGACY",
+ "action": "INSERT",
+ "network_objects": {
+ "4340e634-add5-51f0-f96e-1a939e4c3326": {
+ "obj_uid": "4340e634-add5-51f0-f96e-1a939e4c3326",
+ "obj_name": "gmail.com",
+ "obj_ip": "0.0.0.0/32",
+ "obj_ip_end": "255.255.255.255/32",
+ "obj_color": "black",
+ "obj_typ": "network",
+ "obj_member_refs": null,
+ "obj_member_names": null,
+ "obj_comment": "IPv4 addresses of Fabric Devices."
+ }
+ },
+ "service_objects": {
+ "block-high-risk": {
+ "svc_uid": "block-high-risk",
+ "svc_name": "block-high-risk",
+ "svc_port": 1234,
+ "svc_port_end": 1234,
+ "svc_color": "foreground",
+ "svc_typ": "simple",
+ "ip_proto": 6,
+ "svc_member_refs": null,
+ "svc_member_names": null,
+ "svc_comment": null,
+ "svc_timeout": null,
+ "rpc_nr": null
+ }
+ },
+ "users": {},
+ "zone_objects": {
+ "1-A1": {
+ "zone_name": "1-A1"
+ },
+ "dmz": {
+ "zone_name": "dmz"
+ }
+ },
+ "rulebases": [
+ {
+ "id": null,
+ "uid": "rules_adom_v4_mypkg",
+ "name": "rules_adom_v4_mypkg",
+ "mgm_uid": "25952584-ae41-51f0-0c4e-cd435e7407a6",
+ "is_global": false,
+ "Rules": {
+ "3fa82664-5717-4562-b3fc-2c963f66afa6": {
+ "rule_num": 1,
+ "rule_num_numeric": 0.0,
+ "rule_disabled": false,
+ "rule_src_neg": false,
+ "rule_src": "gmail.com",
+ "rule_src_refs": "4340e634-add5-51f0-f96e-1a939e4c3326",
+ "rule_dst_neg": false,
+ "rule_dst": "gmail.com",
+ "rule_dst_refs": "4340e634-add5-51f0-f96e-1a939e4c3326",
+ "rule_svc_neg": false,
+ "rule_svc": "block-high-risk",
+ "rule_svc_refs": "block-high-risk",
+ "rule_action": "accept",
+ "rule_track": "none",
+ "rule_installon": "rules_adom_v4_mypkg",
+ "rule_time": "",
+ "rule_name": "my combined nat rule",
+ "rule_uid": "3fa82664-5717-4562-b3fc-2c963f66afa6",
+ "rule_custom_fields": "{}",
+ "rule_implied": false,
+ "rule_type": "access",
+ "last_change_admin": "tim",
+ "parent_rule_uid": null,
+ "last_hit": null,
+ "rule_comment": null,
+ "rule_src_zone": "dmz",
+ "rule_dst_zone": "dmz",
+ "rule_head_text": null
+ }
+ }
+ }
+ ],
+ "gateways": [
+ {
+ "Uid": "fg1_root",
+ "Name": "fg1_root",
+ "Routing": [],
+ "Interfaces": [],
+ "RulebaseLinks": [
+ {
+ "from_rulebase_uid": null,
+ "from_rule_uid": null,
+ "to_rulebase_uid": "rules_adom_v4_mypkg",
+ "link_type": "ordered",
+ "is_initial": true,
+ "is_global": false,
+ "is_section": false
+ }
+ ],
+ "GlobalPolicyUid": null,
+ "EnforcedPolicyUids": [],
+ "EnforcedNatPolicyUids": [],
+ "ImportDisabled": false,
+ "ShowInUI": true
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "native_config": {}
+}
diff --git a/documentation/interface-descriptions-json/sample-firewall-normalized-config-single-manager.json b/documentation/interface-descriptions-json/sample-firewall-normalized-config-single-manager.json
new file mode 100644
index 0000000000..4893a04f48
--- /dev/null
+++ b/documentation/interface-descriptions-json/sample-firewall-normalized-config-single-manager.json
@@ -0,0 +1,114 @@
+{
+ "ConfigFormat": "NORMALIZED",
+ "ManagerSet": [
+ {
+ "ManagerUid": "25952584-ae41-51f0-0c4e-cd435e7407a6",
+ "ManagerName": "cp-single-mgr",
+ "IsSuperManager": false,
+ "DomainUid": "",
+ "DomainName": "",
+ "SubManagerIds": [],
+ "Configs": [
+ {
+ "ConfigFormat": "NORMALIZED_LEGACY",
+ "action": "INSERT",
+ "network_objects": {
+ "4340e634-add5-51f0-f96e-1a939e4c3326": {
+ "obj_uid": "4340e634-add5-51f0-f96e-1a939e4c3326",
+ "obj_name": "gmail.com",
+ "obj_ip": "0.0.0.0/32",
+ "obj_ip_end": "255.255.255.255/32",
+ "obj_color": "black",
+ "obj_typ": "network",
+ "obj_member_refs": null,
+ "obj_member_names": null,
+ "obj_comment": "IPv4 addresses of Fabric Devices."
+ }
+ },
+ "service_objects": {
+ "block-high-risk": {
+ "svc_uid": "block-high-risk",
+ "svc_name": "block-high-risk",
+ "svc_port": 1234,
+ "svc_port_end": 1234,
+ "svc_color": "foreground",
+ "svc_typ": "simple",
+ "ip_proto": 6,
+ "svc_member_refs": null,
+ "svc_member_names": null,
+ "svc_comment": null,
+ "svc_timeout": null,
+ "rpc_nr": null
+ }
+ },
+ "users": {},
+ "zone_objects": {},
+ "rulebases": [
+ {
+ "id": null,
+ "uid": "section1",
+ "name": "section1",
+ "mgm_uid": "25952584-ae41-51f0-0c4e-cd435e7407a6",
+ "is_global": false,
+ "Rules": {
+ "3fa82664-5717-4562-b3fc-2c963f66afa6": {
+ "rule_num": 1,
+ "rule_num_numeric": 0.0,
+ "rule_disabled": false,
+ "rule_src_neg": false,
+ "rule_src": "gmail.com",
+ "rule_src_refs": "4340e634-add5-51f0-f96e-1a939e4c3326",
+ "rule_dst_neg": false,
+ "rule_dst": "gmail.com",
+ "rule_dst_refs": "4340e634-add5-51f0-f96e-1a939e4c3326",
+ "rule_svc_neg": false,
+ "rule_svc": "block-high-risk",
+ "rule_svc_refs": "block-high-risk",
+ "rule_action": "accept",
+ "rule_track": "none",
+ "rule_installon": "rules_adom_v4_mypkg",
+ "rule_time": "",
+ "rule_name": "my combined nat rule",
+ "rule_uid": "3fa82664-5717-4562-b3fc-2c963f66afa6",
+ "rule_custom_fields": "{}",
+ "rule_implied": false,
+ "rule_type": "access",
+ "last_change_admin": "tim",
+ "parent_rule_uid": null,
+ "last_hit": null,
+ "rule_comment": null,
+ "rule_head_text": null
+ }
+ }
+ }
+ ],
+ "gateways": [
+ {
+ "Uid": "fg1_root",
+ "Name": "fg1_root",
+ "Routing": [],
+ "Interfaces": [],
+ "RulebaseLinks": [
+ {
+ "from_rulebase_uid": null,
+ "from_rule_uid": null,
+ "to_rulebase_uid": "section1",
+ "link_type": "ordered",
+ "is_initial": true,
+ "is_global": false,
+ "is_section": false
+ }
+ ],
+ "GlobalPolicyUid": null,
+ "EnforcedPolicyUids": [],
+ "EnforcedNatPolicyUids": [],
+ "ImportDisabled": false,
+ "ShowInUI": true
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "native_config": {}
+}
diff --git a/documentation/interface-descriptions-json/sample-ip-data-normalized.json b/documentation/interface-descriptions-json/sample-ip-data-normalized.json
new file mode 100644
index 0000000000..3cbc59b4ab
--- /dev/null
+++ b/documentation/interface-descriptions-json/sample-ip-data-normalized.json
@@ -0,0 +1,32 @@
+{
+ "areas": [
+ {
+ "name": "AREA51",
+ "id_string": "NA51",
+ "subnets": [
+ {
+ "ip": "10.10.10.0/24",
+ "name": "Netz01"
+ },
+ {
+ "ip": "10.10.34.16/30",
+ "name": "Netz02"
+ }
+ ]
+ },
+ {
+ "name": "AREA52",
+ "id_string": "NA52",
+ "subnets": [
+ {
+ "ip": "10.20.10.0/24",
+ "name": "Netz03"
+ },
+ {
+ "ip": "10.20.34.128/25",
+ "name": "Netz04"
+ }
+ ]
+ }
+ ]
+}
diff --git a/documentation/interface-descriptions-json/sample-owner-data-normalized.json b/documentation/interface-descriptions-json/sample-owner-data-normalized.json
new file mode 100644
index 0000000000..faa1e345ed
--- /dev/null
+++ b/documentation/interface-descriptions-json/sample-owner-data-normalized.json
@@ -0,0 +1,46 @@
+{
+ "owners": [
+ {
+ "name": "owner-1",
+ "app_id_external": "APP-4711",
+ "main_user": "CN=tiso4711,OU=Benutzer,DC=company,DC=DE",
+ "modellers": ["CN=app-4711-eigner,OU=Gruppen,DC=company,DC=DE"],
+ "recert_interval": 180,
+ "import_source": "cmdb-export",
+ "app_servers": [
+ {
+ "ip": "10.12.16.88",
+ "ip_end": "10.12.16.88",
+ "type": "host",
+ "name": "host_10.12.16.88"
+ },
+ {
+ "ip": "10.14.0.128",
+ "ip_end": "10.14.0.255",
+ "type": "network"
+ }
+ ]
+ },
+ {
+ "name": "owner-2",
+ "app_id_external": "APP-4712",
+ "main_user": "CN=tiso4712,OU=Benutzer,DC=company,DC=DE",
+ "modellers": ["CN=app-4712-eigner,OU=Gruppen,DC=company,DC=DE"],
+ "recert_interval": 365,
+ "import_source": "cmdb-export",
+ "app_servers": [
+ {
+ "ip": "10.112.16.88",
+ "ip_end": "10.112.16.88",
+ "type": "host",
+ "name": "host_10.112.16.88"
+ },
+ {
+ "ip": "10.112.5.88",
+ "ip_end": "10.112.5.99",
+ "type": "range"
+ }
+ ]
+ }
+ ]
+}
diff --git a/documentation/revision-history-develop.md b/documentation/revision-history-develop.md
index 5085341a81..c8b59c0ef5 100644
--- a/documentation/revision-history-develop.md
+++ b/documentation/revision-history-develop.md
@@ -260,7 +260,7 @@ bugfix release:
# 8.7.1 - 05.03.2025 DEVELOP
- ldap writepath for groups
-
+ demo data (major versions only)
# 8.7.2 - 20.03.2025 DEVELOP
- new config values
- external request: attempt counter
@@ -283,7 +283,7 @@ bugfix release:
# 8.8.8 - 21.08.2025 DEVELOP
- add read-only db user fwo_ro
-- also reducing db listener to localhost
+- also reducing db listener to localhost and other hardening changes
# 8.8.9 - 27.08.2025 DEVELOP
- prepare tables + settings for owner recert + first throw recert popup
@@ -301,6 +301,7 @@ bugfix release:
# 8.9.2 - 17.10.2025 DEVELOP
- add ownerLifeCycleState
- add manageable ownerLifeCycleState menu
+- fix two modelling ui glitches
# 8.9.3 - 05.11.2025 DEVELOP
- hotfix missing permissions for app data import in certain constellations
@@ -311,3 +312,29 @@ bugfix release:
## 8.9.5 - 10.12.2025 DEVELOP
- bugfix release: modelling - change planning showed duplicate NA elements for rule delete requests
+
+# 9.0 - 30.06.2025 DEVELOP
+- rule to gateway mapping 1:n
+- add report output for rule to gw mapping
+- cleanup/rework of main import function
+
+Breaking changes
+ Due to introduction of venv for all imports, the following steps have to be taken to manually import a config:
+
+ sudo -u fworch -i
+ cd importer
+ source venv/bin/activate
+ ./import-mgm.py -m xy -f -s -d 8
+ As we now need support for pip, in installations behind url filter, make sure that all sub-domains of
+ pythonhosted.org
+ are also allowed.
+
+To initialize your venv locally (e.g. within your vscode environment) run
+
+make sure to place your venv outside the repo, e.g. into /home/user/dev/venv
+
+ cd /home/user/dev
+ python3 -m venv installer-venv
+ source installer-venv/bin/activate
+ pip install -r path-to-repo/roles/importer/files/importer/requirements.txt
+- also reducing db listener to localhost
diff --git a/documentation/revision-history-main.md b/documentation/revision-history-main.md
index 9b43c848ef..07d2ffa41e 100644
--- a/documentation/revision-history-main.md
+++ b/documentation/revision-history-main.md
@@ -553,6 +553,12 @@ hotfix release
- improved quality control with stricter automated checks
- various fixes in modelling module
+# 8.8.8 - 23.08.2025 MAIN
+- add read-only db user fwo_ro
+- hadening changes
+ - apache config (information leakage)
+ - listeners (hasura, postgres)
+ - log santisation
# 8.9.1 - 02.10.2025 MAIN
- owner-recertification
@@ -563,6 +569,6 @@ hotfix release
# 8.9.3 - 05.11.2025 MAIN
- hotfix missing permissions for app data import in certain constellations
-## 8.9.4 - 09.12.2025 DEVELOP
+## 8.9.4 - 09.12.2025 MAIN
- bugfix release: common service connection not editable
- new custom scripts for iiq and cmdb import
diff --git a/documentation/rework-2025/readme.md b/documentation/rework-2025/readme.md
new file mode 100644
index 0000000000..410bc6db3c
--- /dev/null
+++ b/documentation/rework-2025/readme.md
@@ -0,0 +1,873 @@
+
+# rework 2025
+
+## data collected from autodiscovery (sync per management)
+
+```json
+{
+ "management": {
+ "uid": "xxxx", # either name or uid can be fetched from API
+ "name": "xxx",
+ "type": "cpr8x, fortimanager, fortiOS, ...", # not read from sync but can be configured in UI/DB
+ "do_not_import": false, # not read from sync but can be configured in UI/DB
+ "hide_in_ui": false, # not read from sync but can be configured in UI/DB
+ "gateways": [
+ {
+ "uid": "xxx",
+ "name": "abc",
+ "type": "cp-gw|fortigate|...",
+ "do_not_import": false, # not read from sync but can be configured in UI/DB
+ "hide_in_ui": false # not read from sync but can be configured in UI/DB
+ }
+ ],
+ "managements": [] # for sub managements, content: see above
+}
+```
+- auto discovery does not store any information about rulebases!
+- this is the only data stored (by initial sync of a management) in the database
+- this means we cannot identify any rulebase changes, pro: these changes will be ignored
+- if we cannot sync a firewall, e.g. palo, we need more fields like policy name, ...
+
+
+## interface for file import (json data structure, normalized config)
+
+```json
+{
+ "global_rulebases": [],
+ "global_network_objects": [],
+ "global_network_services": [],
+ "global_users": [],
+ "sub-managements": [
+ {
+ "uid": "xxx",
+ "name": "abc",
+ # type is not imported because we always assume normalized format,
+ "gateways": [
+ {
+ "uid": "xxx",
+ "name": "xxx",
+ "type": "",
+ "initial_rulebase_uid": "uidxy",
+ "routing": [],
+ "interfaces": [],
+ }
+ ],
+ "rulebases": [
+ {
+ "uid": "xxx",
+ "name": "xxx",
+ "type": "access|nat",
+ "rb_order_no": 1,
+ "rules": [
+ ...
+ "src_ref": "uid1234"
+ ]
+ }
+ ],
+ "rulebase_link": [
+ {
+ "rulebase_from_uid": "xxx", # when null, this is the initial rulebase for a gateway
+ "rule_from_uid": "xxx", # when null, this is the initial rulebase for a gateway
+ "rulebase_to_uid": "xxx",
+ "link_type": "layer|ordered|...",
+ "gateway-uid": "xxx"
+ },
+ ...more links
+ ]
+ "network_objects": []
+ "network_services": []
+ "users": []
+ },
+ #...more managements
+ ]
+}
+```
+
+## check point specific information
+
+### getting routing information from checkpoint gateways
+
+Note: this only works when the user has admin privileges (not just read-only!)
+
+```
+curl --request POST \
+ --url https://192.168.100.111/web_api/run-script \
+ --header 'Content-Type: application/json' \
+ --header 'x-chkp-sid: uvXWp05mPeL-YKAvdyWvzY4YeGGt9GApCyZ2ndwQlOg' \
+ --cookie Session=Login \
+ --data '{
+ "script-name": "show-routing-table",
+ "script": "ip route show",
+ "targets": [
+ "sting-gw"
+ ]
+}'
+```
+return a task id:
+```json
+{
+ "tasks": [
+ {
+ "target": "sting-gw",
+ "task-id": "345d9cfc-43b3-4a9f-b5c9-97755e075622"
+ }
+ ]
+}
+```
+
+this must be used to get the routing table (once the task is completed):
+
+```
+curl --request POST \
+ --url https://192.168.100.111/web_api/show-task \
+ --header 'Content-Type: application/json' \
+ --header 'x-chkp-sid: Ir3sw1JejoCx24Cs0uHfebjhWKGaPdcjwyW9Cn86RlE' \
+ --cookie Session=Login \
+ --data '{
+ "task-id": "345d9cfc-43b3-4a9f-b5c9-97755e075622",
+ "details-level": "full"
+}'
+```
+
+returns
+
+```json
+{
+ "tasks": [
+ {
+ "uid": "5570f622-47c2-41f9-86e6-dc4d0ebf3584",
+ "name": "sting-gw - show-routing-table",
+ "type": "CdmTaskNotification",
+ "task-id": "345d9cfc-43b3-4a9f-b5c9-97755e075622",
+ "task-name": "sting-gw - show-routing-table",
+ "status": "succeeded",
+ "progress-percentage": 100,
+ "task-details": [
+ {
+ "uid": "698d6899-b963-4d34-aeb7-c3c9fa58f8f4",
+ "statusCode": "succeeded",
+ "statusDescription": "default via ...",
+ "taskNotification": "5570f622-47c2-41f9-86e6-dc4d0ebf3584",
+ "gatewayId": "cbdd1e35-b6e9-4ead-b13f-fd6389e34987",
+ "gatewayName": "sting-gw",
+ "transactionId": 559998982,
+ "responseMessage": "xxxx"
+ }
+ ]
+ }
+ ]
+}
+```
+Get the full routing table by base64-decoding the responseMessage.
+
+### interface checkpoint (file) import (json data structure, native config)
+
+ Here, the gateways contain interface information when the command is issued with details-level=full
+ Routing info is missing though.
+ Just need to add the interfaces sectino for each gateway to the gateways.
+
+```json
+{
+ "users": {},
+ "object_tables": [
+ {
+ "object_type": "hosts",
+ "object_chunks": []
+ },
+ {
+ "object_type": "networks",
+ "object_chunks": []
+ },
+ {
+ "object_type": "groups",
+ "object_chunks": []
+ },
+ {
+ "object_type": "gateways-and-servers",
+ "object_chunks": [
+ {
+ "objects": [
+ {
+ "uid": "cbdd1e35-b6e9-4ead-b13f-fd6389e34987",
+ "name": "sting-gw",
+ "type": "simple-gateway",
+ "domain": {
+ "uid": "41e821a0-3720-11e3-aa6e-0800200c9fde",
+ "name": "SMC User",
+ "domain-type": "domain"
+ },
+ "policy": {
+ "access-policy-revision": {
+ "uid": "ab620dc5-b80c-45c6-85ee-11f4faa9f5aa",
+ "name": "xxx",
+ "type": "session",
+ "state": "published",
+ "publish-time": {
+ "posix": 1736767952039,
+ "iso-8601": "2025-01-13T12:32+0100"
+ }
+ },
+ },
+ "operating-system": "Gaia",
+ "hardware": "Open server",
+ "version": "R82",
+ "ipv4-address": "192.168.10.90",
+ "interfaces": [
+ {
+ "interface-name": "eth5",
+ "ipv4-address": "91.26.19.194",
+ "ipv4-network-mask": "255.255.255.240",
+ "ipv4-mask-length": 28,
+ "dynamic-ip": false,
+ "topology": {
+ "leads-to-internet": true
+ }
+ },
+ {
+ "interface-name": "eth2",
+ "ipv4-address": "213.157.1.55",
+ "ipv4-network-mask": "255.255.255.240",
+ "ipv4-mask-length": 28,
+ "dynamic-ip": false,
+ "topology": {
+ "leads-to-internet": false,
+ "ip-address-behind-this-interface": "specific",
+ "leads-to-specific-network": {
+ "uid": "a4944c2e-1801-408f-8fd9-48f873123d5d",
+ "name": "sting-gw_eth2",
+ "type": "group"
+ },
+ "leads-to-dmz": false
+ }
+ },
+ {
+ "interface-name": "eth3",
+ "ipv4-address": "192.168.20.1",
+ "ipv4-network-mask": "255.255.255.192",
+ "ipv4-mask-length": 26,
+ "dynamic-ip": false,
+ "topology": {
+ "leads-to-internet": false,
+ "ip-address-behind-this-interface": "network defined by the interface ip and net mask",
+ "leads-to-dmz": false
+ }
+ }
+ ],
+ "network-security-blades": {
+ "firewall": true,
+ "site-to-site-vpn": true,
+ "application-control": true,
+ "content-awareness": true,
+ "identity-awareness": true,
+ "monitoring": true,
+ "url-filtering": true,
+ "anti-bot": true,
+ "anti-virus": true,
+ "ips": true
+ },
+ },
+ {
+ "uid": "d7a49524-b877-4b2a-b949-a736d86e2baa",
+ "name": "sting-mgmt",
+ "type": "checkpoint-host",
+ "domain": {
+ "uid": "41e821a0-3720-11e3-aa6e-0800200c9fde",
+ "name": "SMC User",
+ "domain-type": "domain"
+ },
+ "ipv4-address": "192.168.100.111",
+ "interfaces": [
+ ],
+ "version": "R82",
+ "os": "Gaia",
+ "hardware": "Open server",
+ "sic-name": "CN=cp_mgmt_sting-mgmt,O=sting.cactus-es.de..jnbkhk",
+ "sic-state": "communicating",
+ "management-blades": {
+ "network-policy-management": true,
+ "user-directory": false,
+ "compliance": false,
+ "logging-and-status": true,
+ "smart-event-server": true,
+ "smart-event-correlation": true,
+ "endpoint-policy": false,
+ "secondary": false,
+ "identity-logging": true
+ },
+ "firewall": false,
+ "read-only": true,
+ "available-actions": {
+ "edit": "false",
+ "delete": "false",
+ "clone": "not_supported"
+ }
+ }
+ ],
+ "from": 1,
+ "to": 2,
+ "total": 2
+ }
+ ]
+ },
+ ...
+ ],
+ "gateways": [
+ {
+ "dev_name": "sting-gw",
+ "dev_uid": "sting-gw-uid", # see gateways and servers
+ "rulebase_links":
+ [
+ {
+ "from_rulebase_uid": "sting-big-gw-top-level-uid",
+ "to_rulebase_uid": "sting-first-ordered-layer-uid",
+ "from_rule_uid": "9cd8ce05-32db-483a-a44a-0c9131cca022",
+ "link_type": "ordered"
+ },
+ {
+ "from_rulebase_uid": "inline-layer-1-uid",
+ "to_rulebase_uid": "inline-layer-2-uid",
+ "from_rule_uid": "52f5f517-6801-4741-84b4-13fe26843433",
+ "link_type": "inline"
+ },
+ {
+ "from_rulebase_uid": "inline-layer-1-uid",
+ "to_rulebase_uid": "inline-layer-2-uid",
+ "from_rule_uid": "52f5f517-6801-4741-84b4-13fe26843433",
+ "link_type": "inline"
+ },
+ {
+ "from_rulebase_uid": "global1-uid",
+ "to_rulebase_uid": "local-uid",
+ "from_rule_uid": "7288ebb9-d951-4139-9ac2-980cf34a60af",
+ "link_type": "local"
+ },
+ {
+ "from_rulebase_uid": null,
+ "to_rulebase_uid": "sting-first-ordered-layer-uid",
+ "from_rule_uid": null,
+ "link_type": "inital"
+ }
+ ],
+ "routing": [],
+ "interfaces": [
+ {
+ "interface-name": "eth5",
+ "ipv4-address": "91.26.19.194",
+ "ipv4-network-mask": "255.255.255.240",
+ "ipv4-mask-length": 28,
+ "dynamic-ip": false,
+ "topology": {
+ "leads-to-internet": true
+ }
+ },
+ {
+ "interface-name": "eth2",
+ "ipv4-address": "213.157.1.55",
+ "ipv4-network-mask": "255.255.255.240",
+ "ipv4-mask-length": 28,
+ "dynamic-ip": false,
+ "topology": {
+ "leads-to-internet": false,
+ "ip-address-behind-this-interface": "specific",
+ "leads-to-specific-network": {
+ "uid": "a4944c2e-1801-408f-8fd9-48f873123d5d",
+ "name": "sting-gw_eth2",
+ "type": "group",
+ "domain": {
+ "uid": "41e821a0-3720-11e3-aa6e-0800200c9fde",
+ "name": "SMC User",
+ "domain-type": "domain"
+ },
+ "icon": "General/group",
+ "color": "black"
+ },
+ "leads-to-dmz": false
+ }
+ },
+ {
+ "interface-name": "eth3",
+ "ipv4-address": "192.168.20.1",
+ "ipv4-network-mask": "255.255.255.192",
+ "ipv4-mask-length": 26,
+ "dynamic-ip": false,
+ "topology": {
+ "leads-to-internet": false,
+ "ip-address-behind-this-interface": "network defined by the interface ip and net mask",
+ "leads-to-dmz": false
+ }
+ },
+ {
+ "interface-name": "eth0",
+ "ipv4-address": "192.168.10.90",
+ "ipv4-network-mask": "255.255.255.0",
+ "ipv4-mask-length": 24,
+ "dynamic-ip": false,
+ "topology": {
+ "leads-to-internet": false,
+ "ip-address-behind-this-interface": "network defined by the interface ip and net mask",
+ "leads-to-dmz": false
+ }
+ },
+ {
+ "interface-name": "eth1",
+ "ipv4-address": "5.7.21.44",
+ "ipv4-network-mask": "255.255.255.248",
+ "ipv4-mask-length": 29,
+ "dynamic-ip": false,
+ "topology": {
+ "leads-to-internet": true
+ }
+ },
+ {
+ "interface-name": "eth6",
+ "ipv4-address": "192.168.5.3",
+ "ipv4-network-mask": "255.255.255.0",
+ "ipv4-mask-length": 24,
+ "dynamic-ip": false,
+ "topology": {
+ "leads-to-internet": true
+ }
+ },
+ {
+ "interface-name": "eth4",
+ "ipv4-address": "192.168.15.1",
+ "ipv4-network-mask": "255.255.255.0",
+ "ipv4-mask-length": 24,
+ "dynamic-ip": false,
+ "topology": {
+ "leads-to-internet": false,
+ "ip-address-behind-this-interface": "network defined by the interface ip and net mask",
+ "leads-to-dmz": false
+ }
+ }
+ ]
+ },
+ {
+ "dev_name": "small-gw",
+ "dev_uid": "small-gw-uid",
+ "rulebase_links":
+ [
+ {
+ "from_rulebase_uid": null,
+ "to_rulebase_uid": "inline-layer-1-uid",
+ "from_rule_uid": null,
+ "link_type": "inital"
+ }
+ ],
+ "routing": [],
+ "interfaces": []
+ }
+ ],
+ "rulebases": [
+ {
+ "name": "sting-first-ordered-layer",
+ "uid": "sting-first-ordered-layer-uid",
+ "chunks": [
+ {
+ "uid": "sting-first-ordered-layer-uid",
+ "name": "sting-first-ordered-layer",
+ "rulebase": [
+ {
+ "uid": "dd12a686-256e-418c-8d8f-a7eb37fc7e5f",
+ "name": "Block attacks",
+ "type": "access-section",
+ "from": 1,
+ "to": 2,
+ "rulebase": [...],
+ }
+ }
+ ]
+ },
+ {
+ "name": "inline-layer-2",
+ "uid": "inline-layer-2-uid",
+ "chunks": [
+ ],
+ },
+
+ ],
+ "nat_rulebases": [
+ {
+ "nat_rule_chunks": [...]
+ }
+ ]
+}
+```
+## Graphql query for new rulebase_link structure
+
+Assuming
+- that we allow for duplication of rule in API call results (we do not fetch every rule exactly once but multiple times)
+
+```graphql
+fragment ruleDetails on rule {
+ rule_uid
+}
+
+fragment rulebaseDetails on rulebase {
+ id
+ uid
+ name
+}
+
+fragment rulebaseLinkRecursive12Layers on rulebase_link {
+ link_type
+ rulebase {
+ ...rulebaseDetails
+ rules(order_by: {rule_num_numeric: asc}) {
+ ...ruleDetails
+ rulebase_links(where: {gw_id: {_eq: $gwId}}) {
+ rulebase {
+ ...rulebaseDetails
+ rules(order_by: {rule_num_numeric: asc}) {
+ ...ruleDetails
+ rulebase_links(where: {gw_id: {_eq: $gwId}}) {
+ rulebase {
+ ...rulebaseDetails
+ rules(order_by: {rule_num_numeric: asc}) {
+ ...ruleDetails
+ rulebase_links(where: {gw_id: {_eq: $gwId}}) {
+ rulebase {
+ ...rulebaseDetails
+ rules(order_by: {rule_num_numeric: asc}) {
+ ...ruleDetails
+ rulebase_links(where: {gw_id: {_eq: $gwId}}) {
+ rulebase {
+ ...rulebaseDetails
+ rules(order_by: {rule_num_numeric: asc}) {
+ ...ruleDetails
+ rulebase_links(where: {gw_id: {_eq: $gwId}}) {
+ rulebase {
+ ...rulebaseDetails
+ rules(order_by: {rule_num_numeric: asc}) {
+ ...ruleDetails
+ rulebase_links(where: {gw_id: {_eq: $gwId}}) {
+ rulebase {
+ ...rulebaseDetails
+ rules(order_by: {rule_num_numeric: asc}) {
+ ...ruleDetails
+ rulebase_links(where: {gw_id: {_eq: $gwId}}) {
+ rulebase {
+ ...rulebaseDetails
+ rules(order_by: {rule_num_numeric: asc}) {
+ ...ruleDetails
+ rulebase_links(where: {gw_id: {_eq: $gwId}}) {
+ rulebase {
+ ...rulebaseDetails
+ rules(order_by: {rule_num_numeric: asc}) {
+ ...ruleDetails
+ rulebase_links(where: {gw_id: {_eq: $gwId}}) {
+ rulebase {
+ ...rulebaseDetails
+ rules(order_by: {rule_num_numeric: asc}) {
+ ...ruleDetails
+ rulebase_links(where: {gw_id: {_eq: $gwId}}) {
+ rulebase {
+ ...rulebaseDetails
+ rules(order_by: {rule_num_numeric: asc}) {
+ ...ruleDetails
+ rulebase_links(where: {gw_id: {_eq: $gwId}}) {
+ rulebase {
+ ...rulebaseDetails
+ rules(order_by: {rule_num_numeric: asc}) {
+ ...ruleDetails
+ rulebase_links(where: {gw_id: {_eq: $gwId}}) {
+ rulebase {
+ ...rulebaseDetails
+ rules(order_by: {rule_num_numeric: asc}) {
+ ...ruleDetails
+ rulebase_links(where: {gw_id: {_eq: $gwId}}) {
+ rulebase {
+ ...rulebaseDetails
+ rules(order_by: {rule_num_numeric: asc}) {
+ ...ruleDetails
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ access_rule
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+fragment linkType on stm_link_type {
+ id
+ name
+}
+
+fragment rulebaseLinkRecursive4Layers on rulebase_link {
+ stm_link_type {
+ ...linkType
+ }
+ rulebase {
+ ...rulebaseDetails
+ rules(order_by: {rule_num_numeric: asc}) {
+ ...ruleDetails
+ rulebase_links(where: {gw_id: {_eq: $gwId}}) {
+ stm_link_type {
+ ...linkType
+ }
+ rulebase {
+ ...rulebaseDetails
+ rules(order_by: {rule_num_numeric: asc}) {
+ ...ruleDetails
+ rulebase_links(where: {gw_id: {_eq: $gwId}}) {
+ stm_link_type {
+ ...linkType
+ }
+ rulebase {
+ ...rulebaseDetails
+ rules(order_by: {rule_num_numeric: asc}) {
+ ...ruleDetails
+ rulebase_links(where: {gw_id: {_eq: $gwId}}) {
+ stm_link_type {
+ ...linkType
+ }
+ rulebase {
+ ...rulebaseDetails
+ rules(order_by: {rule_num_numeric: asc}) {
+ ...ruleDetails
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+query getGwRules12Layers($gwId: Int!) {
+ rulebase_link(where: {gw_id: {_eq: $gwId}, link_type: {_eq: 0}}) {
+ ...rulebaseLinkRecursive12Layers
+ }
+}
+
+query rulesReport($gwId: Int, $mgmId: Int) {
+ management(where: {mgm_id: {_eq: $mgmId}}) {
+ name: mgm_name
+ id: mgm_id
+ devices {
+ name: dev_name
+ id: dev_id
+ rulebase_links(where: {gw_id: {_eq: $gwId}, link_type: {_eq: 0}}) {
+ ...rulebaseLinkRecursive4Layers
+ }
+ }
+ }
+}
+```
+results in the following structure
+```json
+{
+ "data": {
+ "rulebase_link": [
+ {
+ "rulebase": {
+ "id": 347,
+ "uid": "cactus_Security-uid",
+ "name": "cactus_Security-name",
+ "rules": [
+ {
+ "rule_uid": "57f19d03-ed09-4083-9b71-b5eb28e69156",
+ "rulebase_links": []
+ },
+ {
+ "rule_uid": "f505a53a-27a3-4a09-87d2-633ec91a6c53",
+ "rulebase_links": []
+ },
+ {
+ "rule_uid": "a1f25d46-984d-42b3-a986-71279c12550f",
+ "rulebase_links": [
+ {
+ "rulebase": {
+ "id": 64,
+ "uid": "inline-layer-1-uid",
+ "name": "inline-layer-1",
+ "rules": [
+ {
+ "rule_uid": "acc044f6-2a4f-459b-b78c-9e7afee92621",
+ "rulebase_links": []
+ },
+ {
+ "rule_uid": "acc044f6-2a4f-459b-b78c-9e7afee92621",
+ "rulebase_links": []
+ },
+ {
+ "rule_uid": "acc044f6-2a4f-459b-b78c-9e7afee92621",
+ "rulebase_links": []
+ },
+ {
+ "rule_uid": "acc044f6-2a4f-459b-b78c-9e7afee92621",
+ "rulebase_links": [
+ {
+ "rulebase": {
+ "rules": [
+ {
+ "rule_uid": "33ba8f84-a11d-463a-9ee8-6859b6b0035e",
+ },
+ {
+ "rule_uid": "ea9d67bf-f098-4576-baad-1eb7b68900dd",
+ },
+ {
+ "rule_uid": "ea9d67bf-f098-4576-baad-1eb7b68900dd",
+ }
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ }
+ },
+ {
+ "rulebase": {
+ "uid": "inline-layer-1-uid",
+ "name": "inline-layer-1",
+ "rules": [
+ {
+ "rulebase_id": 64,
+ "rule_uid": "acc044f6-2a4f-459b-b78c-9e7afee92621",
+ "rulebase_links": []
+ },
+ {
+ "rulebase_id": 64,
+ "rule_uid": "acc044f6-2a4f-459b-b78c-9e7afee92621",
+ "rulebase_links": [
+ {
+ "rulebase": {
+ "rules": [
+ {
+ "rule_uid": "33ba8f84-a11d-463a-9ee8-6859b6b0035e",
+ },
+ {
+ "rule_uid": "ea9d67bf-f098-4576-baad-1eb7b68900dd",
+ }
+ ]
+ }
+ }
+ ]
+ }
+ ]
+ }
+ }
+ ]
+ },
+ {
+ "rule_uid": "1d48ddf8-3e84-4bb6-8bae-3caf68714637",
+ "rulebase_links": []
+ },
+ {
+ "rule_uid": "88a1934f-9971-48f0-a0d2-7c15ab455447",
+ "rulebase_links": []
+ }
+ ]
+ }
+ }
+ ]
+ }
+}
+```
+Note: the more layers we fetch, the longer the query takes. Query with 7 layers takes ~4s, whereas 5 layers take 2s.
+
+Todo:
+- add indices
+- get rule_metadata into the query
+
+
+
+Your query is slow because of the recursive nature of rulebase_links, especially when you reach the third level of recursion. Here are the primary reasons why the last recursion layer significantly impacts performance:
+
+1. Exponential Growth of Joins
+Each level of recursion in rulebase_links multiplies the number of records retrieved. The deeper the recursion, the more rulebase records are fetched, leading to:
+
+More joins on rulebase_links
+More filtering and ordering operations
+A large Cartesian explosion of data if multiple links exist per rulebase
+By removing the last recursion layer, you're reducing the number of joins and resulting dataset size, making the query significantly faster.
+
+
+How to Optimize?
+✅ Approach 1: Pre-fetch Rulebase IDs, Then Fetch Rules in Batches
+Instead of fetching rules at every recursion step, first get rulebase_ids and then batch query rules separately.
+
+graphql
+Copy
+query rulesReport($mgmId: [Int!], $relevantImportId: bigint, $limit: Int, $offset: Int) {
+ management(
+ where: {
+ hide_in_gui: {_eq: false},
+ mgm_id: {_in: $mgmId},
+ stm_dev_typ: {dev_typ_is_multi_mgmt: {_eq: false}, is_pure_routing_device: {_eq: false}}
+ },
+ order_by: {mgm_name: asc}
+ ) {
+ id: mgm_id
+ uid: mgm_uid
+ name: mgm_name
+ devices(where: {hide_in_gui: {_eq: false}, _or: [{dev_id: {_eq: 9}}]}) {
+ name: dev_name
+ id: dev_id
+ uid: dev_uid
+ rulebase_links(where: {link_type: {_eq: 0}}) {
+ rulebase {
+ id
+ }
+ }
+ }
+ }
+}
+Then, run a second query using those IDs:
+
+graphql
+Copy
+query rulesBatch($rulebaseIds: [Int!], $limit: Int, $offset: Int) {
+ rulebase(where: {id: {_in: $rulebaseIds}}) {
+ id
+ rules(limit: $limit, offset: $offset, order_by: {rule_num_numeric: asc}) {
+ ...ruleOverview
+ }
+ }
+}
+Why This Works:
+
+First query retrieves rulebase IDs efficiently (small result set).
+Second query retrieves rules in a batch, reducing recursion.
+Uses fewer nested loops, improving performance.
diff --git a/fwo.code-workspace b/fwo.code-workspace
index aed977d919..402c648101 100644
--- a/fwo.code-workspace
+++ b/fwo.code-workspace
@@ -3,9 +3,10 @@
{
"path": "."
},
+ { "path": "roles/tests-unit/files/FWO.Test" },
{
"path": "roles"
}
],
"settings": {}
-}
\ No newline at end of file
+}
diff --git a/inventory/group_vars/all.yml b/inventory/group_vars/all.yml
index 404692cf35..a4d996f107 100644
--- a/inventory/group_vars/all.yml
+++ b/inventory/group_vars/all.yml
@@ -1,5 +1,5 @@
### general settings
-product_version: "8.9.5"
+product_version: "9.0"
ansible_user: "{{ lookup('env', 'USER') }}"
ansible_become_method: sudo
ansible_python_interpreter: /usr/bin/python3
@@ -13,6 +13,7 @@ syslog_host: "{{ groups['logserver'].0 }}"
fworch_db_host: "{{ groups['databaseserver'].0 }}"
api_hostname: "{{ groups['apiserver'].0 }}"
sample_hostname: "{{ groups['sampleserver'].0 }}"
+linux_architecture: amd64
# fworch_db_host_ip: "{{ hostvars[fworch_db_host]['ansible_host'] }}"
@@ -35,7 +36,9 @@ fworch_parent_dir: "/usr/local"
fworch_home: "{{ fworch_parent_dir }}/{{ product_name }}"
fworch_conf_dir: "{{ fworch_home }}/etc"
fworch_conf_file: "{{ fworch_conf_dir }}/fworch.json"
+last_commit_id_file_path: "{{ fworch_conf_dir }}/last_commit_id.txt"
fworch_secrets_dir: "{{ fworch_conf_dir }}/secrets"
+importer_venv_dir: "{{ fworch_home }}/importer/importer-venv"
# setting default proxy (may be overwritten via --extra-vars)
http_proxy: "{{ lookup('env','http_proxy') }}"
diff --git a/inventory/group_vars/sampleserver.yml b/inventory/group_vars/sampleserver.yml
index 456092a864..87e11e3853 100644
--- a/inventory/group_vars/sampleserver.yml
+++ b/inventory/group_vars/sampleserver.yml
@@ -7,13 +7,15 @@ sample_fortigate_basename: fortigate
sample_fortigate_name: "{{ sample_fortigate_basename }}_demo"
sample_checkpoint_basename: "checkpoint"
sample_checkpoint_name: "{{ sample_checkpoint_basename }}_demo"
-sample_checkpoint_uri: "https://fwodemodata.cactus.de/demo11-r82.json"
+sample_checkpoint_uri: "https://fwodemodata.cactus.de/demo12_cpr8x_v{{ product_version.split('.') | first }}.json"
demo_cpr8x_name_without_demo: "CPR81"
demo_cpr8x_name: "{{ demo_cpr8x_name_without_demo}}_demo"
-demo_cpr8x_uri: "https://fwodemodata.cactus.de/demo09_cpr81.json"
+demo_cpr8x_uri: "https://fwodemodata.cactus.de/demo12_cpr8x_v{{ product_version.split('.') | first }}.json"
+demo_cpr8x_mgm_uid: "d7a49524-b877-4b2a-b949-a736d86e2baa"
+demo_cpr8x_dev_uid: "cbdd1e35-b6e9-4ead-b13f-fd6389e34987"
demo_fos_name_without_demo: "FortiOS"
demo_fos_name: "{{ demo_fos_name_without_demo }}_demo"
-demo_fos_uri: "https://fwodemodata.cactus.de/demo10_fOS.json"
+demo_fos_uri: "https://fwodemodata.cactus.de/demo10_fOS_v{{ product_version.split('.') | first }}.json"
sample_postfix: _demo
# sample openldap server
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000000..961fd691fe
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,85 @@
+[tool.pyright]
+include = [
+ "roles/importer/files/importer",
+]
+exclude = [
+ "**/node_modules",
+ "**/__pycache__",
+ ".git",
+ ".venv",
+ "venv",
+ "scripts",
+ "roles/importer/files/importer/test",
+]
+
+ignore = [
+ "./scripts",
+]
+
+venvPath = "."
+venv = ".venv"
+
+typeCheckingMode = "strict"
+reportMissingImports = false
+reportMissingTypeStubs = true
+
+
+
+[tool.ruff]
+line-length = 120
+indent-width = 4
+target-version = "py313"
+exclude = [
+ ".bzr",
+ ".direnv",
+ ".eggs",
+ ".git",
+ ".git-rewrite",
+ ".hg",
+ ".ipynb_checkpoints",
+ ".mypy_cache",
+ ".nox",
+ ".pants.d",
+ ".pyenv",
+ ".pytest_cache",
+ ".pytype",
+ ".ruff_cache",
+ ".svn",
+ ".tox",
+ ".venv",
+ ".vscode",
+ "__pypackages__",
+ "_build",
+ "buck-out",
+ "build",
+ "dist",
+ "node_modules",
+ "site-packages",
+ "venv",
+ "__init__.py",
+ "scripts",
+ "roles/tests-integration",
+ "roles/importer/files/importer/test"
+]
+
+[tool.ruff.lint]
+select = ["ALL"]
+ignore = [
+ "ANN", "COM", "C90", "DJ", "EXE", "T10", "TID",
+ "D100", "D101", "D102", "D103", "D104", "D105", "D106", "D107",
+ "D200", "D203", "D205", "D212", "D400", "D401", "D415", "D417",
+ "E402", "E501", "TRY003", "TD002", "TD003", "FIX002",
+ "EM101", "EM102", "PTH100", "PTH123", "PLR0913", "PT009",
+ "PLR0915", "PLR0912", "PLR0911", "FBT001", "FBT002",
+ "BLE001", "DTZ005", "TRY301", "TRY300", "B904", "S113", "S104"
+]
+fixable = ["ALL"]
+dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
+
+[tool.ruff.format]
+quote-style = "double"
+indent-style = "space"
+skip-magic-trailing-comma = false
+line-ending = "auto"
+
+
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000000..39f298cfc3
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1 @@
+ansible>2.13
diff --git a/roles/.editorconfig b/roles/.editorconfig
index fd51e099e7..a2af9f4b7b 100644
--- a/roles/.editorconfig
+++ b/roles/.editorconfig
@@ -1,10 +1,10 @@
# roles EditorConfig file
root = true
-[*]
+[*cs]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = false
-insert_final_newline = false
\ No newline at end of file
+insert_final_newline = true
diff --git a/roles/Directory.Build.props b/roles/Directory.Build.props
new file mode 100644
index 0000000000..80e9ef7965
--- /dev/null
+++ b/roles/Directory.Build.props
@@ -0,0 +1,7 @@
+
+
+ true
+ CS1998
+ CS0618
+
+
\ No newline at end of file
diff --git a/roles/FWO.sln b/roles/FWO.sln
index f8ffb688df..50dbb1730f 100644
--- a/roles/FWO.sln
+++ b/roles/FWO.sln
@@ -31,17 +31,24 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FWO.Mail", "lib\files\FWO.M
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FWO.Recert", "lib\files\FWO.Recert\FWO.Recert.csproj", "{520779B1-20EB-45D9-8A02-D0C4DFEC9302}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "files", "files", "{B48F8BD5-1056-4670-BEFA-F4A260293B6F}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FWO.Encryption", "lib\files\FWO.Encryption\FWO.Encryption.csproj", "{6EBEBF57-3399-4008-BA10-0D21F6827244}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FWO.Basics", "lib\files\FWO.Basics\FWO.Basics.csproj", "{0CBD4CC5-3E39-4134-A0E1-4DB8999619F3}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FWO.Basics", "lib\files\FWO.Basics\FWO.Basics.csproj", "{0CBD4CC5-3E39-4134-A0E1-4DB8999619F3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FWO.Services", "lib\files\FWO.Services\FWO.Services.csproj", "{56021BEC-39E5-4EC7-B3DB-80834B57B2F0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FWO.Data", "lib\files\FWO.Data\FWO.Data.csproj", "{9E80E931-7350-1356-D96D-34F3EDA6ABFB}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FWO.ExternalSystems", "lib\files\FWO.ExternalSystems\FWO.ExternalSystems.csproj", "{072AE48C-2B5E-49A6-81AF-32F6C941655D}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
+ ProjectSection(SolutionItems) = preProject
+ .editorconfig = .editorconfig
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "files", "files", "{5603A4C9-96C7-439B-B587-FF58B1ECE052}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FWO.Compliance", "lib\files\FWO.Compliance\FWO.Compliance.csproj", "{A22D31CE-BAC6-4708-8B34-BCD46972F2F6}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FWO.FwLogic", "lib\files\FWO.FwLogic\FWO.FwLogic.csproj", "{FFB78F8D-88DC-4781-A0EB-7FE6C25BA2F0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -117,10 +124,14 @@ Global
{9E80E931-7350-1356-D96D-34F3EDA6ABFB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9E80E931-7350-1356-D96D-34F3EDA6ABFB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9E80E931-7350-1356-D96D-34F3EDA6ABFB}.Release|Any CPU.Build.0 = Release|Any CPU
- {072AE48C-2B5E-49A6-81AF-32F6C941655D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {072AE48C-2B5E-49A6-81AF-32F6C941655D}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {072AE48C-2B5E-49A6-81AF-32F6C941655D}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {072AE48C-2B5E-49A6-81AF-32F6C941655D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A22D31CE-BAC6-4708-8B34-BCD46972F2F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A22D31CE-BAC6-4708-8B34-BCD46972F2F6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A22D31CE-BAC6-4708-8B34-BCD46972F2F6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A22D31CE-BAC6-4708-8B34-BCD46972F2F6}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FFB78F8D-88DC-4781-A0EB-7FE6C25BA2F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FFB78F8D-88DC-4781-A0EB-7FE6C25BA2F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FFB78F8D-88DC-4781-A0EB-7FE6C25BA2F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FFB78F8D-88DC-4781-A0EB-7FE6C25BA2F0}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -136,11 +147,7 @@ Global
{C1D1FE54-4CDD-41C0-AABC-415950AA24D5} = {CE55F125-0CD2-4789-A3C1-045DEF33ABA5}
{1E7CA417-C64A-4BD9-98D2-5A0A2DD94726} = {CE55F125-0CD2-4789-A3C1-045DEF33ABA5}
{520779B1-20EB-45D9-8A02-D0C4DFEC9302} = {CE55F125-0CD2-4789-A3C1-045DEF33ABA5}
- {B48F8BD5-1056-4670-BEFA-F4A260293B6F} = {CE55F125-0CD2-4789-A3C1-045DEF33ABA5}
- {6EBEBF57-3399-4008-BA10-0D21F6827244} = {B48F8BD5-1056-4670-BEFA-F4A260293B6F}
- {0CBD4CC5-3E39-4134-A0E1-4DB8999619F3} = {B48F8BD5-1056-4670-BEFA-F4A260293B6F}
- {9E80E931-7350-1356-D96D-34F3EDA6ABFB} = {B48F8BD5-1056-4670-BEFA-F4A260293B6F}
- {072AE48C-2B5E-49A6-81AF-32F6C941655D} = {B48F8BD5-1056-4670-BEFA-F4A260293B6F}
+ {FFB78F8D-88DC-4781-A0EB-7FE6C25BA2F0} = {CE55F125-0CD2-4789-A3C1-045DEF33ABA5}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {68364621-1011-4D44-9CF5-518F0DC3F459}
diff --git a/roles/api/files/replace_metadata.json b/roles/api/files/replace_metadata.json
index 079b8edde3..f4fc69536c 100644
--- a/roles/api/files/replace_metadata.json
+++ b/roles/api/files/replace_metadata.json
@@ -11,14 +11,68 @@
"tables": [
{
"table": {
- "name": "ip_range",
+ "name": "criterion",
"schema": "compliance"
},
- "object_relationships": [
+ "array_relationships": [
{
- "name": "network_zone",
+ "name": "ip_ranges",
"using": {
- "foreign_key_constraint_on": "network_zone_id"
+ "foreign_key_constraint_on": {
+ "column": "criterion_id",
+ "table": {
+ "name": "ip_range",
+ "schema": "compliance"
+ }
+ }
+ }
+ },
+ {
+ "name": "network_zone_communications",
+ "using": {
+ "foreign_key_constraint_on": {
+ "column": "criterion_id",
+ "table": {
+ "name": "network_zone_communication",
+ "schema": "compliance"
+ }
+ }
+ }
+ },
+ {
+ "name": "network_zones",
+ "using": {
+ "foreign_key_constraint_on": {
+ "column": "criterion_id",
+ "table": {
+ "name": "network_zone",
+ "schema": "compliance"
+ }
+ }
+ }
+ },
+ {
+ "name": "policy_criterions",
+ "using": {
+ "foreign_key_constraint_on": {
+ "column": "criterion_id",
+ "table": {
+ "name": "policy_criterion",
+ "schema": "compliance"
+ }
+ }
+ }
+ },
+ {
+ "name": "violations",
+ "using": {
+ "foreign_key_constraint_on": {
+ "column": "criterion_id",
+ "table": {
+ "name": "violation",
+ "schema": "compliance"
+ }
+ }
}
}
],
@@ -28,201 +82,302 @@
"permission": {
"check": {},
"columns": [
- "network_zone_id",
- "ip_range_end",
- "ip_range_start"
- ]
- }
- },
- {
- "role": "importer",
- "permission": {
- "check": {},
- "columns": [
- "network_zone_id",
- "ip_range_end",
- "ip_range_start"
+ "id",
+ "comment",
+ "content",
+ "criterion_type",
+ "import_source",
+ "name",
+ "created",
+ "removed"
]
- }
+ },
+ "comment": ""
},
{
"role": "middleware-server",
"permission": {
"check": {},
"columns": [
- "network_zone_id",
- "ip_range_end",
- "ip_range_start"
+ "id",
+ "comment",
+ "content",
+ "criterion_type",
+ "import_source",
+ "name",
+ "created",
+ "removed"
]
- }
+ },
+ "comment": ""
}
],
"select_permissions": [
{
- "role": "approver",
+ "role": "auditor",
"permission": {
"columns": [
- "network_zone_id",
- "ip_range_end",
- "ip_range_start"
+ "id",
+ "comment",
+ "content",
+ "criterion_type",
+ "import_source",
+ "name",
+ "created",
+ "removed"
],
- "filter": {},
- "allow_aggregations": true
- }
+ "filter": {}
+ },
+ "comment": ""
},
{
- "role": "auditor",
+ "role": "fw-admin",
"permission": {
"columns": [
- "network_zone_id",
- "ip_range_end",
- "ip_range_start"
+ "id",
+ "comment",
+ "content",
+ "criterion_type",
+ "import_source",
+ "name",
+ "created",
+ "removed"
],
- "filter": {},
- "allow_aggregations": true
- }
+ "filter": {}
+ },
+ "comment": ""
},
{
- "role": "fw-admin",
+ "role": "middleware-server",
"permission": {
"columns": [
- "network_zone_id",
- "ip_range_end",
- "ip_range_start"
+ "id",
+ "comment",
+ "content",
+ "criterion_type",
+ "import_source",
+ "name",
+ "created",
+ "removed"
],
- "filter": {},
- "allow_aggregations": true
- }
+ "filter": {}
+ },
+ "comment": ""
},
{
- "role": "implementer",
+ "role": "reporter",
"permission": {
"columns": [
- "network_zone_id",
- "ip_range_end",
- "ip_range_start"
+ "id",
+ "comment",
+ "content",
+ "criterion_type",
+ "import_source",
+ "name",
+ "created",
+ "removed"
],
- "filter": {},
- "allow_aggregations": true
- }
+ "filter": {}
+ },
+ "comment": ""
},
{
- "role": "importer",
+ "role": "reporter-viewall",
"permission": {
"columns": [
- "network_zone_id",
- "ip_range_end",
- "ip_range_start"
+ "id",
+ "comment",
+ "content",
+ "criterion_type",
+ "import_source",
+ "name",
+ "created",
+ "removed"
+ ],
+ "filter": {}
+ },
+ "comment": ""
+ }
+ ],
+ "update_permissions": [
+ {
+ "role": "fw-admin",
+ "permission": {
+ "columns": [
+ "id",
+ "comment",
+ "content",
+ "criterion_type",
+ "import_source",
+ "name",
+ "created",
+ "removed"
],
"filter": {},
- "allow_aggregations": true
- }
+ "check": null
+ },
+ "comment": ""
},
{
"role": "middleware-server",
"permission": {
"columns": [
- "network_zone_id",
- "ip_range_end",
- "ip_range_start"
+ "comment",
+ "import_source"
],
"filter": {},
- "allow_aggregations": true
+ "check": null
+ },
+ "comment": ""
+ }
+ ]
+ },
+ {
+ "table": {
+ "name": "ip_range",
+ "schema": "compliance"
+ },
+ "object_relationships": [
+ {
+ "name": "criterion",
+ "using": {
+ "foreign_key_constraint_on": "criterion_id"
}
},
{
- "role": "planner",
+ "name": "network_zone",
+ "using": {
+ "foreign_key_constraint_on": "network_zone_id"
+ }
+ }
+ ],
+ "insert_permissions": [
+ {
+ "role": "fw-admin",
"permission": {
+ "check": {},
"columns": [
"network_zone_id",
"ip_range_end",
- "ip_range_start"
- ],
- "filter": {},
- "allow_aggregations": true
- }
+ "ip_range_start",
+ "criterion_id",
+ "name",
+ "created",
+ "removed"
+ ]
+ },
+ "comment": ""
},
{
- "role": "recertifier",
+ "role": "middleware-server",
"permission": {
+ "check": {},
"columns": [
"network_zone_id",
"ip_range_end",
- "ip_range_start"
- ],
- "filter": {},
- "allow_aggregations": true
+ "ip_range_start",
+ "criterion_id",
+ "name",
+ "created",
+ "removed"
+ ]
}
- },
+ }
+ ],
+ "select_permissions": [
{
- "role": "reporter",
+ "role": "auditor",
"permission": {
"columns": [
"network_zone_id",
"ip_range_end",
- "ip_range_start"
+ "ip_range_start",
+ "criterion_id",
+ "name",
+ "created",
+ "removed"
],
"filter": {},
"allow_aggregations": true
}
},
{
- "role": "reporter-viewall",
+ "role": "fw-admin",
"permission": {
"columns": [
"network_zone_id",
"ip_range_end",
- "ip_range_start"
+ "ip_range_start",
+ "criterion_id",
+ "name",
+ "created",
+ "removed"
],
"filter": {},
"allow_aggregations": true
}
},
{
- "role": "requester",
+ "role": "middleware-server",
"permission": {
"columns": [
"network_zone_id",
"ip_range_end",
- "ip_range_start"
+ "ip_range_start",
+ "criterion_id",
+ "name",
+ "created",
+ "removed"
],
"filter": {},
"allow_aggregations": true
}
},
{
- "role": "reviewer",
+ "role": "reporter",
"permission": {
"columns": [
"network_zone_id",
"ip_range_end",
- "ip_range_start"
+ "ip_range_start",
+ "criterion_id",
+ "name",
+ "created",
+ "removed"
],
- "filter": {},
- "allow_aggregations": true
- }
- }
- ],
- "update_permissions": [
+ "filter": {}
+ },
+ "comment": ""
+ },
{
- "role": "fw-admin",
+ "role": "reporter-viewall",
"permission": {
"columns": [
"network_zone_id",
"ip_range_end",
- "ip_range_start"
+ "ip_range_start",
+ "criterion_id",
+ "name",
+ "created",
+ "removed"
],
"filter": {},
- "check": {}
+ "allow_aggregations": true
}
- },
+ }
+ ],
+ "update_permissions": [
{
- "role": "importer",
+ "role": "fw-admin",
"permission": {
"columns": [
"network_zone_id",
"ip_range_end",
- "ip_range_start"
+ "ip_range_start",
+ "criterion_id",
+ "name",
+ "created",
+ "removed"
],
"filter": {},
"check": {}
@@ -234,32 +389,16 @@
"columns": [
"network_zone_id",
"ip_range_end",
- "ip_range_start"
+ "ip_range_start",
+ "criterion_id",
+ "name",
+ "created",
+ "removed"
],
"filter": {},
"check": {}
}
}
- ],
- "delete_permissions": [
- {
- "role": "fw-admin",
- "permission": {
- "filter": {}
- }
- },
- {
- "role": "importer",
- "permission": {
- "filter": {}
- }
- },
- {
- "role": "middleware-server",
- "permission": {
- "filter": {}
- }
- }
]
},
{
@@ -268,6 +407,12 @@
"schema": "compliance"
},
"object_relationships": [
+ {
+ "name": "criterion",
+ "using": {
+ "foreign_key_constraint_on": "criterion_id"
+ }
+ },
{
"name": "super_network_zone",
"using": {
@@ -335,20 +480,11 @@
"owner_id",
"super_network_zone_id",
"description",
- "name"
- ]
- }
- },
- {
- "role": "importer",
- "permission": {
- "check": {},
- "columns": [
- "id",
- "owner_id",
- "super_network_zone_id",
- "description",
- "name"
+ "name",
+ "criterion_id",
+ "id_string",
+ "created",
+ "removed"
]
}
},
@@ -357,209 +493,290 @@
"permission": {
"check": {},
"columns": [
+ "created",
+ "criterion_id",
+ "description",
"id",
+ "id_string",
+ "is_auto_calculated_internet_zone",
+ "is_auto_calculated_undefined_internal_zone",
+ "name",
"owner_id",
- "super_network_zone_id",
- "description",
- "name"
+ "removed",
+ "super_network_zone_id"
]
}
}
],
"select_permissions": [
{
- "role": "approver",
+ "role": "auditor",
"permission": {
"columns": [
"id",
- "name",
- "description",
+ "owner_id",
"super_network_zone_id",
- "owner_id"
+ "description",
+ "name",
+ "criterion_id",
+ "id_string",
+ "created",
+ "removed"
],
"filter": {},
"allow_aggregations": true
}
},
{
- "role": "auditor",
+ "role": "fw-admin",
"permission": {
"columns": [
"id",
- "name",
- "description",
+ "owner_id",
"super_network_zone_id",
- "owner_id"
+ "description",
+ "name",
+ "criterion_id",
+ "id_string",
+ "created",
+ "removed"
],
"filter": {},
"allow_aggregations": true
}
},
{
- "role": "fw-admin",
+ "role": "middleware-server",
"permission": {
"columns": [
+ "created",
+ "criterion_id",
+ "description",
"id",
+ "id_string",
+ "is_auto_calculated_internet_zone",
+ "is_auto_calculated_undefined_internal_zone",
"name",
- "description",
- "super_network_zone_id",
- "owner_id"
+ "owner_id",
+ "removed",
+ "super_network_zone_id"
],
"filter": {},
"allow_aggregations": true
}
},
{
- "role": "implementer",
+ "role": "reporter",
"permission": {
"columns": [
"id",
- "name",
- "description",
+ "owner_id",
"super_network_zone_id",
- "owner_id"
+ "description",
+ "name",
+ "criterion_id",
+ "id_string",
+ "created",
+ "removed"
],
"filter": {},
"allow_aggregations": true
}
},
{
- "role": "importer",
+ "role": "reporter-viewall",
"permission": {
"columns": [
"id",
- "name",
- "description",
+ "owner_id",
"super_network_zone_id",
- "owner_id"
+ "description",
+ "name",
+ "criterion_id",
+ "id_string",
+ "created",
+ "removed"
],
"filter": {},
"allow_aggregations": true
}
- },
+ }
+ ],
+ "update_permissions": [
{
- "role": "middleware-server",
+ "role": "fw-admin",
"permission": {
"columns": [
"id",
- "name",
- "description",
+ "owner_id",
"super_network_zone_id",
- "owner_id"
+ "description",
+ "name",
+ "criterion_id",
+ "id_string",
+ "created",
+ "removed"
],
"filter": {},
- "allow_aggregations": true
+ "check": {}
}
},
{
- "role": "planner",
+ "role": "middleware-server",
"permission": {
"columns": [
+ "created",
+ "criterion_id",
+ "description",
"id",
+ "id_string",
+ "is_auto_calculated_internet_zone",
"name",
- "description",
- "super_network_zone_id",
- "owner_id"
+ "owner_id",
+ "removed",
+ "super_network_zone_id"
],
"filter": {},
- "allow_aggregations": true
+ "check": {}
+ }
+ }
+ ]
+ },
+ {
+ "table": {
+ "name": "network_zone_communication",
+ "schema": "compliance"
+ },
+ "object_relationships": [
+ {
+ "name": "criterion",
+ "using": {
+ "foreign_key_constraint_on": "criterion_id"
}
},
{
- "role": "recertifier",
+ "name": "from_network_zone",
+ "using": {
+ "foreign_key_constraint_on": "from_network_zone_id"
+ }
+ },
+ {
+ "name": "to_network_zone",
+ "using": {
+ "foreign_key_constraint_on": "to_network_zone_id"
+ }
+ }
+ ],
+ "insert_permissions": [
+ {
+ "role": "fw-admin",
"permission": {
+ "check": {},
"columns": [
- "id",
- "name",
- "description",
- "super_network_zone_id",
- "owner_id"
- ],
- "filter": {},
- "allow_aggregations": true
+ "from_network_zone_id",
+ "to_network_zone_id",
+ "criterion_id",
+ "created",
+ "removed"
+ ]
}
},
{
- "role": "reporter",
+ "role": "middleware-server",
"permission": {
+ "check": {},
"columns": [
- "id",
- "name",
- "description",
- "super_network_zone_id",
- "owner_id"
+ "from_network_zone_id",
+ "to_network_zone_id",
+ "criterion_id",
+ "created",
+ "removed"
+ ]
+ }
+ }
+ ],
+ "select_permissions": [
+ {
+ "role": "auditor",
+ "permission": {
+ "columns": [
+ "from_network_zone_id",
+ "to_network_zone_id",
+ "criterion_id",
+ "created",
+ "removed"
],
"filter": {},
"allow_aggregations": true
}
},
{
- "role": "reporter-viewall",
+ "role": "fw-admin",
"permission": {
"columns": [
- "id",
- "name",
- "description",
- "super_network_zone_id",
- "owner_id"
+ "from_network_zone_id",
+ "to_network_zone_id",
+ "criterion_id",
+ "created",
+ "removed"
],
"filter": {},
"allow_aggregations": true
}
},
{
- "role": "requester",
+ "role": "middleware-server",
"permission": {
"columns": [
- "id",
- "name",
- "description",
- "super_network_zone_id",
- "owner_id"
+ "from_network_zone_id",
+ "to_network_zone_id",
+ "criterion_id",
+ "created",
+ "removed"
],
"filter": {},
"allow_aggregations": true
}
},
{
- "role": "reviewer",
+ "role": "reporter",
"permission": {
"columns": [
- "id",
- "name",
- "description",
- "super_network_zone_id",
- "owner_id"
+ "from_network_zone_id",
+ "to_network_zone_id",
+ "criterion_id",
+ "created",
+ "removed"
],
"filter": {},
"allow_aggregations": true
}
- }
- ],
- "update_permissions": [
+ },
{
- "role": "fw-admin",
+ "role": "reporter-viewall",
"permission": {
"columns": [
- "id",
- "owner_id",
- "super_network_zone_id",
- "description",
- "name"
+ "from_network_zone_id",
+ "to_network_zone_id",
+ "criterion_id",
+ "created",
+ "removed"
],
"filter": {},
- "check": {}
+ "allow_aggregations": true
}
- },
+ }
+ ],
+ "update_permissions": [
{
- "role": "importer",
+ "role": "fw-admin",
"permission": {
"columns": [
- "id",
- "owner_id",
- "super_network_zone_id",
- "description",
- "name"
+ "from_network_zone_id",
+ "to_network_zone_id",
+ "criterion_id",
+ "created",
+ "removed"
],
"filter": {},
"check": {}
@@ -569,315 +786,303 @@
"role": "middleware-server",
"permission": {
"columns": [
- "id",
- "owner_id",
- "super_network_zone_id",
- "description",
- "name"
+ "from_network_zone_id",
+ "to_network_zone_id",
+ "criterion_id",
+ "created",
+ "removed"
],
"filter": {},
"check": {}
}
}
+ ]
+ },
+ {
+ "table": {
+ "name": "policy",
+ "schema": "compliance"
+ },
+ "array_relationships": [
+ {
+ "name": "policy_criterions",
+ "using": {
+ "foreign_key_constraint_on": {
+ "column": "policy_id",
+ "table": {
+ "name": "policy_criterion",
+ "schema": "compliance"
+ }
+ }
+ }
+ },
+ {
+ "name": "violations",
+ "using": {
+ "foreign_key_constraint_on": {
+ "column": "policy_id",
+ "table": {
+ "name": "violation",
+ "schema": "compliance"
+ }
+ }
+ }
+ }
],
- "delete_permissions": [
+ "select_permissions": [
{
- "role": "fw-admin",
+ "role": "auditor",
"permission": {
+ "columns": [
+ "disabled",
+ "id",
+ "name",
+ "created_date"
+ ],
"filter": {}
- }
+ },
+ "comment": ""
},
{
- "role": "importer",
+ "role": "fw-admin",
"permission": {
+ "columns": [
+ "disabled",
+ "id",
+ "name",
+ "created_date"
+ ],
"filter": {}
- }
+ },
+ "comment": ""
},
{
"role": "middleware-server",
"permission": {
+ "columns": [
+ "disabled",
+ "id",
+ "name",
+ "created_date"
+ ],
"filter": {}
- }
+ },
+ "comment": ""
}
]
},
{
"table": {
- "name": "network_zone_communication",
+ "name": "policy_criterion",
"schema": "compliance"
},
"object_relationships": [
{
- "name": "from_network_zone",
+ "name": "criterion",
"using": {
- "foreign_key_constraint_on": "from_network_zone_id"
+ "foreign_key_constraint_on": "criterion_id"
}
},
{
- "name": "to_network_zone",
+ "name": "policy",
"using": {
- "foreign_key_constraint_on": "to_network_zone_id"
+ "foreign_key_constraint_on": "policy_id"
}
}
],
- "insert_permissions": [
+ "select_permissions": [
+ {
+ "role": "auditor",
+ "permission": {
+ "columns": [
+ "criterion_id",
+ "policy_id",
+ "created",
+ "removed"
+ ],
+ "filter": {}
+ },
+ "comment": ""
+ },
{
"role": "fw-admin",
"permission": {
- "check": {},
"columns": [
- "from_network_zone_id",
- "to_network_zone_id"
- ]
- }
+ "criterion_id",
+ "policy_id",
+ "created",
+ "removed"
+ ],
+ "filter": {}
+ },
+ "comment": ""
},
{
- "role": "importer",
+ "role": "middleware-server",
"permission": {
- "check": {},
"columns": [
- "from_network_zone_id",
- "to_network_zone_id"
- ]
+ "criterion_id",
+ "policy_id",
+ "created",
+ "removed"
+ ],
+ "filter": {}
+ },
+ "comment": ""
+ }
+ ]
+ },
+ {
+ "table": {
+ "name": "violation",
+ "schema": "compliance"
+ },
+ "object_relationships": [
+ {
+ "name": "criterion",
+ "using": {
+ "foreign_key_constraint_on": "criterion_id"
+ }
+ },
+ {
+ "name": "policy",
+ "using": {
+ "foreign_key_constraint_on": "policy_id"
}
},
+ {
+ "name": "rule",
+ "using": {
+ "foreign_key_constraint_on": "rule_id"
+ }
+ }
+ ],
+ "insert_permissions": [
{
"role": "middleware-server",
"permission": {
"check": {},
"columns": [
- "from_network_zone_id",
- "to_network_zone_id"
+ "criterion_id",
+ "details",
+ "found_date",
+ "id",
+ "mgmt_uid",
+ "policy_id",
+ "removed_date",
+ "risk_score",
+ "rule_id",
+ "rule_uid"
]
- }
+ },
+ "comment": ""
}
],
"select_permissions": [
- {
- "role": "approver",
- "permission": {
- "columns": [
- "from_network_zone_id",
- "to_network_zone_id"
- ],
- "filter": {},
- "allow_aggregations": true
- }
- },
{
"role": "auditor",
"permission": {
"columns": [
- "from_network_zone_id",
- "to_network_zone_id"
+ "id",
+ "rule_id",
+ "criterion_id",
+ "policy_id",
+ "risk_score",
+ "details",
+ "found_date",
+ "removed_date"
],
- "filter": {},
- "allow_aggregations": true
- }
+ "filter": {}
+ },
+ "comment": ""
},
{
"role": "fw-admin",
"permission": {
"columns": [
- "from_network_zone_id",
- "to_network_zone_id"
- ],
- "filter": {},
- "allow_aggregations": true
- }
- },
- {
- "role": "implementer",
- "permission": {
- "columns": [
- "from_network_zone_id",
- "to_network_zone_id"
+ "id",
+ "rule_id",
+ "criterion_id",
+ "policy_id",
+ "risk_score",
+ "details",
+ "found_date",
+ "removed_date"
],
- "filter": {},
- "allow_aggregations": true
- }
+ "filter": {}
+ },
+ "comment": ""
},
{
- "role": "importer",
+ "role": "middleware-server",
"permission": {
"columns": [
- "from_network_zone_id",
- "to_network_zone_id"
+ "criterion_id",
+ "details",
+ "found_date",
+ "id",
+ "mgmt_uid",
+ "policy_id",
+ "removed_date",
+ "risk_score",
+ "rule_id",
+ "rule_uid"
],
- "filter": {},
- "allow_aggregations": true
- }
- },
+ "filter": {}
+ },
+ "comment": ""
+ }
+ ],
+ "update_permissions": [
{
"role": "middleware-server",
"permission": {
"columns": [
- "from_network_zone_id",
- "to_network_zone_id"
+ "removed_date"
],
"filter": {},
- "allow_aggregations": true
+ "check": null
+ },
+ "comment": ""
+ }
+ ]
+ },
+ {
+ "table": {
+ "name": "change_history",
+ "schema": "modelling"
+ },
+ "object_relationships": [
+ {
+ "name": "owner",
+ "using": {
+ "foreign_key_constraint_on": "app_id"
}
- },
+ }
+ ],
+ "insert_permissions": [
{
- "role": "planner",
+ "role": "middleware-server",
"permission": {
+ "check": {},
"columns": [
- "from_network_zone_id",
- "to_network_zone_id"
- ],
- "filter": {},
- "allow_aggregations": true
- }
+ "id",
+ "object_id",
+ "changer",
+ "change_text",
+ "app_id",
+ "change_type",
+ "object_type",
+ "change_time",
+ "change_source"
+ ]
+ },
+ "comment": ""
},
{
- "role": "recertifier",
+ "role": "modeller",
"permission": {
- "columns": [
- "from_network_zone_id",
- "to_network_zone_id"
- ],
- "filter": {},
- "allow_aggregations": true
- }
- },
- {
- "role": "reporter",
- "permission": {
- "columns": [
- "from_network_zone_id",
- "to_network_zone_id"
- ],
- "filter": {},
- "allow_aggregations": true
- }
- },
- {
- "role": "reporter-viewall",
- "permission": {
- "columns": [
- "from_network_zone_id",
- "to_network_zone_id"
- ],
- "filter": {},
- "allow_aggregations": true
- }
- },
- {
- "role": "requester",
- "permission": {
- "columns": [
- "from_network_zone_id",
- "to_network_zone_id"
- ],
- "filter": {},
- "allow_aggregations": true
- }
- },
- {
- "role": "reviewer",
- "permission": {
- "columns": [
- "from_network_zone_id",
- "to_network_zone_id"
- ],
- "filter": {},
- "allow_aggregations": true
- }
- }
- ],
- "update_permissions": [
- {
- "role": "fw-admin",
- "permission": {
- "columns": [
- "from_network_zone_id",
- "to_network_zone_id"
- ],
- "filter": {},
- "check": {}
- }
- },
- {
- "role": "importer",
- "permission": {
- "columns": [
- "from_network_zone_id",
- "to_network_zone_id"
- ],
- "filter": {},
- "check": {}
- }
- },
- {
- "role": "middleware-server",
- "permission": {
- "columns": [
- "from_network_zone_id",
- "to_network_zone_id"
- ],
- "filter": {},
- "check": {}
- }
- }
- ],
- "delete_permissions": [
- {
- "role": "fw-admin",
- "permission": {
- "filter": {}
- }
- },
- {
- "role": "importer",
- "permission": {
- "filter": {}
- }
- },
- {
- "role": "middleware-server",
- "permission": {
- "filter": {}
- }
- }
- ]
- },
- {
- "table": {
- "name": "change_history",
- "schema": "modelling"
- },
- "object_relationships": [
- {
- "name": "owner",
- "using": {
- "foreign_key_constraint_on": "app_id"
- }
- }
- ],
- "insert_permissions": [
- {
- "role": "middleware-server",
- "permission": {
- "check": {},
- "columns": [
- "id",
- "object_id",
- "changer",
- "change_text",
- "app_id",
- "change_type",
- "object_type",
- "change_time",
- "change_source"
- ]
- },
- "comment": ""
- },
- {
- "role": "modeller",
- "permission": {
- "check": {},
+ "check": {},
"columns": [
"id",
"object_id",
@@ -3167,6 +3372,34 @@
}
}
],
+ "insert_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "check": {},
+ "columns": [
+ "abs_change_id",
+ "control_id",
+ "log_obj_id",
+ "new_obj_id",
+ "old_obj_id",
+ "documented",
+ "security_relevant",
+ "change_action",
+ "change_request_info",
+ "unique_name",
+ "change_type_id",
+ "doku_admin",
+ "import_admin",
+ "mgm_id",
+ "changelog_obj_comment",
+ "change_time",
+ "docu_time"
+ ]
+ },
+ "comment": ""
+ }
+ ],
"select_permissions": [
{
"role": "importer",
@@ -3202,12 +3435,6 @@
"schema": "public"
},
"object_relationships": [
- {
- "name": "device",
- "using": {
- "foreign_key_constraint_on": "dev_id"
- }
- },
{
"name": "import_control",
"using": {
@@ -3263,6 +3490,36 @@
}
}
],
+ "insert_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "check": {},
+ "columns": [
+ "abs_change_id",
+ "control_id",
+ "log_rule_id",
+ "new_rule_id",
+ "old_rule_id",
+ "documented",
+ "implicit_change",
+ "security_relevant",
+ "change_action",
+ "change_request_info",
+ "unique_name",
+ "change_type_id",
+ "dev_id",
+ "doku_admin",
+ "import_admin",
+ "mgm_id",
+ "changelog_rule_comment",
+ "change_time",
+ "docu_time"
+ ]
+ },
+ "comment": ""
+ }
+ ],
"select_permissions": [
{
"role": "auditor",
@@ -3288,6 +3545,9 @@
"change_time",
"unique_name"
],
+ "computed_fields": [
+ "cl_rule_relevant_for_tenant"
+ ],
"filter": {}
}
},
@@ -3315,6 +3575,9 @@
"change_time",
"docu_time"
],
+ "computed_fields": [
+ "cl_rule_relevant_for_tenant"
+ ],
"filter": {},
"allow_aggregations": true
}
@@ -3323,25 +3586,28 @@
"role": "importer",
"permission": {
"columns": [
- "log_rule_id",
- "doku_admin",
+ "abs_change_id",
"control_id",
- "import_admin",
+ "log_rule_id",
"new_rule_id",
"old_rule_id",
- "implicit_change",
- "abs_change_id",
- "change_action",
- "changelog_rule_comment",
"documented",
- "docu_time",
- "mgm_id",
- "dev_id",
- "change_type_id",
+ "implicit_change",
"security_relevant",
+ "change_action",
"change_request_info",
+ "unique_name",
+ "change_type_id",
+ "dev_id",
+ "doku_admin",
+ "import_admin",
+ "mgm_id",
+ "changelog_rule_comment",
"change_time",
- "unique_name"
+ "docu_time"
+ ],
+ "computed_fields": [
+ "cl_rule_relevant_for_tenant"
],
"filter": {},
"allow_aggregations": true
@@ -3371,6 +3637,9 @@
"change_time",
"docu_time"
],
+ "computed_fields": [
+ "cl_rule_relevant_for_tenant"
+ ],
"filter": {}
},
"comment": ""
@@ -3497,6 +3766,9 @@
"change_time",
"unique_name"
],
+ "computed_fields": [
+ "cl_rule_relevant_for_tenant"
+ ],
"filter": {},
"allow_aggregations": true
}
@@ -3552,31 +3824,59 @@
}
}
],
- "select_permissions": [
+ "insert_permissions": [
{
"role": "importer",
"permission": {
+ "check": {},
"columns": [
- "log_svc_id",
- "doku_admin",
+ "abs_change_id",
"control_id",
- "import_admin",
+ "log_svc_id",
"new_svc_id",
"old_svc_id",
- "abs_change_id",
- "change_action",
- "changelog_svc_comment",
"documented",
- "docu_time",
- "mgm_id",
- "change_type_id",
"security_relevant",
+ "change_action",
"change_request_info",
- "change_time",
- "unique_name"
- ],
- "filter": {},
- "allow_aggregations": true
+ "unique_name",
+ "change_type_id",
+ "doku_admin",
+ "import_admin",
+ "mgm_id",
+ "changelog_svc_comment",
+ "change_time",
+ "docu_time"
+ ]
+ },
+ "comment": ""
+ }
+ ],
+ "select_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "columns": [
+ "log_svc_id",
+ "doku_admin",
+ "control_id",
+ "import_admin",
+ "new_svc_id",
+ "old_svc_id",
+ "abs_change_id",
+ "change_action",
+ "changelog_svc_comment",
+ "documented",
+ "docu_time",
+ "mgm_id",
+ "change_type_id",
+ "security_relevant",
+ "change_request_info",
+ "change_time",
+ "unique_name"
+ ],
+ "filter": {},
+ "allow_aggregations": true
}
}
]
@@ -3630,6 +3930,34 @@
}
}
],
+ "insert_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "check": {},
+ "columns": [
+ "abs_change_id",
+ "control_id",
+ "log_usr_id",
+ "new_user_id",
+ "old_user_id",
+ "documented",
+ "security_relevant",
+ "change_action",
+ "change_request_info",
+ "unique_name",
+ "change_type_id",
+ "doku_admin",
+ "import_admin",
+ "mgm_id",
+ "changelog_user_comment",
+ "change_time",
+ "docu_time"
+ ]
+ },
+ "comment": ""
+ }
+ ],
"select_permissions": [
{
"role": "importer",
@@ -4334,6 +4662,14 @@
"name": "customtxt",
"schema": "public"
},
+ "object_relationships": [
+ {
+ "name": "languageByLanguage",
+ "using": {
+ "foreign_key_constraint_on": "language"
+ }
+ }
+ ],
"select_permissions": [
{
"role": "anonymous",
@@ -4525,18 +4861,6 @@
}
}
},
- {
- "name": "changelog_rules",
- "using": {
- "foreign_key_constraint_on": {
- "column": "dev_id",
- "table": {
- "name": "changelog_rule",
- "schema": "public"
- }
- }
- }
- },
{
"name": "gw_interfaces",
"using": {
@@ -4598,39 +4922,24 @@
}
},
{
- "name": "rule_metadata",
+ "name": "rule_enforced_on_gateways",
"using": {
"foreign_key_constraint_on": {
"column": "dev_id",
"table": {
- "name": "rule_metadata",
+ "name": "rule_enforced_on_gateway",
"schema": "public"
}
}
}
},
{
- "name": "rules",
+ "name": "rulebase_links",
"using": {
"foreign_key_constraint_on": {
- "column": "dev_id",
+ "column": "gw_id",
"table": {
- "name": "rule",
- "schema": "public"
- }
- }
- }
- },
- {
- "name": "rules_with_owner",
- "using": {
- "manual_configuration": {
- "column_mapping": {
- "dev_id": "dev_id"
- },
- "insertion_order": null,
- "remote_table": {
- "name": "view_rule_with_owner",
+ "name": "rulebase_link",
"schema": "public"
}
}
@@ -4692,6 +5001,7 @@
"force_initial_import",
"hide_in_gui",
"dev_name",
+ "dev_uid",
"global_rulebase_name",
"global_rulebase_uid",
"local_rulebase_name",
@@ -4719,6 +5029,7 @@
"force_initial_import",
"hide_in_gui",
"dev_name",
+ "dev_uid",
"global_rulebase_name",
"global_rulebase_uid",
"local_rulebase_name",
@@ -4753,24 +5064,25 @@
"role": "auditor",
"permission": {
"columns": [
- "dev_id",
- "mgm_id",
- "dev_name",
- "dev_typ_id",
+ "clearing_import_ran",
"dev_active",
- "dev_comment",
- "dev_create",
- "dev_update",
"do_not_import",
- "clearing_import_ran",
"force_initial_import",
"hide_in_gui",
+ "dev_name",
+ "dev_uid",
"global_rulebase_name",
"global_rulebase_uid",
+ "local_rulebase_name",
+ "local_rulebase_uid",
"package_name",
"package_uid",
- "local_rulebase_uid",
- "local_rulebase_name"
+ "dev_id",
+ "dev_typ_id",
+ "mgm_id",
+ "dev_comment",
+ "dev_create",
+ "dev_update"
],
"filter": {},
"allow_aggregations": true
@@ -4813,6 +5125,7 @@
"force_initial_import",
"hide_in_gui",
"dev_name",
+ "dev_uid",
"global_rulebase_name",
"global_rulebase_uid",
"local_rulebase_name",
@@ -4847,24 +5160,25 @@
"role": "importer",
"permission": {
"columns": [
- "dev_id",
- "mgm_id",
- "dev_name",
- "dev_typ_id",
+ "clearing_import_ran",
"dev_active",
- "dev_comment",
- "dev_create",
- "dev_update",
"do_not_import",
- "clearing_import_ran",
"force_initial_import",
"hide_in_gui",
+ "dev_name",
+ "dev_uid",
"global_rulebase_name",
"global_rulebase_uid",
+ "local_rulebase_name",
+ "local_rulebase_uid",
"package_name",
"package_uid",
- "local_rulebase_uid",
- "local_rulebase_name"
+ "dev_id",
+ "dev_typ_id",
+ "mgm_id",
+ "dev_comment",
+ "dev_create",
+ "dev_update"
],
"filter": {},
"allow_aggregations": true
@@ -4874,24 +5188,25 @@
"role": "middleware-server",
"permission": {
"columns": [
- "dev_id",
- "mgm_id",
+ "clearing_import_ran",
+ "dev_active",
+ "do_not_import",
+ "force_initial_import",
+ "hide_in_gui",
"dev_name",
- "local_rulebase_name",
- "local_rulebase_uid",
+ "dev_uid",
"global_rulebase_name",
"global_rulebase_uid",
+ "local_rulebase_name",
+ "local_rulebase_uid",
"package_name",
"package_uid",
+ "dev_id",
"dev_typ_id",
- "dev_active",
+ "mgm_id",
"dev_comment",
"dev_create",
- "dev_update",
- "do_not_import",
- "clearing_import_ran",
- "force_initial_import",
- "hide_in_gui"
+ "dev_update"
],
"filter": {}
}
@@ -4906,6 +5221,7 @@
"force_initial_import",
"hide_in_gui",
"dev_name",
+ "dev_uid",
"global_rulebase_name",
"global_rulebase_uid",
"local_rulebase_name",
@@ -4946,6 +5262,7 @@
"force_initial_import",
"hide_in_gui",
"dev_name",
+ "dev_uid",
"global_rulebase_name",
"global_rulebase_uid",
"local_rulebase_name",
@@ -4986,6 +5303,7 @@
"force_initial_import",
"hide_in_gui",
"dev_name",
+ "dev_uid",
"global_rulebase_name",
"global_rulebase_uid",
"local_rulebase_name",
@@ -5026,6 +5344,7 @@
"force_initial_import",
"hide_in_gui",
"dev_name",
+ "dev_uid",
"global_rulebase_name",
"global_rulebase_uid",
"local_rulebase_name",
@@ -5060,24 +5379,25 @@
"role": "reporter-viewall",
"permission": {
"columns": [
- "dev_id",
- "mgm_id",
- "dev_name",
- "dev_typ_id",
+ "clearing_import_ran",
"dev_active",
- "dev_comment",
- "dev_create",
- "dev_update",
"do_not_import",
- "clearing_import_ran",
"force_initial_import",
"hide_in_gui",
+ "dev_name",
+ "dev_uid",
"global_rulebase_name",
"global_rulebase_uid",
+ "local_rulebase_name",
+ "local_rulebase_uid",
"package_name",
"package_uid",
- "local_rulebase_uid",
- "local_rulebase_name"
+ "dev_id",
+ "dev_typ_id",
+ "mgm_id",
+ "dev_comment",
+ "dev_create",
+ "dev_update"
],
"filter": {},
"allow_aggregations": true
@@ -5093,6 +5413,7 @@
"force_initial_import",
"hide_in_gui",
"dev_name",
+ "dev_uid",
"global_rulebase_name",
"global_rulebase_uid",
"local_rulebase_name",
@@ -5133,6 +5454,7 @@
"force_initial_import",
"hide_in_gui",
"dev_name",
+ "dev_uid",
"global_rulebase_name",
"global_rulebase_uid",
"local_rulebase_name",
@@ -5175,6 +5497,7 @@
"force_initial_import",
"hide_in_gui",
"dev_name",
+ "dev_uid",
"global_rulebase_name",
"global_rulebase_uid",
"local_rulebase_name",
@@ -5720,11 +6043,11 @@
"import_id",
"debug_mode",
"start_import_flag",
- "chunk_number",
"mgm_id",
"config"
]
- }
+ },
+ "comment": ""
}
],
"select_permissions": [
@@ -5735,13 +6058,13 @@
"import_id",
"debug_mode",
"start_import_flag",
- "chunk_number",
"mgm_id",
"config"
],
"filter": {},
"allow_aggregations": true
- }
+ },
+ "comment": ""
}
],
"delete_permissions": [
@@ -6007,6 +6330,18 @@
}
}
},
+ {
+ "name": "ruleEnforcedOnGatewaysByRemoved",
+ "using": {
+ "foreign_key_constraint_on": {
+ "column": "removed",
+ "table": {
+ "name": "rule_enforced_on_gateway",
+ "schema": "public"
+ }
+ }
+ }
+ },
{
"name": "ruleFromsByRfLastSeen",
"using": {
@@ -6079,6 +6414,18 @@
}
}
},
+ {
+ "name": "rule_enforced_on_gateways",
+ "using": {
+ "foreign_key_constraint_on": {
+ "column": "created",
+ "table": {
+ "name": "rule_enforced_on_gateway",
+ "schema": "public"
+ }
+ }
+ }
+ },
{
"name": "rule_froms",
"using": {
@@ -6151,6 +6498,30 @@
}
}
},
+ {
+ "name": "rulebaseLinksByRemoved",
+ "using": {
+ "foreign_key_constraint_on": {
+ "column": "removed",
+ "table": {
+ "name": "rulebase_link",
+ "schema": "public"
+ }
+ }
+ }
+ },
+ {
+ "name": "rulebase_links",
+ "using": {
+ "foreign_key_constraint_on": {
+ "column": "created",
+ "table": {
+ "name": "rulebase_link",
+ "schema": "public"
+ }
+ }
+ }
+ },
{
"name": "rules",
"using": {
@@ -6351,6 +6722,7 @@
"check": {},
"columns": [
"control_id",
+ "is_full_import",
"any_changes_found",
"is_initial_import",
"notification_done",
@@ -6426,9 +6798,10 @@
"permission": {
"columns": [
"control_id",
- "any_changes_found",
+ "is_full_import",
"is_initial_import",
"notification_done",
+ "any_changes_found",
"rule_changes_found",
"successful_import",
"delimiter_group",
@@ -6590,9 +6963,10 @@
"permission": {
"columns": [
"control_id",
- "any_changes_found",
+ "is_full_import",
"is_initial_import",
"notification_done",
+ "any_changes_found",
"rule_changes_found",
"successful_import",
"delimiter_group",
@@ -7165,6 +7539,18 @@
"schema": "public"
},
"array_relationships": [
+ {
+ "name": "customtxts",
+ "using": {
+ "foreign_key_constraint_on": {
+ "column": "language",
+ "table": {
+ "name": "customtxt",
+ "schema": "public"
+ }
+ }
+ }
+ },
{
"name": "txts",
"using": {
@@ -7327,21 +7713,65 @@
},
{
"table": {
- "name": "ldap_connection",
+ "name": "latest_config",
"schema": "public"
},
- "object_relationships": [
+ "insert_permissions": [
{
- "name": "tenant",
- "using": {
- "foreign_key_constraint_on": "tenant_id"
- }
+ "role": "importer",
+ "permission": {
+ "check": {},
+ "columns": [
+ "import_id",
+ "mgm_id",
+ "config"
+ ]
+ },
+ "comment": ""
}
],
- "array_relationships": [
+ "select_permissions": [
{
- "name": "uiusers",
- "using": {
+ "role": "importer",
+ "permission": {
+ "columns": [
+ "import_id",
+ "mgm_id",
+ "config"
+ ],
+ "filter": {},
+ "allow_aggregations": true
+ },
+ "comment": ""
+ }
+ ],
+ "delete_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "filter": {}
+ },
+ "comment": ""
+ }
+ ]
+ },
+ {
+ "table": {
+ "name": "ldap_connection",
+ "schema": "public"
+ },
+ "object_relationships": [
+ {
+ "name": "tenant",
+ "using": {
+ "foreign_key_constraint_on": "tenant_id"
+ }
+ }
+ ],
+ "array_relationships": [
+ {
+ "name": "uiusers",
+ "using": {
"foreign_key_constraint_on": {
"column": "ldap_connection_id",
"table": {
@@ -8404,6 +8834,18 @@
}
}
},
+ {
+ "name": "rule_metadata",
+ "using": {
+ "foreign_key_constraint_on": {
+ "column": "mgm_id",
+ "table": {
+ "name": "rule_metadata",
+ "schema": "public"
+ }
+ }
+ }
+ },
{
"name": "rule_nwobj_resolveds",
"using": {
@@ -8440,6 +8882,18 @@
}
}
},
+ {
+ "name": "rulebases",
+ "using": {
+ "foreign_key_constraint_on": {
+ "column": "mgm_id",
+ "table": {
+ "name": "rulebase",
+ "schema": "public"
+ }
+ }
+ }
+ },
{
"name": "rules",
"using": {
@@ -8586,8 +9040,10 @@
"domain_uid",
"ext_mgm_data",
"hide_in_gui",
+ "is_super_manager",
"mgm_id",
"mgm_name",
+ "mgm_uid",
"multi_device_manager_id"
],
"filter": {
@@ -8603,29 +9059,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": {},
"allow_aggregations": true
@@ -8636,29 +9096,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": {},
"allow_aggregations": true
@@ -8674,8 +9138,10 @@
"domain_uid",
"ext_mgm_data",
"hide_in_gui",
+ "is_super_manager",
"mgm_id",
"mgm_name",
+ "mgm_uid",
"multi_device_manager_id"
],
"filter": {
@@ -8691,29 +9157,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": {},
"allow_aggregations": true
@@ -8724,29 +9194,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": {},
"allow_aggregations": true
@@ -8762,8 +9236,10 @@
"domain_uid",
"ext_mgm_data",
"hide_in_gui",
+ "is_super_manager",
"mgm_id",
"mgm_name",
+ "mgm_uid",
"multi_device_manager_id"
],
"filter": {
@@ -8784,8 +9260,10 @@
"domain_uid",
"ext_mgm_data",
"hide_in_gui",
+ "is_super_manager",
"mgm_id",
"mgm_name",
+ "mgm_uid",
"multi_device_manager_id"
],
"filter": {
@@ -8801,29 +9279,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": {
@@ -8842,9 +9324,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": {
@@ -8864,8 +9350,10 @@
"domain_uid",
"ext_mgm_data",
"hide_in_gui",
+ "is_super_manager",
"mgm_id",
"mgm_name",
+ "mgm_uid",
"multi_device_manager_id"
],
"filter": {},
@@ -8881,8 +9369,10 @@
"domain_uid",
"ext_mgm_data",
"hide_in_gui",
+ "is_super_manager",
"mgm_id",
"mgm_name",
+ "mgm_uid",
"multi_device_manager_id"
],
"filter": {
@@ -8903,8 +9393,10 @@
"domain_uid",
"ext_mgm_data",
"hide_in_gui",
+ "is_super_manager",
"mgm_id",
"mgm_name",
+ "mgm_uid",
"multi_device_manager_id"
],
"filter": {
@@ -8922,29 +9414,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": {},
"check": {}
@@ -9239,31 +9735,73 @@
}
}
],
- "select_permissions": [
+ "insert_permissions": [
{
- "role": "auditor",
+ "role": "importer",
"permission": {
+ "check": {},
"columns": [
+ "obj_create",
"obj_id",
- "last_change_admin",
- "zone_id",
- "mgm_id",
- "obj_name",
+ "obj_last_seen",
+ "removed",
+ "active",
+ "initial_config",
+ "obj_nat",
"obj_comment",
- "obj_uid",
- "obj_typ_id",
"obj_location",
- "obj_member_names",
- "obj_member_refs",
- "initial_config",
+ "obj_name",
"obj_sw",
+ "obj_sys_contact",
+ "obj_sys_location",
+ "obj_sys_name",
+ "obj_sys_readcom",
+ "obj_sys_writecom",
"obj_ip",
"obj_ip_end",
- "obj_nat",
- "nattyp_id",
"obj_nat_ip",
"obj_nat_ip_end",
- "obj_nat_install",
+ "last_change_admin",
+ "mgm_id",
+ "nattyp_id",
+ "obj_color_id",
+ "obj_nat_install",
+ "obj_typ_id",
+ "zone_id",
+ "obj_member_names",
+ "obj_member_refs",
+ "obj_sys_desc",
+ "obj_uid"
+ ]
+ },
+ "comment": ""
+ }
+ ],
+ "select_permissions": [
+ {
+ "role": "auditor",
+ "permission": {
+ "columns": [
+ "obj_id",
+ "last_change_admin",
+ "zone_id",
+ "mgm_id",
+ "obj_name",
+ "obj_comment",
+ "obj_uid",
+ "obj_typ_id",
+ "obj_location",
+ "obj_member_names",
+ "obj_member_refs",
+ "initial_config",
+ "obj_sw",
+ "obj_ip",
+ "obj_ip_end",
+ "obj_nat",
+ "nattyp_id",
+ "obj_nat_ip",
+ "obj_nat_ip_end",
+ "obj_nat_install",
"obj_color_id",
"obj_sys_name",
"obj_sys_location",
@@ -9273,7 +9811,8 @@
"obj_sys_writecom",
"active",
"obj_create",
- "obj_last_seen"
+ "obj_last_seen",
+ "removed"
],
"filter": {},
"allow_aggregations": true
@@ -9286,6 +9825,7 @@
"obj_create",
"obj_id",
"obj_last_seen",
+ "removed",
"active",
"initial_config",
"obj_nat",
@@ -9318,6 +9858,46 @@
"allow_aggregations": true
}
},
+ {
+ "role": "importer",
+ "permission": {
+ "columns": [
+ "obj_create",
+ "obj_id",
+ "obj_last_seen",
+ "removed",
+ "active",
+ "initial_config",
+ "obj_nat",
+ "obj_comment",
+ "obj_location",
+ "obj_name",
+ "obj_sw",
+ "obj_sys_contact",
+ "obj_sys_location",
+ "obj_sys_name",
+ "obj_sys_readcom",
+ "obj_sys_writecom",
+ "obj_ip",
+ "obj_ip_end",
+ "obj_nat_ip",
+ "obj_nat_ip_end",
+ "last_change_admin",
+ "mgm_id",
+ "nattyp_id",
+ "obj_color_id",
+ "obj_nat_install",
+ "obj_typ_id",
+ "zone_id",
+ "obj_member_names",
+ "obj_member_refs",
+ "obj_sys_desc",
+ "obj_uid"
+ ],
+ "filter": {}
+ },
+ "comment": ""
+ },
{
"role": "middleware-server",
"permission": {
@@ -9351,7 +9931,8 @@
"obj_sys_writecom",
"active",
"obj_create",
- "obj_last_seen"
+ "obj_last_seen",
+ "removed"
],
"filter": {},
"allow_aggregations": true
@@ -9390,7 +9971,8 @@
"obj_sys_writecom",
"active",
"obj_create",
- "obj_last_seen"
+ "obj_last_seen",
+ "removed"
],
"filter": {
"mgm_id": {
@@ -9433,7 +10015,8 @@
"obj_sys_writecom",
"active",
"obj_create",
- "obj_last_seen"
+ "obj_last_seen",
+ "removed"
],
"filter": {
"mgm_id": {
@@ -9476,7 +10059,8 @@
"obj_sys_writecom",
"active",
"obj_create",
- "obj_last_seen"
+ "obj_last_seen",
+ "removed"
],
"filter": {
"mgm_id": {
@@ -9519,12 +10103,65 @@
"obj_sys_writecom",
"active",
"obj_create",
- "obj_last_seen"
+ "obj_last_seen",
+ "removed"
],
"filter": {},
"allow_aggregations": true
}
}
+ ],
+ "update_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "columns": [
+ "obj_create",
+ "obj_id",
+ "obj_last_seen",
+ "removed",
+ "active",
+ "initial_config",
+ "obj_nat",
+ "obj_comment",
+ "obj_location",
+ "obj_name",
+ "obj_sw",
+ "obj_sys_contact",
+ "obj_sys_location",
+ "obj_sys_name",
+ "obj_sys_readcom",
+ "obj_sys_writecom",
+ "obj_ip",
+ "obj_ip_end",
+ "obj_nat_ip",
+ "obj_nat_ip_end",
+ "last_change_admin",
+ "mgm_id",
+ "nattyp_id",
+ "obj_color_id",
+ "obj_nat_install",
+ "obj_typ_id",
+ "zone_id",
+ "obj_member_names",
+ "obj_member_refs",
+ "obj_sys_desc",
+ "obj_uid"
+ ],
+ "filter": {},
+ "check": {}
+ },
+ "comment": ""
+ }
+ ],
+ "delete_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "filter": {}
+ },
+ "comment": ""
+ }
]
},
{
@@ -9558,6 +10195,24 @@
}
}
],
+ "insert_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "check": {},
+ "columns": [
+ "import_created",
+ "import_last_seen",
+ "objgrp_id",
+ "objgrp_member_id",
+ "removed",
+ "active",
+ "negated"
+ ]
+ },
+ "comment": ""
+ }
+ ],
"select_permissions": [
{
"role": "auditor",
@@ -9567,6 +10222,7 @@
"objgrp_member_id",
"import_created",
"import_last_seen",
+ "removed",
"active",
"negated"
],
@@ -9581,6 +10237,7 @@
"import_created",
"import_last_seen",
"objgrp_id",
+ "removed",
"objgrp_member_id",
"active",
"negated"
@@ -9588,6 +10245,22 @@
"filter": {}
}
},
+ {
+ "role": "importer",
+ "permission": {
+ "columns": [
+ "import_created",
+ "import_last_seen",
+ "objgrp_id",
+ "objgrp_member_id",
+ "removed",
+ "active",
+ "negated"
+ ],
+ "filter": {}
+ },
+ "comment": ""
+ },
{
"role": "middleware-server",
"permission": {
@@ -9596,6 +10269,7 @@
"import_last_seen",
"objgrp_id",
"objgrp_member_id",
+ "removed",
"active",
"negated"
],
@@ -9611,6 +10285,7 @@
"objgrp_member_id",
"import_created",
"import_last_seen",
+ "removed",
"active",
"negated"
],
@@ -9626,6 +10301,7 @@
"objgrp_member_id",
"import_created",
"import_last_seen",
+ "removed",
"active",
"negated"
],
@@ -9641,6 +10317,7 @@
"objgrp_member_id",
"import_created",
"import_last_seen",
+ "removed",
"active",
"negated"
],
@@ -9656,12 +10333,41 @@
"objgrp_member_id",
"import_created",
"import_last_seen",
+ "removed",
"active",
"negated"
],
"filter": {}
}
}
+ ],
+ "update_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "columns": [
+ "import_created",
+ "import_last_seen",
+ "objgrp_id",
+ "objgrp_member_id",
+ "removed",
+ "active",
+ "negated"
+ ],
+ "filter": {},
+ "check": {}
+ },
+ "comment": ""
+ }
+ ],
+ "delete_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "filter": {}
+ },
+ "comment": ""
+ }
]
},
{
@@ -9695,6 +10401,24 @@
}
}
],
+ "insert_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "check": {},
+ "columns": [
+ "import_created",
+ "import_last_seen",
+ "objgrp_flat_id",
+ "objgrp_flat_member_id",
+ "removed",
+ "active",
+ "negated"
+ ]
+ },
+ "comment": ""
+ }
+ ],
"select_permissions": [
{
"role": "auditor",
@@ -9705,6 +10429,7 @@
"active",
"import_created",
"import_last_seen",
+ "removed",
"negated"
],
"filter": {},
@@ -9717,6 +10442,7 @@
"columns": [
"import_created",
"import_last_seen",
+ "removed",
"objgrp_flat_id",
"objgrp_flat_member_id",
"active",
@@ -9725,6 +10451,22 @@
"filter": {}
}
},
+ {
+ "role": "importer",
+ "permission": {
+ "columns": [
+ "import_created",
+ "import_last_seen",
+ "objgrp_flat_id",
+ "objgrp_flat_member_id",
+ "removed",
+ "active",
+ "negated"
+ ],
+ "filter": {}
+ },
+ "comment": ""
+ },
{
"role": "middleware-server",
"permission": {
@@ -9733,6 +10475,7 @@
"import_last_seen",
"objgrp_flat_id",
"objgrp_flat_member_id",
+ "removed",
"active",
"negated"
],
@@ -9746,6 +10489,7 @@
"columns": [
"objgrp_flat_id",
"objgrp_flat_member_id",
+ "removed",
"active",
"import_created",
"import_last_seen",
@@ -9761,6 +10505,7 @@
"columns": [
"objgrp_flat_id",
"objgrp_flat_member_id",
+ "removed",
"active",
"import_created",
"import_last_seen",
@@ -9776,6 +10521,7 @@
"columns": [
"objgrp_flat_id",
"objgrp_flat_member_id",
+ "removed",
"active",
"import_created",
"import_last_seen",
@@ -9791,6 +10537,7 @@
"columns": [
"objgrp_flat_id",
"objgrp_flat_member_id",
+ "removed",
"active",
"import_created",
"import_last_seen",
@@ -9799,6 +10546,34 @@
"filter": {}
}
}
+ ],
+ "update_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "columns": [
+ "import_created",
+ "import_last_seen",
+ "objgrp_flat_id",
+ "objgrp_flat_member_id",
+ "removed",
+ "active",
+ "negated"
+ ],
+ "filter": {},
+ "check": {}
+ },
+ "comment": ""
+ }
+ ],
+ "delete_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "filter": {}
+ },
+ "comment": ""
+ }
]
},
{
@@ -9807,6 +10582,12 @@
"schema": "public"
},
"object_relationships": [
+ {
+ "name": "owner_lifecycle_state",
+ "using": {
+ "foreign_key_constraint_on": "owner_lifecycle_state_id"
+ }
+ },
{
"name": "tenant",
"using": {
@@ -10511,7 +11292,21 @@
"table": {
"name": "owner_lifecycle_state",
"schema": "public"
- }
+ },
+ "array_relationships": [
+ {
+ "name": "owners",
+ "using": {
+ "foreign_key_constraint_on": {
+ "column": "owner_lifecycle_state_id",
+ "table": {
+ "name": "owner",
+ "schema": "public"
+ }
+ }
+ }
+ }
+ ]
},
{
"table": {
@@ -13755,12 +14550,6 @@
"schema": "public"
},
"object_relationships": [
- {
- "name": "device",
- "using": {
- "foreign_key_constraint_on": "dev_id"
- }
- },
{
"name": "importControlByRuleLastSeen",
"using": {
@@ -13800,17 +14589,13 @@
{
"name": "rule_metadatum",
"using": {
- "manual_configuration": {
- "column_mapping": {
- "dev_id": "dev_id",
- "rule_uid": "rule_uid"
- },
- "insertion_order": null,
- "remote_table": {
- "name": "rule_metadata",
- "schema": "public"
- }
- }
+ "foreign_key_constraint_on": "rule_uid"
+ }
+ },
+ {
+ "name": "rulebase",
+ "using": {
+ "foreign_key_constraint_on": "rulebase_id"
}
},
{
@@ -13869,6 +14654,57 @@
}
}
},
+ {
+ "name": "compliance_violations",
+ "using": {
+ "foreign_key_constraint_on": {
+ "column": "rule_id",
+ "table": {
+ "name": "violation",
+ "schema": "compliance"
+ }
+ }
+ }
+ },
+ {
+ "name": "compliance_violations_version_agnostic",
+ "using": {
+ "manual_configuration": {
+ "column_mapping": {
+ "rule_uid": "rule_uid"
+ },
+ "insertion_order": null,
+ "remote_table": {
+ "name": "violation",
+ "schema": "compliance"
+ }
+ }
+ }
+ },
+ {
+ "name": "rule_enforced_on_gateways",
+ "using": {
+ "foreign_key_constraint_on": {
+ "column": "rule_id",
+ "table": {
+ "name": "rule_enforced_on_gateway",
+ "schema": "public"
+ }
+ }
+ }
+ },
+ {
+ "name": "rule_from_zones",
+ "using": {
+ "foreign_key_constraint_on": {
+ "column": "rule_id",
+ "table": {
+ "name": "rule_from_zone",
+ "schema": "public"
+ }
+ }
+ }
+ },
{
"name": "rule_froms",
"using": {
@@ -13917,6 +14753,18 @@
}
}
},
+ {
+ "name": "rule_to_zones",
+ "using": {
+ "foreign_key_constraint_on": {
+ "column": "rule_id",
+ "table": {
+ "name": "rule_to_zone",
+ "schema": "public"
+ }
+ }
+ }
+ },
{
"name": "rule_tos",
"using": {
@@ -13941,6 +14789,18 @@
}
}
},
+ {
+ "name": "rulebase_links",
+ "using": {
+ "foreign_key_constraint_on": {
+ "column": "from_rule_id",
+ "table": {
+ "name": "rulebase_link",
+ "schema": "public"
+ }
+ }
+ }
+ },
{
"name": "rules",
"using": {
@@ -13998,18 +14858,73 @@
}
}
],
+ "insert_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "check": {},
+ "columns": [
+ "access_rule",
+ "action_id",
+ "active",
+ "dev_id",
+ "is_global",
+ "last_change_admin",
+ "mgm_id",
+ "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",
+ "rulebase_id",
+ "track_id",
+ "xlate_rule"
+ ]
+ },
+ "comment": ""
+ }
+ ],
"select_permissions": [
{
"role": "auditor",
"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",
@@ -14024,6 +14939,7 @@
"dev_id",
"last_change_admin",
"mgm_id",
+ "rulebase_id",
"rule_from_zone",
"rule_num",
"rule_to_zone",
@@ -14055,12 +14971,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",
@@ -14075,6 +14993,7 @@
"dev_id",
"last_change_admin",
"mgm_id",
+ "rulebase_id",
"rule_from_zone",
"rule_num",
"rule_to_zone",
@@ -14102,16 +15021,18 @@
}
},
{
- "role": "middleware-server",
+ "role": "importer",
"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",
@@ -14126,6 +15047,7 @@
"dev_id",
"last_change_admin",
"mgm_id",
+ "rulebase_id",
"rule_from_zone",
"rule_num",
"rule_to_zone",
@@ -14148,21 +15070,23 @@
"computed_fields": [
"rule_relevant_for_tenant"
],
- "filter": {},
- "allow_aggregations": true
- }
+ "filter": {}
+ },
+ "comment": ""
},
{
- "role": "modeller",
+ "role": "middleware-server",
"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",
@@ -14177,6 +15101,7 @@
"dev_id",
"last_change_admin",
"mgm_id",
+ "rulebase_id",
"rule_from_zone",
"rule_num",
"rule_to_zone",
@@ -14199,39 +15124,23 @@
"computed_fields": [
"rule_relevant_for_tenant"
],
- "filter": {
- "_and": [
- {
- "mgm_id": {
- "_in": "x-hasura-visible-managements"
- }
- },
- {
- "dev_id": {
- "_in": "x-hasura-visible-devices"
- }
- },
- {
- "rule_relevant_for_tenant": {
- "_eq": "true"
- }
- }
- ]
- },
+ "filter": {},
"allow_aggregations": true
}
},
{
- "role": "recertifier",
+ "role": "modeller",
"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",
@@ -14246,6 +15155,7 @@
"dev_id",
"last_change_admin",
"mgm_id",
+ "rulebase_id",
"rule_from_zone",
"rule_num",
"rule_to_zone",
@@ -14275,11 +15185,6 @@
"_in": "x-hasura-visible-managements"
}
},
- {
- "dev_id": {
- "_in": "x-hasura-visible-devices"
- }
- },
{
"rule_relevant_for_tenant": {
"_eq": "true"
@@ -14291,22 +15196,91 @@
}
},
{
- "role": "reporter",
+ "role": "recertifier",
"permission": {
"columns": [
- "parent_rule_id",
- "rule_create",
- "rule_id",
- "rule_last_seen",
- "xlate_rule",
"access_rule",
+ "action_id",
"active",
+ "dev_id",
+ "is_global",
+ "last_change_admin",
+ "mgm_id",
"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_src_neg",
- "rule_svc_neg",
+ "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"
+ ],
+ "computed_fields": [
+ "rule_relevant_for_tenant"
+ ],
+ "filter": {
+ "_and": [
+ {
+ "mgm_id": {
+ "_in": "x-hasura-visible-managements"
+ }
+ },
+ {
+ "rule_relevant_for_tenant": {
+ "_eq": "true"
+ }
+ }
+ ]
+ },
+ "allow_aggregations": true
+ }
+ },
+ {
+ "role": "reporter",
+ "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",
+ "rule_implied",
+ "rule_src_neg",
+ "rule_svc_neg",
"rule_installon",
"rule_name",
"rule_ruleid",
@@ -14315,6 +15289,7 @@
"dev_id",
"last_change_admin",
"mgm_id",
+ "rulebase_id",
"rule_from_zone",
"rule_num",
"rule_to_zone",
@@ -14344,11 +15319,6 @@
"_in": "x-hasura-visible-managements"
}
},
- {
- "dev_id": {
- "_in": "x-hasura-visible-devices"
- }
- },
{
"rule_relevant_for_tenant": {
"_eq": "true"
@@ -14364,12 +15334,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",
@@ -14384,6 +15356,7 @@
"dev_id",
"last_change_admin",
"mgm_id",
+ "rulebase_id",
"rule_from_zone",
"rule_num",
"rule_to_zone",
@@ -14410,30 +15383,55 @@
"allow_aggregations": true
}
}
+ ],
+ "update_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "columns": [
+ "active",
+ "removed",
+ "rule_last_seen",
+ "rule_num_numeric"
+ ],
+ "filter": {},
+ "check": {}
+ },
+ "comment": ""
+ }
+ ],
+ "delete_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "filter": {}
+ },
+ "comment": ""
+ }
]
},
{
"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"
}
},
{
@@ -14441,24 +15439,21 @@
"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": {
- "name": "rule_from_relevant_for_tenant",
- "schema": "public"
- },
- "session_argument": "hasura_session"
- }
+ "role": "importer",
+ "permission": {
+ "check": {},
+ "columns": [
+ "created",
+ "removed",
+ "dev_id",
+ "rule_id"
+ ]
+ },
+ "comment": ""
}
],
"select_permissions": [
@@ -14466,271 +15461,194 @@
"role": "auditor",
"permission": {
"columns": [
- "obj_id",
- "rf_create",
- "rf_last_seen",
- "rule_from_id",
- "rule_id",
- "user_id",
- "active",
- "negated"
- ],
- "computed_fields": [
- "rule_from_relevant_for_tenant"
+ "created",
+ "removed",
+ "dev_id",
+ "rule_id"
],
- "filter": {},
- "allow_aggregations": true
- }
+ "filter": {}
+ },
+ "comment": ""
},
{
"role": "fw-admin",
"permission": {
"columns": [
- "obj_id",
- "rf_create",
- "rf_last_seen",
- "rule_from_id",
- "rule_id",
- "user_id",
- "active",
- "negated"
- ],
- "computed_fields": [
- "rule_from_relevant_for_tenant"
+ "created",
+ "removed",
+ "dev_id",
+ "rule_id"
],
"filter": {},
"allow_aggregations": true
- }
+ },
+ "comment": ""
},
{
- "role": "middleware-server",
+ "role": "importer",
"permission": {
"columns": [
- "obj_id",
- "rf_create",
- "rf_last_seen",
- "rule_from_id",
- "rule_id",
- "user_id",
- "active",
- "negated"
+ "created",
+ "removed",
+ "dev_id",
+ "rule_id"
],
"filter": {}
},
"comment": ""
},
{
- "role": "modeller",
+ "role": "middleware-server",
"permission": {
"columns": [
- "obj_id",
- "rf_create",
- "rf_last_seen",
- "rule_from_id",
- "rule_id",
- "user_id",
- "active",
- "negated"
+ "created",
+ "removed",
+ "dev_id",
+ "rule_id"
],
- "computed_fields": [
- "rule_from_relevant_for_tenant"
+ "filter": {},
+ "allow_aggregations": true
+ },
+ "comment": ""
+ },
+ {
+ "role": "modeller",
+ "permission": {
+ "columns": [
+ "created",
+ "removed",
+ "dev_id",
+ "rule_id"
],
"filter": {
- "_and": [
- {
- "rule": {
- "mgm_id": {
- "_in": "x-hasura-visible-managements"
- }
- }
- },
- {
- "rule": {
- "dev_id": {
- "_in": "x-hasura-visible-devices"
- }
- }
- },
- {
- "rule_from_relevant_for_tenant": {
- "_eq": "true"
- }
- }
- ]
- },
- "allow_aggregations": true
- }
+ "dev_id": {
+ "_in": "x-hasura-visible-devices"
+ }
+ }
+ },
+ "comment": ""
},
{
"role": "recertifier",
"permission": {
"columns": [
- "obj_id",
- "rf_create",
- "rf_last_seen",
- "rule_from_id",
- "rule_id",
- "user_id",
- "active",
- "negated"
- ],
- "computed_fields": [
- "rule_from_relevant_for_tenant"
+ "created",
+ "removed",
+ "dev_id",
+ "rule_id"
],
"filter": {
- "_and": [
- {
- "rule": {
- "mgm_id": {
- "_in": "x-hasura-visible-managements"
- }
- }
- },
- {
- "rule": {
- "dev_id": {
- "_in": "x-hasura-visible-devices"
- }
- }
- },
- {
- "rule_from_relevant_for_tenant": {
- "_eq": "true"
- }
- }
- ]
- },
- "allow_aggregations": true
- }
+ "dev_id": {
+ "_in": "x-hasura-visible-devices"
+ }
+ }
+ },
+ "comment": ""
},
{
"role": "reporter",
"permission": {
"columns": [
- "obj_id",
- "rf_create",
- "rf_last_seen",
- "rule_from_id",
- "rule_id",
- "user_id",
- "active",
- "negated"
- ],
- "computed_fields": [
- "rule_from_relevant_for_tenant"
+ "created",
+ "removed",
+ "dev_id",
+ "rule_id"
],
"filter": {
- "_and": [
- {
- "rule": {
- "mgm_id": {
- "_in": "x-hasura-visible-managements"
- }
- }
- },
- {
- "rule": {
- "dev_id": {
- "_in": "x-hasura-visible-devices"
- }
- }
- },
- {
- "rule_from_relevant_for_tenant": {
- "_eq": "true"
- }
- }
- ]
- },
- "allow_aggregations": true
- }
+ "dev_id": {
+ "_in": "x-hasura-visible-devices"
+ }
+ }
+ },
+ "comment": ""
},
{
"role": "reporter-viewall",
"permission": {
"columns": [
- "obj_id",
- "rf_create",
- "rf_last_seen",
- "rule_from_id",
- "rule_id",
- "user_id",
- "active",
- "negated"
- ],
- "computed_fields": [
- "rule_from_relevant_for_tenant"
+ "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_metadata",
+ "name": "rule_from",
"schema": "public"
},
"object_relationships": [
{
- "name": "device",
+ "name": "importControlByRfLastSeen",
"using": {
- "foreign_key_constraint_on": "dev_id"
+ "foreign_key_constraint_on": "rf_last_seen"
}
},
{
- "name": "uiuser",
+ "name": "import_control",
"using": {
- "foreign_key_constraint_on": "rule_owner"
+ "foreign_key_constraint_on": "rf_create"
}
},
{
- "name": "uiuserByRuleLastCertifier",
+ "name": "object",
"using": {
- "foreign_key_constraint_on": "rule_last_certifier"
+ "foreign_key_constraint_on": "obj_id"
}
- }
- ],
- "array_relationships": [
+ },
{
- "name": "recertifications",
+ "name": "rule",
"using": {
- "foreign_key_constraint_on": {
- "column": "rule_metadata_id",
- "table": {
- "name": "recertification",
- "schema": "public"
- }
- }
+ "foreign_key_constraint_on": "rule_id"
}
},
{
- "name": "rule_owners",
+ "name": "usr",
"using": {
- "foreign_key_constraint_on": {
- "column": "rule_metadata_id",
- "table": {
- "name": "rule_owner",
- "schema": "public"
- }
- }
+ "foreign_key_constraint_on": "user_id"
}
- },
+ }
+ ],
+ "computed_fields": [
{
- "name": "rules",
- "using": {
- "manual_configuration": {
- "column_mapping": {
- "dev_id": "dev_id",
- "rule_uid": "rule_uid"
- },
- "insertion_order": null,
- "remote_table": {
- "name": "rule",
- "schema": "public"
- }
- }
+ "name": "rule_from_relevant_for_tenant",
+ "definition": {
+ "function": {
+ "name": "rule_from_relevant_for_tenant",
+ "schema": "public"
+ },
+ "session_argument": "hasura_session"
}
}
],
@@ -14740,25 +15658,18 @@
"permission": {
"check": {},
"columns": [
- "rule_to_be_removed",
- "dev_id",
- "last_change_admin",
- "rule_last_certifier",
- "rule_owner",
- "rule_hit_counter",
- "rule_metadata_id",
- "rule_uid",
- "rule_created",
- "rule_decert_date",
- "rule_first_hit",
- "rule_last_certified",
- "rule_last_hit",
- "rule_last_modified",
- "rule_last_certifier_dn",
- "rule_owner_dn",
- "rule_recertification_comment"
+ "obj_id",
+ "removed",
+ "rf_create",
+ "rf_last_seen",
+ "rule_from_id",
+ "rule_id",
+ "user_id",
+ "active",
+ "negated"
]
- }
+ },
+ "comment": ""
}
],
"select_permissions": [
@@ -14766,23 +15677,18 @@
"role": "auditor",
"permission": {
"columns": [
- "rule_metadata_id",
- "dev_id",
- "rule_uid",
- "rule_created",
- "rule_last_modified",
- "rule_first_hit",
- "rule_last_hit",
- "rule_hit_counter",
- "rule_last_certified",
- "rule_last_certifier",
- "rule_last_certifier_dn",
- "rule_owner",
- "rule_owner_dn",
- "rule_to_be_removed",
- "last_change_admin",
- "rule_decert_date",
- "rule_recertification_comment"
+ "active",
+ "negated",
+ "obj_id",
+ "removed",
+ "rf_create",
+ "rf_last_seen",
+ "rule_from_id",
+ "rule_id",
+ "user_id"
+ ],
+ "computed_fields": [
+ "rule_from_relevant_for_tenant"
],
"filter": {},
"allow_aggregations": true
@@ -14792,23 +15698,19 @@
"role": "fw-admin",
"permission": {
"columns": [
- "rule_hit_counter",
- "rule_metadata_id",
- "rule_to_be_removed",
- "rule_last_certifier_dn",
- "rule_owner_dn",
- "rule_recertification_comment",
- "dev_id",
- "last_change_admin",
- "rule_last_certifier",
- "rule_owner",
- "rule_uid",
- "rule_created",
- "rule_decert_date",
- "rule_first_hit",
- "rule_last_certified",
- "rule_last_hit",
- "rule_last_modified"
+ "active",
+ "negated",
+ "obj_id",
+ "removed",
+ "rf_create",
+ "rf_last_seen",
+ "removed",
+ "rule_from_id",
+ "rule_id",
+ "user_id"
+ ],
+ "computed_fields": [
+ "rule_from_relevant_for_tenant"
],
"filter": {},
"allow_aggregations": true
@@ -14818,107 +15720,1257 @@
"role": "importer",
"permission": {
"columns": [
- "rule_to_be_removed",
- "dev_id",
- "last_change_admin",
+ "obj_id",
+ "removed",
+ "rf_create",
+ "rf_last_seen",
+ "rule_from_id",
+ "rule_id",
+ "user_id",
+ "active",
+ "negated"
+ ],
+ "computed_fields": [
+ "rule_from_relevant_for_tenant"
+ ],
+ "filter": {},
+ "allow_aggregations": true
+ },
+ "comment": ""
+ },
+ {
+ "role": "middleware-server",
+ "permission": {
+ "columns": [
+ "active",
+ "negated",
+ "obj_id",
+ "removed",
+ "rf_create",
+ "rf_last_seen",
+ "rule_from_id",
+ "rule_id",
+ "user_id"
+ ],
+ "computed_fields": [
+ "rule_from_relevant_for_tenant"
+ ],
+ "filter": {}
+ },
+ "comment": ""
+ },
+ {
+ "role": "modeller",
+ "permission": {
+ "columns": [
+ "obj_id",
+ "removed",
+ "rf_create",
+ "rf_last_seen",
+ "rule_from_id",
+ "rule_id",
+ "user_id",
+ "active",
+ "negated"
+ ],
+ "computed_fields": [
+ "rule_from_relevant_for_tenant"
+ ],
+ "filter": {
+ "_and": [
+ {
+ "rule": {
+ "mgm_id": {
+ "_in": "x-hasura-visible-managements"
+ }
+ }
+ },
+ {
+ "rule_from_relevant_for_tenant": {
+ "_eq": "true"
+ }
+ }
+ ]
+ },
+ "allow_aggregations": true
+ }
+ },
+ {
+ "role": "recertifier",
+ "permission": {
+ "columns": [
+ "active",
+ "negated",
+ "obj_id",
+ "removed",
+ "rf_create",
+ "rf_last_seen",
+ "rule_from_id",
+ "rule_id",
+ "user_id"
+ ],
+ "computed_fields": [
+ "rule_from_relevant_for_tenant"
+ ],
+ "filter": {
+ "_and": [
+ {
+ "rule": {
+ "mgm_id": {
+ "_in": "x-hasura-visible-managements"
+ }
+ }
+ },
+ {
+ "rule_from_relevant_for_tenant": {
+ "_eq": "true"
+ }
+ }
+ ]
+ },
+ "allow_aggregations": true
+ }
+ },
+ {
+ "role": "reporter",
+ "permission": {
+ "columns": [
+ "obj_id",
+ "removed",
+ "rf_create",
+ "rf_last_seen",
+ "rule_from_id",
+ "rule_id",
+ "user_id",
+ "active",
+ "negated"
+ ],
+ "computed_fields": [
+ "rule_from_relevant_for_tenant"
+ ],
+ "filter": {
+ "_and": [
+ {
+ "rule": {
+ "mgm_id": {
+ "_in": "x-hasura-visible-managements"
+ }
+ }
+ },
+ {
+ "rule_from_relevant_for_tenant": {
+ "_eq": "true"
+ }
+ }
+ ]
+ },
+ "allow_aggregations": true
+ }
+ },
+ {
+ "role": "reporter-viewall",
+ "permission": {
+ "columns": [
+ "obj_id",
+ "removed",
+ "rf_create",
+ "rf_last_seen",
+ "rule_from_id",
+ "rule_id",
+ "user_id",
+ "active",
+ "negated"
+ ],
+ "computed_fields": [
+ "rule_from_relevant_for_tenant"
+ ],
+ "filter": {},
+ "allow_aggregations": true
+ }
+ }
+ ],
+ "update_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "columns": [
+ "obj_id",
+ "removed",
+ "rf_create",
+ "rf_last_seen",
+ "rule_from_id",
+ "rule_id",
+ "user_id",
+ "active",
+ "negated"
+ ],
+ "filter": {},
+ "check": {}
+ },
+ "comment": ""
+ }
+ ],
+ "delete_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "filter": {}
+ },
+ "comment": ""
+ }
+ ]
+ },
+ {
+ "table": {
+ "name": "rule_from_zone",
+ "schema": "public"
+ },
+ "object_relationships": [
+ {
+ "name": "rule",
+ "using": {
+ "foreign_key_constraint_on": "rule_id"
+ }
+ },
+ {
+ "name": "zone",
+ "using": {
+ "foreign_key_constraint_on": "zone_id"
+ }
+ }
+ ],
+ "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_metadata",
+ "schema": "public"
+ },
+ "object_relationships": [
+ {
+ "name": "management",
+ "using": {
+ "foreign_key_constraint_on": "mgm_id"
+ }
+ },
+ {
+ "name": "uiuser",
+ "using": {
+ "foreign_key_constraint_on": "rule_owner"
+ }
+ },
+ {
+ "name": "uiuserByRuleLastCertifier",
+ "using": {
+ "foreign_key_constraint_on": "rule_last_certifier"
+ }
+ }
+ ],
+ "array_relationships": [
+ {
+ "name": "recertifications",
+ "using": {
+ "foreign_key_constraint_on": {
+ "column": "rule_metadata_id",
+ "table": {
+ "name": "recertification",
+ "schema": "public"
+ }
+ }
+ }
+ },
+ {
+ "name": "rule_owners",
+ "using": {
+ "foreign_key_constraint_on": {
+ "column": "rule_metadata_id",
+ "table": {
+ "name": "rule_owner",
+ "schema": "public"
+ }
+ }
+ }
+ },
+ {
+ "name": "rules",
+ "using": {
+ "foreign_key_constraint_on": {
+ "column": "rule_uid",
+ "table": {
+ "name": "rule",
+ "schema": "public"
+ }
+ }
+ }
+ }
+ ],
+ "insert_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "check": {},
+ "columns": [
+ "last_change_admin",
+ "mgm_id",
+ "rule_created",
+ "rule_decert_date",
+ "rule_first_hit",
+ "rule_hit_counter",
+ "rule_last_certified",
"rule_last_certifier",
+ "rule_last_certifier_dn",
+ "rule_last_hit",
+ "rule_last_modified",
+ "rule_metadata_id",
"rule_owner",
+ "rule_owner_dn",
+ "rule_recertification_comment",
+ "rule_to_be_removed",
+ "rule_uid"
+ ]
+ }
+ }
+ ],
+ "select_permissions": [
+ {
+ "role": "auditor",
+ "permission": {
+ "columns": [
+ "last_change_admin",
+ "mgm_id",
+ "rule_created",
+ "rule_decert_date",
+ "rule_first_hit",
"rule_hit_counter",
+ "rule_last_certified",
+ "rule_last_certifier",
+ "rule_last_certifier_dn",
+ "rule_last_hit",
+ "rule_last_modified",
"rule_metadata_id",
- "rule_uid",
+ "rule_owner",
+ "rule_owner_dn",
+ "rule_recertification_comment",
+ "rule_to_be_removed",
+ "rule_uid"
+ ],
+ "filter": {},
+ "allow_aggregations": true
+ }
+ },
+ {
+ "role": "fw-admin",
+ "permission": {
+ "columns": [
+ "last_change_admin",
+ "mgm_id",
+ "rule_created",
+ "rule_decert_date",
+ "rule_first_hit",
+ "rule_hit_counter",
+ "rule_last_certified",
+ "rule_last_certifier",
+ "rule_last_certifier_dn",
+ "rule_last_hit",
+ "rule_last_modified",
+ "rule_metadata_id",
+ "rule_owner",
+ "rule_owner_dn",
+ "rule_recertification_comment",
+ "rule_to_be_removed",
+ "rule_uid"
+ ],
+ "filter": {},
+ "allow_aggregations": true
+ }
+ },
+ {
+ "role": "importer",
+ "permission": {
+ "columns": [
+ "last_change_admin",
+ "mgm_id",
+ "rule_created",
+ "rule_decert_date",
+ "rule_first_hit",
+ "rule_hit_counter",
+ "rule_last_certified",
+ "rule_last_certifier",
+ "rule_last_certifier_dn",
+ "rule_last_hit",
+ "rule_last_modified",
+ "rule_metadata_id",
+ "rule_owner",
+ "rule_owner_dn",
+ "rule_recertification_comment",
+ "rule_to_be_removed",
+ "rule_uid"
+ ],
+ "filter": {},
+ "allow_aggregations": true
+ }
+ },
+ {
+ "role": "middleware-server",
+ "permission": {
+ "columns": [
+ "last_change_admin",
+ "mgm_id",
+ "rule_created",
+ "rule_decert_date",
+ "rule_first_hit",
+ "rule_hit_counter",
+ "rule_last_certified",
+ "rule_last_certifier",
+ "rule_last_certifier_dn",
+ "rule_last_hit",
+ "rule_last_modified",
+ "rule_metadata_id",
+ "rule_owner",
+ "rule_owner_dn",
+ "rule_recertification_comment",
+ "rule_to_be_removed",
+ "rule_uid"
+ ],
+ "filter": {},
+ "allow_aggregations": true
+ }
+ },
+ {
+ "role": "modeller",
+ "permission": {
+ "columns": [
+ "last_change_admin",
+ "mgm_id",
+ "rule_created",
+ "rule_decert_date",
+ "rule_first_hit",
+ "rule_hit_counter",
+ "rule_last_certified",
+ "rule_last_certifier",
+ "rule_last_certifier_dn",
+ "rule_last_hit",
+ "rule_last_modified",
+ "rule_metadata_id",
+ "rule_owner",
+ "rule_owner_dn",
+ "rule_recertification_comment",
+ "rule_to_be_removed",
+ "rule_uid"
+ ],
+ "filter": {},
+ "allow_aggregations": true
+ }
+ },
+ {
+ "role": "recertifier",
+ "permission": {
+ "columns": [
+ "last_change_admin",
+ "mgm_id",
+ "rule_created",
+ "rule_decert_date",
+ "rule_first_hit",
+ "rule_hit_counter",
+ "rule_last_certified",
+ "rule_last_certifier",
+ "rule_last_certifier_dn",
+ "rule_last_hit",
+ "rule_last_modified",
+ "rule_metadata_id",
+ "rule_owner",
+ "rule_owner_dn",
+ "rule_recertification_comment",
+ "rule_to_be_removed",
+ "rule_uid"
+ ],
+ "filter": {},
+ "allow_aggregations": true
+ }
+ },
+ {
+ "role": "reporter",
+ "permission": {
+ "columns": [
+ "last_change_admin",
+ "mgm_id",
+ "rule_created",
+ "rule_decert_date",
+ "rule_first_hit",
+ "rule_hit_counter",
+ "rule_last_certified",
+ "rule_last_certifier",
+ "rule_last_certifier_dn",
+ "rule_last_hit",
+ "rule_last_modified",
+ "rule_metadata_id",
+ "rule_owner",
+ "rule_owner_dn",
+ "rule_recertification_comment",
+ "rule_to_be_removed",
+ "rule_uid"
+ ],
+ "filter": {},
+ "allow_aggregations": true
+ }
+ },
+ {
+ "role": "reporter-viewall",
+ "permission": {
+ "columns": [
+ "last_change_admin",
+ "mgm_id",
+ "rule_created",
+ "rule_decert_date",
+ "rule_first_hit",
+ "rule_hit_counter",
+ "rule_last_certified",
+ "rule_last_certifier",
+ "rule_last_certifier_dn",
+ "rule_last_hit",
+ "rule_last_modified",
+ "rule_metadata_id",
+ "rule_owner",
+ "rule_owner_dn",
+ "rule_recertification_comment",
+ "rule_to_be_removed",
+ "rule_uid"
+ ],
+ "filter": {},
+ "allow_aggregations": true
+ }
+ }
+ ],
+ "update_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "columns": [
+ "last_change_admin",
+ "mgm_id",
"rule_created",
"rule_decert_date",
"rule_first_hit",
+ "rule_hit_counter",
"rule_last_certified",
+ "rule_last_certifier",
+ "rule_last_certifier_dn",
"rule_last_hit",
"rule_last_modified",
- "rule_last_certifier_dn",
+ "rule_metadata_id",
+ "rule_owner",
"rule_owner_dn",
- "rule_recertification_comment"
+ "rule_recertification_comment",
+ "rule_to_be_removed",
+ "rule_uid"
+ ],
+ "filter": {},
+ "check": null
+ }
+ },
+ {
+ "role": "recertifier",
+ "permission": {
+ "columns": [
+ "rule_decert_date",
+ "rule_last_certified",
+ "rule_last_certifier",
+ "rule_last_certifier_dn",
+ "rule_recertification_comment",
+ "rule_to_be_removed"
+ ],
+ "filter": {},
+ "check": {}
+ }
+ }
+ ],
+ "delete_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "filter": {}
+ }
+ }
+ ]
+ },
+ {
+ "table": {
+ "name": "rule_nwobj_resolved",
+ "schema": "public"
+ },
+ "object_relationships": [
+ {
+ "name": "importControlByCreated",
+ "using": {
+ "foreign_key_constraint_on": "created"
+ }
+ },
+ {
+ "name": "import_control",
+ "using": {
+ "foreign_key_constraint_on": "removed"
+ }
+ },
+ {
+ "name": "management",
+ "using": {
+ "foreign_key_constraint_on": "mgm_id"
+ }
+ },
+ {
+ "name": "object",
+ "using": {
+ "foreign_key_constraint_on": "obj_id"
+ }
+ },
+ {
+ "name": "rule",
+ "using": {
+ "foreign_key_constraint_on": "rule_id"
+ }
+ }
+ ],
+ "insert_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "check": {},
+ "columns": [
+ "created",
+ "obj_id",
+ "removed",
+ "rule_id",
+ "mgm_id"
+ ]
+ },
+ "comment": ""
+ }
+ ],
+ "select_permissions": [
+ {
+ "role": "auditor",
+ "permission": {
+ "columns": [
+ "created",
+ "mgm_id",
+ "obj_id",
+ "removed",
+ "rule_id"
+ ],
+ "filter": {},
+ "allow_aggregations": true
+ }
+ },
+ {
+ "role": "fw-admin",
+ "permission": {
+ "columns": [
+ "created",
+ "obj_id",
+ "removed",
+ "rule_id",
+ "mgm_id"
+ ],
+ "filter": {},
+ "allow_aggregations": true
+ }
+ },
+ {
+ "role": "importer",
+ "permission": {
+ "columns": [
+ "created",
+ "obj_id",
+ "removed",
+ "rule_id",
+ "mgm_id"
+ ],
+ "filter": {},
+ "allow_aggregations": true
+ },
+ "comment": ""
+ },
+ {
+ "role": "recertifier",
+ "permission": {
+ "columns": [
+ "created",
+ "mgm_id",
+ "obj_id",
+ "removed",
+ "rule_id"
+ ],
+ "filter": {
+ "mgm_id": {
+ "_in": "x-hasura-visible-managements"
+ }
+ },
+ "allow_aggregations": true
+ }
+ },
+ {
+ "role": "reporter",
+ "permission": {
+ "columns": [
+ "created",
+ "mgm_id",
+ "obj_id",
+ "removed",
+ "rule_id"
+ ],
+ "filter": {
+ "mgm_id": {
+ "_in": "X-Hasura-visible-managements"
+ }
+ },
+ "allow_aggregations": true
+ }
+ },
+ {
+ "role": "reporter-viewall",
+ "permission": {
+ "columns": [
+ "created",
+ "mgm_id",
+ "obj_id",
+ "removed",
+ "rule_id"
+ ],
+ "filter": {},
+ "allow_aggregations": true
+ }
+ }
+ ],
+ "update_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "columns": [
+ "created",
+ "obj_id",
+ "removed",
+ "rule_id",
+ "mgm_id"
+ ],
+ "filter": {},
+ "check": {}
+ },
+ "comment": ""
+ }
+ ],
+ "delete_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "filter": {}
+ },
+ "comment": ""
+ }
+ ]
+ },
+ {
+ "table": {
+ "name": "rule_owner",
+ "schema": "public"
+ },
+ "object_relationships": [
+ {
+ "name": "owner",
+ "using": {
+ "foreign_key_constraint_on": "owner_id"
+ }
+ },
+ {
+ "name": "rule_metadatum",
+ "using": {
+ "foreign_key_constraint_on": "rule_metadata_id"
+ }
+ }
+ ],
+ "select_permissions": [
+ {
+ "role": "auditor",
+ "permission": {
+ "columns": [
+ "rule_metadata_id",
+ "owner_id"
+ ],
+ "filter": {}
+ }
+ }
+ ]
+ },
+ {
+ "table": {
+ "name": "rule_service",
+ "schema": "public"
+ },
+ "object_relationships": [
+ {
+ "name": "importControlByRsLastSeen",
+ "using": {
+ "foreign_key_constraint_on": "rs_last_seen"
+ }
+ },
+ {
+ "name": "import_control",
+ "using": {
+ "foreign_key_constraint_on": "rs_create"
+ }
+ },
+ {
+ "name": "rule",
+ "using": {
+ "foreign_key_constraint_on": "rule_id"
+ }
+ },
+ {
+ "name": "service",
+ "using": {
+ "foreign_key_constraint_on": "svc_id"
+ }
+ }
+ ],
+ "insert_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "check": {},
+ "columns": [
+ "rs_create",
+ "removed",
+ "rs_last_seen",
+ "rule_id",
+ "svc_id",
+ "active",
+ "negated"
+ ]
+ },
+ "comment": ""
+ }
+ ],
+ "select_permissions": [
+ {
+ "role": "auditor",
+ "permission": {
+ "columns": [
+ "active",
+ "negated",
+ "removed",
+ "rs_create",
+ "rs_last_seen",
+ "rule_id",
+ "svc_id"
+ ],
+ "filter": {}
+ }
+ },
+ {
+ "role": "fw-admin",
+ "permission": {
+ "columns": [
+ "active",
+ "negated",
+ "removed",
+ "rs_create",
+ "rs_last_seen",
+ "removed",
+ "rule_id",
+ "svc_id"
+ ],
+ "filter": {}
+ }
+ },
+ {
+ "role": "importer",
+ "permission": {
+ "columns": [
+ "removed",
+ "rs_create",
+ "rs_last_seen",
+ "rule_id",
+ "svc_id",
+ "active",
+ "negated"
+ ],
+ "filter": {},
+ "allow_aggregations": true
+ },
+ "comment": ""
+ },
+ {
+ "role": "middleware-server",
+ "permission": {
+ "columns": [
+ "active",
+ "negated",
+ "removed",
+ "rs_create",
+ "rs_last_seen",
+ "rule_id",
+ "svc_id"
+ ],
+ "filter": {}
+ },
+ "comment": ""
+ },
+ {
+ "role": "modeller",
+ "permission": {
+ "columns": [
+ "active",
+ "negated",
+ "removed",
+ "rs_create",
+ "rs_last_seen",
+ "rule_id",
+ "svc_id"
+ ],
+ "filter": {},
+ "allow_aggregations": true
+ }
+ },
+ {
+ "role": "recertifier",
+ "permission": {
+ "columns": [
+ "active",
+ "negated",
+ "removed",
+ "rs_create",
+ "rs_last_seen",
+ "rule_id",
+ "svc_id"
+ ],
+ "filter": {},
+ "allow_aggregations": true
+ }
+ },
+ {
+ "role": "reporter",
+ "permission": {
+ "columns": [
+ "active",
+ "negated",
+ "removed",
+ "rs_create",
+ "rs_last_seen",
+ "rule_id",
+ "svc_id"
+ ],
+ "filter": {},
+ "allow_aggregations": true
+ }
+ },
+ {
+ "role": "reporter-viewall",
+ "permission": {
+ "columns": [
+ "active",
+ "negated",
+ "removed",
+ "rs_create",
+ "rs_last_seen",
+ "rule_id",
+ "svc_id"
+ ],
+ "filter": {}
+ }
+ }
+ ],
+ "update_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "columns": [
+ "removed",
+ "rs_create",
+ "rs_last_seen",
+ "rule_id",
+ "svc_id",
+ "active",
+ "negated"
+ ],
+ "filter": {},
+ "check": {}
+ },
+ "comment": ""
+ }
+ ],
+ "delete_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "filter": {}
+ },
+ "comment": ""
+ }
+ ]
+ },
+ {
+ "table": {
+ "name": "rule_svc_resolved",
+ "schema": "public"
+ },
+ "object_relationships": [
+ {
+ "name": "importControlByCreated",
+ "using": {
+ "foreign_key_constraint_on": "created"
+ }
+ },
+ {
+ "name": "import_control",
+ "using": {
+ "foreign_key_constraint_on": "removed"
+ }
+ },
+ {
+ "name": "management",
+ "using": {
+ "foreign_key_constraint_on": "mgm_id"
+ }
+ },
+ {
+ "name": "rule",
+ "using": {
+ "foreign_key_constraint_on": "rule_id"
+ }
+ },
+ {
+ "name": "service",
+ "using": {
+ "foreign_key_constraint_on": "svc_id"
+ }
+ }
+ ],
+ "insert_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "check": {},
+ "columns": [
+ "created",
+ "removed",
+ "rule_id",
+ "svc_id",
+ "mgm_id"
+ ]
+ },
+ "comment": ""
+ }
+ ],
+ "select_permissions": [
+ {
+ "role": "auditor",
+ "permission": {
+ "columns": [
+ "mgm_id",
+ "created",
+ "removed",
+ "rule_id",
+ "svc_id"
],
"filter": {},
"allow_aggregations": true
}
},
{
- "role": "middleware-server",
+ "role": "fw-admin",
"permission": {
"columns": [
- "rule_to_be_removed",
- "dev_id",
- "last_change_admin",
- "rule_last_certifier",
- "rule_owner",
- "rule_hit_counter",
- "rule_metadata_id",
- "rule_uid",
- "rule_created",
- "rule_decert_date",
- "rule_first_hit",
- "rule_last_certified",
- "rule_last_hit",
- "rule_last_modified",
- "rule_last_certifier_dn",
- "rule_owner_dn",
- "rule_recertification_comment"
+ "created",
+ "removed",
+ "rule_id",
+ "svc_id",
+ "mgm_id"
],
"filter": {},
"allow_aggregations": true
}
},
{
- "role": "modeller",
+ "role": "importer",
"permission": {
"columns": [
- "rule_metadata_id",
- "dev_id",
- "rule_uid",
- "rule_created",
- "rule_last_modified",
- "rule_first_hit",
- "rule_last_hit",
- "rule_hit_counter",
- "rule_last_certified",
- "rule_last_certifier",
- "rule_last_certifier_dn",
- "rule_owner",
- "rule_owner_dn",
- "rule_to_be_removed",
- "last_change_admin",
- "rule_decert_date",
- "rule_recertification_comment"
+ "created",
+ "removed",
+ "rule_id",
+ "svc_id",
+ "mgm_id"
],
- "filter": {
- "dev_id": {
- "_in": "x-hasura-visible-devices"
- }
- },
+ "filter": {},
"allow_aggregations": true
- }
+ },
+ "comment": ""
},
{
"role": "recertifier",
"permission": {
"columns": [
- "rule_to_be_removed",
- "dev_id",
- "last_change_admin",
- "rule_last_certifier",
- "rule_owner",
- "rule_hit_counter",
- "rule_metadata_id",
- "rule_uid",
- "rule_created",
- "rule_decert_date",
- "rule_first_hit",
- "rule_last_certified",
- "rule_last_hit",
- "rule_last_modified",
- "rule_last_certifier_dn",
- "rule_owner_dn",
- "rule_recertification_comment"
+ "mgm_id",
+ "rule_id",
+ "svc_id",
+ "created",
+ "removed"
],
- "filter": {},
+ "filter": {
+ "mgm_id": {
+ "_in": "x-hasura-visible-managements"
+ }
+ },
"allow_aggregations": true
}
},
@@ -14926,27 +16978,15 @@
"role": "reporter",
"permission": {
"columns": [
- "rule_metadata_id",
- "dev_id",
- "rule_uid",
- "rule_created",
- "rule_last_modified",
- "rule_first_hit",
- "rule_last_hit",
- "rule_hit_counter",
- "rule_last_certified",
- "rule_last_certifier",
- "rule_last_certifier_dn",
- "rule_owner",
- "rule_owner_dn",
- "rule_to_be_removed",
- "last_change_admin",
- "rule_decert_date",
- "rule_recertification_comment"
+ "mgm_id",
+ "created",
+ "removed",
+ "rule_id",
+ "svc_id"
],
"filter": {
- "dev_id": {
- "_in": "x-hasura-visible-devices"
+ "mgm_id": {
+ "_in": "X-Hasura-visible-managements"
}
},
"allow_aggregations": true
@@ -14956,23 +16996,11 @@
"role": "reporter-viewall",
"permission": {
"columns": [
- "rule_to_be_removed",
- "dev_id",
- "last_change_admin",
- "rule_last_certifier",
- "rule_owner",
- "rule_hit_counter",
- "rule_metadata_id",
- "rule_uid",
- "rule_created",
- "rule_decert_date",
- "rule_first_hit",
- "rule_last_certified",
- "rule_last_hit",
- "rule_last_modified",
- "rule_last_certifier_dn",
- "rule_owner_dn",
- "rule_recertification_comment"
+ "mgm_id",
+ "created",
+ "removed",
+ "rule_id",
+ "svc_id"
],
"filter": {},
"allow_aggregations": true
@@ -14984,42 +17012,16 @@
"role": "importer",
"permission": {
"columns": [
- "rule_to_be_removed",
- "dev_id",
- "last_change_admin",
- "rule_last_certifier",
- "rule_owner",
- "rule_hit_counter",
- "rule_metadata_id",
- "rule_uid",
- "rule_created",
- "rule_decert_date",
- "rule_first_hit",
- "rule_last_certified",
- "rule_last_hit",
- "rule_last_modified",
- "rule_last_certifier_dn",
- "rule_owner_dn",
- "rule_recertification_comment"
- ],
- "filter": {},
- "check": null
- }
- },
- {
- "role": "recertifier",
- "permission": {
- "columns": [
- "rule_decert_date",
- "rule_last_certified",
- "rule_last_certifier",
- "rule_last_certifier_dn",
- "rule_recertification_comment",
- "rule_to_be_removed"
+ "created",
+ "removed",
+ "rule_id",
+ "svc_id",
+ "mgm_id"
],
"filter": {},
"check": {}
- }
+ },
+ "comment": ""
}
],
"delete_permissions": [
@@ -15027,73 +17029,196 @@
"role": "importer",
"permission": {
"filter": {}
- }
+ },
+ "comment": ""
}
]
},
{
"table": {
- "name": "rule_nwobj_resolved",
+ "name": "rule_to",
"schema": "public"
},
"object_relationships": [
{
- "name": "importControlByCreated",
+ "name": "importControlByRtLastSeen",
"using": {
- "foreign_key_constraint_on": "created"
+ "foreign_key_constraint_on": "rt_last_seen"
}
},
{
"name": "import_control",
"using": {
- "foreign_key_constraint_on": "removed"
+ "foreign_key_constraint_on": "rt_create"
}
},
{
- "name": "management",
+ "name": "object",
"using": {
- "foreign_key_constraint_on": "mgm_id"
+ "foreign_key_constraint_on": "obj_id"
}
},
{
- "name": "object",
+ "name": "rule",
"using": {
- "foreign_key_constraint_on": "obj_id"
+ "foreign_key_constraint_on": "rule_id"
+ }
+ },
+ {
+ "name": "usr",
+ "using": {
+ "foreign_key_constraint_on": "user_id"
+ }
+ }
+ ],
+ "computed_fields": [
+ {
+ "name": "rule_to_relevant_for_tenant",
+ "definition": {
+ "function": {
+ "name": "rule_to_relevant_for_tenant",
+ "schema": "public"
+ },
+ "session_argument": "hasura_session"
+ }
+ }
+ ],
+ "insert_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "check": {},
+ "columns": [
+ "obj_id",
+ "removed",
+ "rt_create",
+ "rt_last_seen",
+ "rule_id",
+ "rule_to_id",
+ "user_id",
+ "active",
+ "negated"
+ ]
+ },
+ "comment": ""
+ }
+ ],
+ "select_permissions": [
+ {
+ "role": "auditor",
+ "permission": {
+ "columns": [
+ "active",
+ "negated",
+ "obj_id",
+ "removed",
+ "rt_create",
+ "rt_last_seen",
+ "rule_id",
+ "rule_to_id",
+ "user_id"
+ ],
+ "computed_fields": [
+ "rule_to_relevant_for_tenant"
+ ],
+ "filter": {}
+ }
+ },
+ {
+ "role": "fw-admin",
+ "permission": {
+ "columns": [
+ "active",
+ "negated",
+ "obj_id",
+ "removed",
+ "rt_create",
+ "rt_last_seen",
+ "rule_id",
+ "rule_to_id",
+ "user_id"
+ ],
+ "computed_fields": [
+ "rule_to_relevant_for_tenant"
+ ],
+ "filter": {}
}
},
{
- "name": "rule",
- "using": {
- "foreign_key_constraint_on": "rule_id"
- }
- }
- ],
- "select_permissions": [
- {
- "role": "auditor",
+ "role": "importer",
"permission": {
"columns": [
- "created",
- "mgm_id",
"obj_id",
"removed",
- "rule_id"
+ "rt_create",
+ "rt_last_seen",
+ "rule_id",
+ "rule_to_id",
+ "user_id",
+ "active",
+ "negated"
+ ],
+ "computed_fields": [
+ "rule_to_relevant_for_tenant"
],
"filter": {},
"allow_aggregations": true
+ },
+ "comment": ""
+ },
+ {
+ "role": "middleware-server",
+ "permission": {
+ "columns": [
+ "active",
+ "negated",
+ "obj_id",
+ "removed",
+ "rt_create",
+ "rt_last_seen",
+ "rule_id",
+ "rule_to_id",
+ "user_id"
+ ],
+ "computed_fields": [
+ "rule_to_relevant_for_tenant"
+ ],
+ "filter": {}
}
},
{
- "role": "fw-admin",
+ "role": "modeller",
"permission": {
"columns": [
- "created",
+ "active",
+ "negated",
"obj_id",
"removed",
+ "rt_create",
+ "rt_last_seen",
"rule_id",
- "mgm_id"
+ "rule_to_id",
+ "user_id"
],
- "filter": {},
+ "computed_fields": [
+ "rule_to_relevant_for_tenant"
+ ],
+ "filter": {
+ "_and": [
+ {
+ "rule": {
+ "mgm_id": {
+ "_in": "x-hasura-visible-managements"
+ }
+ }
+ },
+ {
+ "rule_to_relevant_for_tenant": {
+ "_eq": "true"
+ }
+ }
+ ]
+ },
"allow_aggregations": true
}
},
@@ -15101,16 +17226,34 @@
"role": "recertifier",
"permission": {
"columns": [
- "created",
- "mgm_id",
+ "active",
+ "negated",
"obj_id",
"removed",
- "rule_id"
+ "rt_create",
+ "rt_last_seen",
+ "rule_id",
+ "rule_to_id",
+ "user_id"
+ ],
+ "computed_fields": [
+ "rule_to_relevant_for_tenant"
],
"filter": {
- "mgm_id": {
- "_in": "x-hasura-visible-managements"
- }
+ "_and": [
+ {
+ "rule": {
+ "mgm_id": {
+ "_in": "x-hasura-visible-managements"
+ }
+ }
+ },
+ {
+ "rule_to_relevant_for_tenant": {
+ "_eq": "true"
+ }
+ }
+ ]
},
"allow_aggregations": true
}
@@ -15119,16 +17262,34 @@
"role": "reporter",
"permission": {
"columns": [
- "created",
- "mgm_id",
+ "active",
+ "negated",
"obj_id",
"removed",
- "rule_id"
+ "rt_create",
+ "rt_last_seen",
+ "rule_id",
+ "rule_to_id",
+ "user_id"
+ ],
+ "computed_fields": [
+ "rule_to_relevant_for_tenant"
],
"filter": {
- "mgm_id": {
- "_in": "X-Hasura-visible-managements"
- }
+ "_and": [
+ {
+ "rule": {
+ "mgm_id": {
+ "_in": "x-hasura-visible-managements"
+ }
+ }
+ },
+ {
+ "rule_to_relevant_for_tenant": {
+ "_eq": "true"
+ }
+ }
+ ]
},
"allow_aggregations": true
}
@@ -15137,68 +17298,60 @@
"role": "reporter-viewall",
"permission": {
"columns": [
- "created",
- "mgm_id",
+ "active",
+ "negated",
"obj_id",
"removed",
- "rule_id"
+ "rt_create",
+ "rt_last_seen",
+ "rule_id",
+ "rule_to_id",
+ "user_id"
],
- "filter": {},
- "allow_aggregations": true
- }
- }
- ]
- },
- {
- "table": {
- "name": "rule_owner",
- "schema": "public"
- },
- "object_relationships": [
- {
- "name": "owner",
- "using": {
- "foreign_key_constraint_on": "owner_id"
- }
- },
- {
- "name": "rule_metadatum",
- "using": {
- "foreign_key_constraint_on": "rule_metadata_id"
+ "computed_fields": [
+ "rule_to_relevant_for_tenant"
+ ],
+ "filter": {}
}
}
],
- "select_permissions": [
+ "update_permissions": [
{
- "role": "auditor",
+ "role": "importer",
"permission": {
"columns": [
- "rule_metadata_id",
- "owner_id"
+ "obj_id",
+ "removed",
+ "rt_create",
+ "rt_last_seen",
+ "rule_id",
+ "rule_to_id",
+ "user_id",
+ "active",
+ "negated"
],
+ "filter": {},
+ "check": {}
+ },
+ "comment": ""
+ }
+ ],
+ "delete_permissions": [
+ {
+ "role": "importer",
+ "permission": {
"filter": {}
- }
+ },
+ "comment": ""
}
]
},
{
"table": {
- "name": "rule_service",
+ "name": "rule_to_zone",
"schema": "public"
},
"object_relationships": [
- {
- "name": "importControlByRsLastSeen",
- "using": {
- "foreign_key_constraint_on": "rs_last_seen"
- }
- },
- {
- "name": "import_control",
- "using": {
- "foreign_key_constraint_on": "rs_create"
- }
- },
{
"name": "rule",
"using": {
@@ -15206,51 +17359,78 @@
}
},
{
- "name": "service",
+ "name": "zone",
"using": {
- "foreign_key_constraint_on": "svc_id"
+ "foreign_key_constraint_on": "zone_id"
}
}
],
+ "insert_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "check": {},
+ "columns": [
+ "created",
+ "removed",
+ "rule_id",
+ "zone_id"
+ ]
+ },
+ "comment": ""
+ }
+ ],
"select_permissions": [
{
- "role": "auditor",
+ "role": "auditor",
+ "permission": {
+ "columns": [
+ "created",
+ "removed",
+ "rule_id",
+ "zone_id"
+ ],
+ "filter": {},
+ "allow_aggregations": true
+ },
+ "comment": ""
+ },
+ {
+ "role": "fw-admin",
"permission": {
"columns": [
+ "created",
+ "removed",
"rule_id",
- "svc_id",
- "active",
- "rs_create",
- "rs_last_seen",
- "negated"
+ "zone_id"
],
- "filter": {}
- }
+ "filter": {},
+ "allow_aggregations": true
+ },
+ "comment": ""
},
{
- "role": "fw-admin",
+ "role": "importer",
"permission": {
"columns": [
- "rs_create",
- "rs_last_seen",
+ "created",
+ "removed",
"rule_id",
- "svc_id",
- "active",
- "negated"
+ "zone_id"
],
- "filter": {}
- }
+ "filter": {},
+ "allow_aggregations": true
+ },
+ "comment": ""
},
{
"role": "middleware-server",
"permission": {
"columns": [
- "rs_create",
- "rs_last_seen",
+ "created",
+ "removed",
"rule_id",
- "svc_id",
- "active",
- "negated"
+ "zone_id"
],
"filter": {}
},
@@ -15260,66 +17440,88 @@
"role": "modeller",
"permission": {
"columns": [
+ "created",
+ "removed",
"rule_id",
- "svc_id",
- "active",
- "rs_create",
- "rs_last_seen",
- "negated"
+ "zone_id"
],
"filter": {},
"allow_aggregations": true
- }
+ },
+ "comment": ""
},
{
"role": "recertifier",
"permission": {
"columns": [
+ "created",
+ "removed",
"rule_id",
- "svc_id",
- "active",
- "rs_create",
- "rs_last_seen",
- "negated"
+ "zone_id"
],
"filter": {},
"allow_aggregations": true
- }
+ },
+ "comment": ""
},
{
"role": "reporter",
"permission": {
"columns": [
+ "created",
+ "removed",
"rule_id",
- "svc_id",
- "active",
- "rs_create",
- "rs_last_seen",
- "negated"
+ "zone_id"
],
"filter": {},
"allow_aggregations": true
- }
+ },
+ "comment": ""
},
{
"role": "reporter-viewall",
"permission": {
"columns": [
+ "created",
+ "removed",
"rule_id",
- "svc_id",
- "active",
- "rs_create",
- "rs_last_seen",
- "negated"
+ "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",
+ "name": "rule_user_resolved",
"schema": "public"
},
"object_relationships": [
@@ -15348,22 +17550,38 @@
}
},
{
- "name": "service",
+ "name": "usr",
"using": {
- "foreign_key_constraint_on": "svc_id"
+ "foreign_key_constraint_on": "user_id"
}
}
],
+ "insert_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "check": {},
+ "columns": [
+ "created",
+ "removed",
+ "rule_id",
+ "user_id",
+ "mgm_id"
+ ]
+ },
+ "comment": ""
+ }
+ ],
"select_permissions": [
{
"role": "auditor",
"permission": {
"columns": [
"mgm_id",
- "created",
- "removed",
"rule_id",
- "svc_id"
+ "user_id",
+ "created",
+ "removed"
],
"filter": {},
"allow_aggregations": true
@@ -15376,22 +17594,37 @@
"created",
"removed",
"rule_id",
- "svc_id",
+ "user_id",
"mgm_id"
],
"filter": {},
"allow_aggregations": true
}
},
+ {
+ "role": "importer",
+ "permission": {
+ "columns": [
+ "created",
+ "removed",
+ "rule_id",
+ "user_id",
+ "mgm_id"
+ ],
+ "filter": {},
+ "allow_aggregations": true
+ },
+ "comment": ""
+ },
{
"role": "recertifier",
"permission": {
"columns": [
"mgm_id",
- "rule_id",
- "svc_id",
"created",
- "removed"
+ "removed",
+ "rule_id",
+ "user_id"
],
"filter": {
"mgm_id": {
@@ -15406,10 +17639,10 @@
"permission": {
"columns": [
"mgm_id",
- "created",
- "removed",
"rule_id",
- "svc_id"
+ "user_id",
+ "created",
+ "removed"
],
"filter": {
"mgm_id": {
@@ -15427,380 +17660,485 @@
"created",
"removed",
"rule_id",
- "svc_id"
+ "user_id"
],
"filter": {},
"allow_aggregations": true
}
}
+ ],
+ "update_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "columns": [
+ "created",
+ "removed",
+ "rule_id",
+ "user_id",
+ "mgm_id"
+ ],
+ "filter": {},
+ "check": {}
+ },
+ "comment": ""
+ }
+ ],
+ "delete_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "filter": {}
+ },
+ "comment": ""
+ }
]
},
{
"table": {
- "name": "rule_to",
+ "name": "rulebase",
"schema": "public"
},
"object_relationships": [
{
- "name": "importControlByRtLastSeen",
- "using": {
- "foreign_key_constraint_on": "rt_last_seen"
- }
- },
- {
- "name": "import_control",
+ "name": "management",
"using": {
- "foreign_key_constraint_on": "rt_create"
+ "foreign_key_constraint_on": "mgm_id"
}
- },
+ }
+ ],
+ "array_relationships": [
{
- "name": "object",
+ "name": "rulebaseLinksByFromRulebaseId",
"using": {
- "foreign_key_constraint_on": "obj_id"
+ "foreign_key_constraint_on": {
+ "column": "from_rulebase_id",
+ "table": {
+ "name": "rulebase_link",
+ "schema": "public"
+ }
+ }
}
},
{
- "name": "rule",
+ "name": "rulebase_links",
"using": {
- "foreign_key_constraint_on": "rule_id"
+ "foreign_key_constraint_on": {
+ "column": "to_rulebase_id",
+ "table": {
+ "name": "rulebase_link",
+ "schema": "public"
+ }
+ }
}
},
{
- "name": "usr",
+ "name": "rules",
"using": {
- "foreign_key_constraint_on": "user_id"
+ "foreign_key_constraint_on": {
+ "column": "rulebase_id",
+ "table": {
+ "name": "rule",
+ "schema": "public"
+ }
+ }
}
}
],
"computed_fields": [
{
- "name": "rule_to_relevant_for_tenant",
+ "name": "get_rules_for_owner",
"definition": {
"function": {
- "name": "rule_to_relevant_for_tenant",
+ "name": "get_rulebase_for_owner",
"schema": "public"
- },
- "session_argument": "hasura_session"
+ }
}
}
],
+ "insert_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "check": {},
+ "columns": [
+ "created",
+ "removed",
+ "is_global",
+ "name",
+ "uid",
+ "id",
+ "mgm_id"
+ ]
+ },
+ "comment": ""
+ }
+ ],
"select_permissions": [
{
"role": "auditor",
"permission": {
"columns": [
- "obj_id",
- "rt_create",
- "rt_last_seen",
- "rule_id",
- "rule_to_id",
- "user_id",
- "active",
- "negated"
- ],
- "computed_fields": [
- "rule_to_relevant_for_tenant"
+ "created",
+ "removed",
+ "is_global",
+ "name",
+ "uid",
+ "id",
+ "mgm_id"
],
- "filter": {}
- }
+ "filter": {},
+ "allow_aggregations": true
+ },
+ "comment": ""
},
{
- "role": "fw-admin",
+ "role": "importer",
"permission": {
"columns": [
- "active",
- "negated",
- "obj_id",
- "rt_create",
- "rt_last_seen",
- "rule_id",
- "rule_to_id",
- "user_id"
- ],
- "computed_fields": [
- "rule_to_relevant_for_tenant"
+ "created",
+ "removed",
+ "is_global",
+ "name",
+ "uid",
+ "id",
+ "mgm_id"
],
- "filter": {}
- }
+ "filter": {},
+ "allow_aggregations": true
+ },
+ "comment": ""
},
{
"role": "middleware-server",
"permission": {
"columns": [
- "active",
- "negated",
- "obj_id",
- "rt_create",
- "rt_last_seen",
- "rule_id",
- "rule_to_id",
- "user_id"
+ "created",
+ "removed",
+ "is_global",
+ "name",
+ "uid",
+ "id",
+ "mgm_id"
],
"filter": {}
- }
+ },
+ "comment": ""
},
{
- "role": "modeller",
+ "role": "recertifier",
"permission": {
"columns": [
- "active",
- "negated",
- "obj_id",
- "rt_create",
- "rt_last_seen",
- "rule_id",
- "rule_to_id",
- "user_id"
- ],
- "computed_fields": [
- "rule_to_relevant_for_tenant"
+ "created",
+ "removed",
+ "is_global",
+ "name",
+ "uid",
+ "id",
+ "mgm_id"
],
- "filter": {
- "_and": [
- {
- "rule": {
- "mgm_id": {
- "_in": "x-hasura-visible-managements"
- }
- }
- },
- {
- "rule": {
- "dev_id": {
- "_in": "x-hasura-visible-devices"
- }
- }
- },
- {
- "rule_to_relevant_for_tenant": {
- "_eq": "true"
- }
- }
- ]
- },
- "allow_aggregations": true
- }
+ "filter": {}
+ },
+ "comment": ""
},
{
- "role": "recertifier",
+ "role": "reporter",
"permission": {
"columns": [
- "obj_id",
- "rt_create",
- "rt_last_seen",
- "rule_id",
- "rule_to_id",
- "user_id",
- "active",
- "negated"
- ],
- "computed_fields": [
- "rule_to_relevant_for_tenant"
+ "created",
+ "removed",
+ "is_global",
+ "name",
+ "uid",
+ "id",
+ "mgm_id"
],
- "filter": {
- "_and": [
- {
- "rule": {
- "mgm_id": {
- "_in": "x-hasura-visible-managements"
- }
- }
- },
- {
- "rule": {
- "dev_id": {
- "_in": "x-hasura-visible-devices"
- }
- }
- },
- {
- "rule_to_relevant_for_tenant": {
- "_eq": "true"
- }
- }
- ]
- },
+ "filter": {},
"allow_aggregations": true
- }
+ },
+ "comment": ""
},
{
- "role": "reporter",
+ "role": "reporter-viewall",
"permission": {
"columns": [
- "active",
- "negated",
- "obj_id",
- "rt_create",
- "rt_last_seen",
- "rule_id",
- "rule_to_id",
- "user_id"
- ],
- "computed_fields": [
- "rule_to_relevant_for_tenant"
+ "created",
+ "removed",
+ "is_global",
+ "name",
+ "uid",
+ "id",
+ "mgm_id"
],
- "filter": {
- "_and": [
- {
- "rule": {
- "mgm_id": {
- "_in": "x-hasura-visible-managements"
- }
- }
- },
- {
- "rule": {
- "dev_id": {
- "_in": "x-hasura-visible-devices"
- }
- }
- },
- {
- "rule_to_relevant_for_tenant": {
- "_eq": "true"
- }
- }
- ]
- },
+ "filter": {},
"allow_aggregations": true
- }
- },
+ },
+ "comment": ""
+ }
+ ],
+ "update_permissions": [
{
- "role": "reporter-viewall",
+ "role": "importer",
"permission": {
"columns": [
- "obj_id",
- "rt_create",
- "rt_last_seen",
- "rule_id",
- "rule_to_id",
- "user_id",
- "active",
- "negated"
- ],
- "computed_fields": [
- "rule_to_relevant_for_tenant"
+ "created",
+ "removed",
+ "is_global",
+ "name",
+ "uid",
+ "id",
+ "mgm_id"
],
+ "filter": {},
+ "check": {}
+ },
+ "comment": ""
+ }
+ ],
+ "delete_permissions": [
+ {
+ "role": "importer",
+ "permission": {
"filter": {}
- }
+ },
+ "comment": ""
}
]
},
{
"table": {
- "name": "rule_user_resolved",
+ "name": "rulebase_link",
"schema": "public"
},
"object_relationships": [
{
- "name": "importControlByCreated",
+ "name": "device",
"using": {
- "foreign_key_constraint_on": "created"
+ "foreign_key_constraint_on": "gw_id"
}
},
{
- "name": "import_control",
+ "name": "importControlByRemoved",
"using": {
"foreign_key_constraint_on": "removed"
}
},
{
- "name": "management",
+ "name": "import_control",
"using": {
- "foreign_key_constraint_on": "mgm_id"
+ "foreign_key_constraint_on": "created"
}
},
{
"name": "rule",
"using": {
- "foreign_key_constraint_on": "rule_id"
+ "foreign_key_constraint_on": "from_rule_id"
}
},
{
- "name": "usr",
+ "name": "rulebase",
"using": {
- "foreign_key_constraint_on": "user_id"
+ "foreign_key_constraint_on": "to_rulebase_id"
+ }
+ },
+ {
+ "name": "rulebaseByFromRulebaseId",
+ "using": {
+ "foreign_key_constraint_on": "from_rulebase_id"
+ }
+ },
+ {
+ "name": "stm_link_type",
+ "using": {
+ "foreign_key_constraint_on": "link_type"
}
}
],
+ "insert_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "check": {},
+ "columns": [
+ "created",
+ "removed",
+ "is_global",
+ "is_initial",
+ "is_section",
+ "from_rulebase_id",
+ "from_rule_id",
+ "gw_id",
+ "id",
+ "link_type",
+ "to_rulebase_id"
+ ]
+ },
+ "comment": ""
+ }
+ ],
"select_permissions": [
{
"role": "auditor",
"permission": {
"columns": [
- "mgm_id",
- "rule_id",
- "user_id",
"created",
- "removed"
+ "from_rule_id",
+ "removed",
+ "is_global",
+ "is_initial",
+ "is_section",
+ "from_rulebase_id",
+ "gw_id",
+ "id",
+ "link_type",
+ "to_rulebase_id"
],
- "filter": {},
- "allow_aggregations": true
- }
+ "filter": {}
+ },
+ "comment": ""
},
{
- "role": "fw-admin",
+ "role": "importer",
"permission": {
"columns": [
"created",
"removed",
- "rule_id",
- "user_id",
- "mgm_id"
+ "is_global",
+ "is_initial",
+ "is_section",
+ "from_rulebase_id",
+ "from_rule_id",
+ "gw_id",
+ "id",
+ "link_type",
+ "to_rulebase_id"
],
"filter": {},
"allow_aggregations": true
- }
+ },
+ "comment": ""
+ },
+ {
+ "role": "middleware-server",
+ "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": "modeller",
+ "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": [
- "mgm_id",
"created",
+ "from_rule_id",
"removed",
- "rule_id",
- "user_id"
+ "is_global",
+ "is_initial",
+ "is_section",
+ "from_rulebase_id",
+ "gw_id",
+ "id",
+ "link_type",
+ "to_rulebase_id"
],
- "filter": {
- "mgm_id": {
- "_in": "x-hasura-visible-managements"
- }
- },
- "allow_aggregations": true
- }
+ "filter": {}
+ },
+ "comment": ""
},
{
"role": "reporter",
"permission": {
"columns": [
- "mgm_id",
- "rule_id",
- "user_id",
"created",
- "removed"
+ "from_rule_id",
+ "removed",
+ "is_global",
+ "is_initial",
+ "is_section",
+ "from_rulebase_id",
+ "gw_id",
+ "id",
+ "link_type",
+ "to_rulebase_id"
],
- "filter": {
- "mgm_id": {
- "_in": "X-Hasura-visible-managements"
- }
- },
- "allow_aggregations": true
- }
+ "filter": {}
+ },
+ "comment": ""
},
{
"role": "reporter-viewall",
"permission": {
"columns": [
- "mgm_id",
"created",
+ "from_rule_id",
"removed",
- "rule_id",
- "user_id"
+ "is_global",
+ "is_initial",
+ "is_section",
+ "from_rulebase_id",
+ "gw_id",
+ "id",
+ "link_type",
+ "to_rulebase_id"
+ ],
+ "filter": {}
+ },
+ "comment": ""
+ }
+ ],
+ "update_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "columns": [
+ "removed"
],
"filter": {},
- "allow_aggregations": true
- }
+ "check": {}
+ },
+ "comment": ""
+ }
+ ],
+ "delete_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "filter": {}
+ },
+ "comment": ""
}
]
},
@@ -15999,6 +18337,51 @@
}
}
],
+ "insert_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "check": {},
+ "columns": [
+ "removed",
+ "svc_create",
+ "svc_id",
+ "svc_last_seen",
+ "active",
+ "initial_config",
+ "srv_keeponinstall",
+ "svc_accept_rep",
+ "svc_accept_rep_any",
+ "svc_mfa",
+ "svc_sync",
+ "svc_sync_delay",
+ "svc_tcp_res",
+ "svc_timeout_std",
+ "svc_code",
+ "svc_name",
+ "svc_rpcnr",
+ "ip_proto_id",
+ "last_change_admin",
+ "mgm_id",
+ "svc_color_id",
+ "svc_port",
+ "svc_port_end",
+ "svc_source_port",
+ "svc_source_port_end",
+ "svc_sync_delay_start",
+ "svc_timeout",
+ "svc_typ_id",
+ "svc_comment",
+ "svc_match",
+ "svc_member_names",
+ "svc_member_refs",
+ "svc_prod_specific",
+ "svc_uid"
+ ]
+ },
+ "comment": ""
+ }
+ ],
"select_permissions": [
{
"role": "auditor",
@@ -16036,6 +18419,7 @@
"active",
"last_change_admin",
"svc_create",
+ "removed",
"svc_last_seen"
],
"filter": {},
@@ -16049,6 +18433,7 @@
"svc_create",
"svc_id",
"svc_last_seen",
+ "removed",
"active",
"initial_config",
"srv_keeponinstall",
@@ -16084,6 +18469,49 @@
"allow_aggregations": true
}
},
+ {
+ "role": "importer",
+ "permission": {
+ "columns": [
+ "removed",
+ "svc_create",
+ "svc_id",
+ "svc_last_seen",
+ "active",
+ "initial_config",
+ "srv_keeponinstall",
+ "svc_accept_rep",
+ "svc_accept_rep_any",
+ "svc_mfa",
+ "svc_sync",
+ "svc_sync_delay",
+ "svc_tcp_res",
+ "svc_timeout_std",
+ "svc_code",
+ "svc_name",
+ "svc_rpcnr",
+ "ip_proto_id",
+ "last_change_admin",
+ "mgm_id",
+ "svc_color_id",
+ "svc_port",
+ "svc_port_end",
+ "svc_source_port",
+ "svc_source_port_end",
+ "svc_sync_delay_start",
+ "svc_timeout",
+ "svc_typ_id",
+ "svc_comment",
+ "svc_match",
+ "svc_member_names",
+ "svc_member_refs",
+ "svc_prod_specific",
+ "svc_uid"
+ ],
+ "filter": {}
+ },
+ "comment": ""
+ },
{
"role": "middleware-server",
"permission": {
@@ -16091,6 +18519,7 @@
"svc_create",
"svc_id",
"svc_last_seen",
+ "removed",
"active",
"initial_config",
"srv_keeponinstall",
@@ -16162,7 +18591,8 @@
"active",
"last_change_admin",
"svc_create",
- "svc_last_seen"
+ "svc_last_seen",
+ "removed"
],
"filter": {
"mgm_id": {
@@ -16208,7 +18638,8 @@
"active",
"last_change_admin",
"svc_create",
- "svc_last_seen"
+ "svc_last_seen",
+ "removed"
],
"filter": {
"mgm_id": {
@@ -16254,6 +18685,7 @@
"active",
"last_change_admin",
"svc_create",
+ "removed",
"svc_last_seen"
],
"filter": {
@@ -16292,19 +18724,75 @@
"svc_accept_rep",
"svc_accept_rep_any",
"svc_mfa",
- "svc_timeout_std",
- "svc_timeout",
+ "svc_timeout_std",
+ "svc_timeout",
+ "svc_sync",
+ "svc_sync_delay",
+ "svc_sync_delay_start",
+ "active",
+ "last_change_admin",
+ "removed",
+ "svc_create",
+ "svc_last_seen"
+ ],
+ "filter": {},
+ "allow_aggregations": true
+ }
+ }
+ ],
+ "update_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "columns": [
+ "removed",
+ "svc_create",
+ "svc_id",
+ "svc_last_seen",
+ "active",
+ "initial_config",
+ "srv_keeponinstall",
+ "svc_accept_rep",
+ "svc_accept_rep_any",
+ "svc_mfa",
"svc_sync",
"svc_sync_delay",
- "svc_sync_delay_start",
- "active",
+ "svc_tcp_res",
+ "svc_timeout_std",
+ "svc_code",
+ "svc_name",
+ "svc_rpcnr",
+ "ip_proto_id",
"last_change_admin",
- "svc_create",
- "svc_last_seen"
+ "mgm_id",
+ "svc_color_id",
+ "svc_port",
+ "svc_port_end",
+ "svc_source_port",
+ "svc_source_port_end",
+ "svc_sync_delay_start",
+ "svc_timeout",
+ "svc_typ_id",
+ "svc_comment",
+ "svc_match",
+ "svc_member_names",
+ "svc_member_refs",
+ "svc_prod_specific",
+ "svc_uid"
],
"filter": {},
- "allow_aggregations": true
- }
+ "check": {}
+ },
+ "comment": ""
+ }
+ ],
+ "delete_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "filter": {}
+ },
+ "comment": ""
}
]
},
@@ -16357,7 +18845,8 @@
"permission": {
"columns": [
"action_id",
- "action_name"
+ "action_name",
+ "allowed"
],
"filter": {}
}
@@ -16367,7 +18856,8 @@
"permission": {
"columns": [
"action_id",
- "action_name"
+ "action_name",
+ "allowed"
],
"filter": {}
}
@@ -16376,6 +18866,7 @@
"role": "fw-admin",
"permission": {
"columns": [
+ "allowed",
"action_name",
"action_id"
],
@@ -16388,7 +18879,8 @@
"permission": {
"columns": [
"action_id",
- "action_name"
+ "action_name",
+ "allowed"
],
"filter": {}
}
@@ -16396,7 +18888,11 @@
{
"role": "importer",
"permission": {
- "columns": [],
+ "columns": [
+ "allowed",
+ "action_name",
+ "action_id"
+ ],
"filter": {},
"allow_aggregations": true
}
@@ -16405,8 +18901,9 @@
"role": "modeller",
"permission": {
"columns": [
+ "action_id",
"action_name",
- "action_id"
+ "allowed"
],
"filter": {}
},
@@ -16417,7 +18914,8 @@
"permission": {
"columns": [
"action_id",
- "action_name"
+ "action_name",
+ "allowed"
],
"filter": {}
}
@@ -16427,7 +18925,8 @@
"permission": {
"columns": [
"action_id",
- "action_name"
+ "action_name",
+ "allowed"
],
"filter": {}
}
@@ -16437,7 +18936,8 @@
"permission": {
"columns": [
"action_id",
- "action_name"
+ "action_name",
+ "allowed"
],
"filter": {}
}
@@ -16447,7 +18947,8 @@
"permission": {
"columns": [
"action_id",
- "action_name"
+ "action_name",
+ "allowed"
],
"filter": {},
"allow_aggregations": true
@@ -16458,7 +18959,8 @@
"permission": {
"columns": [
"action_id",
- "action_name"
+ "action_name",
+ "allowed"
],
"filter": {}
}
@@ -16468,7 +18970,8 @@
"permission": {
"columns": [
"action_id",
- "action_name"
+ "action_name",
+ "allowed"
],
"filter": {}
}
@@ -16659,7 +19162,12 @@
{
"role": "importer",
"permission": {
- "columns": [],
+ "columns": [
+ "color_rgb",
+ "color_name",
+ "color_id",
+ "color_comment"
+ ],
"filter": {},
"allow_aggregations": true
}
@@ -17134,7 +19642,11 @@
{
"role": "importer",
"permission": {
- "columns": [],
+ "columns": [
+ "ip_proto_name",
+ "ip_proto_id",
+ "ip_proto_comment"
+ ],
"filter": {},
"allow_aggregations": true
}
@@ -17232,6 +19744,128 @@
}
]
},
+ {
+ "table": {
+ "name": "stm_link_type",
+ "schema": "public"
+ },
+ "array_relationships": [
+ {
+ "name": "rulebase_links",
+ "using": {
+ "foreign_key_constraint_on": {
+ "column": "link_type",
+ "table": {
+ "name": "rulebase_link",
+ "schema": "public"
+ }
+ }
+ }
+ }
+ ],
+ "select_permissions": [
+ {
+ "role": "auditor",
+ "permission": {
+ "columns": [
+ "name",
+ "id"
+ ],
+ "filter": {}
+ },
+ "comment": ""
+ },
+ {
+ "role": "fw-admin",
+ "permission": {
+ "columns": [
+ "name",
+ "id"
+ ],
+ "filter": {}
+ },
+ "comment": ""
+ },
+ {
+ "role": "importer",
+ "permission": {
+ "columns": [
+ "name",
+ "id"
+ ],
+ "filter": {},
+ "allow_aggregations": true
+ },
+ "comment": ""
+ },
+ {
+ "role": "middleware-server",
+ "permission": {
+ "columns": [
+ "name",
+ "id"
+ ],
+ "filter": {}
+ },
+ "comment": ""
+ },
+ {
+ "role": "modeller",
+ "permission": {
+ "columns": [
+ "name",
+ "id"
+ ],
+ "filter": {}
+ },
+ "comment": ""
+ },
+ {
+ "role": "recertifier",
+ "permission": {
+ "columns": [
+ "name",
+ "id"
+ ],
+ "filter": {}
+ },
+ "comment": ""
+ },
+ {
+ "role": "reporter",
+ "permission": {
+ "columns": [
+ "name",
+ "id"
+ ],
+ "filter": {}
+ },
+ "comment": ""
+ },
+ {
+ "role": "reporter-viewall",
+ "permission": {
+ "columns": [
+ "name",
+ "id"
+ ],
+ "filter": {}
+ },
+ "comment": ""
+ },
+ {
+ "role": "reviewer",
+ "permission": {
+ "columns": [
+ "name",
+ "id"
+ ],
+ "filter": {}
+ },
+ "comment": ""
+ }
+ ]
+ },
{
"table": {
"name": "stm_obj_typ",
@@ -17278,7 +19912,11 @@
{
"role": "importer",
"permission": {
- "columns": [],
+ "columns": [
+ "obj_typ_name",
+ "obj_typ_id",
+ "obj_typ_comment"
+ ],
"filter": {},
"allow_aggregations": true
}
@@ -17388,7 +20026,11 @@
{
"role": "importer",
"permission": {
- "columns": [],
+ "columns": [
+ "svc_typ_name",
+ "svc_typ_id",
+ "svc_typ_comment"
+ ],
"filter": {},
"allow_aggregations": true
}
@@ -17543,7 +20185,10 @@
{
"role": "importer",
"permission": {
- "columns": [],
+ "columns": [
+ "track_name",
+ "track_id"
+ ],
"filter": {},
"allow_aggregations": true
}
@@ -17666,7 +20311,10 @@
{
"role": "importer",
"permission": {
- "columns": [],
+ "columns": [
+ "usr_typ_name",
+ "usr_typ_id"
+ ],
"filter": {},
"allow_aggregations": true
}
@@ -17757,6 +20405,24 @@
}
}
],
+ "insert_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "check": {},
+ "columns": [
+ "import_created",
+ "import_last_seen",
+ "removed",
+ "svcgrp_id",
+ "svcgrp_member_id",
+ "active",
+ "negated"
+ ]
+ },
+ "comment": ""
+ }
+ ],
"select_permissions": [
{
"role": "auditor",
@@ -17765,6 +20431,7 @@
"svcgrp_id",
"svcgrp_member_id",
"import_created",
+ "removed",
"import_last_seen",
"active",
"negated"
@@ -17779,6 +20446,7 @@
"columns": [
"import_created",
"import_last_seen",
+ "removed",
"svcgrp_id",
"svcgrp_member_id",
"active",
@@ -17787,12 +20455,29 @@
"filter": {}
}
},
+ {
+ "role": "importer",
+ "permission": {
+ "columns": [
+ "import_created",
+ "import_last_seen",
+ "removed",
+ "svcgrp_id",
+ "svcgrp_member_id",
+ "active",
+ "negated"
+ ],
+ "filter": {}
+ },
+ "comment": ""
+ },
{
"role": "middleware-server",
"permission": {
"columns": [
"import_created",
"import_last_seen",
+ "removed",
"svcgrp_id",
"svcgrp_member_id",
"active",
@@ -17810,6 +20495,7 @@
"svcgrp_member_id",
"import_created",
"import_last_seen",
+ "removed",
"active",
"negated"
],
@@ -17825,6 +20511,7 @@
"svcgrp_member_id",
"import_created",
"import_last_seen",
+ "removed",
"active",
"negated"
],
@@ -17840,26 +20527,56 @@
"svcgrp_member_id",
"import_created",
"import_last_seen",
+ "removed",
"active",
"negated"
],
"filter": {},
"allow_aggregations": true
}
- },
+ },
+ {
+ "role": "reporter-viewall",
+ "permission": {
+ "columns": [
+ "svcgrp_id",
+ "svcgrp_member_id",
+ "import_created",
+ "import_last_seen",
+ "removed",
+ "active",
+ "negated"
+ ],
+ "filter": {}
+ }
+ }
+ ],
+ "update_permissions": [
{
- "role": "reporter-viewall",
+ "role": "importer",
"permission": {
"columns": [
- "svcgrp_id",
- "svcgrp_member_id",
"import_created",
"import_last_seen",
+ "removed",
+ "svcgrp_id",
+ "svcgrp_member_id",
"active",
"negated"
],
+ "filter": {},
+ "check": {}
+ },
+ "comment": ""
+ }
+ ],
+ "delete_permissions": [
+ {
+ "role": "importer",
+ "permission": {
"filter": {}
- }
+ },
+ "comment": ""
}
]
},
@@ -17894,6 +20611,24 @@
}
}
],
+ "insert_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "check": {},
+ "columns": [
+ "removed",
+ "active",
+ "negated",
+ "import_created",
+ "import_last_seen",
+ "svcgrp_flat_id",
+ "svcgrp_flat_member_id"
+ ]
+ },
+ "comment": ""
+ }
+ ],
"select_permissions": [
{
"role": "auditor",
@@ -17903,6 +20638,7 @@
"svcgrp_flat_member_id",
"import_created",
"import_last_seen",
+ "removed",
"active",
"negated"
],
@@ -17918,16 +20654,35 @@
"negated",
"import_created",
"import_last_seen",
+ "removed",
"svcgrp_flat_id",
"svcgrp_flat_member_id"
],
"filter": {}
}
},
+ {
+ "role": "importer",
+ "permission": {
+ "columns": [
+ "removed",
+ "active",
+ "negated",
+ "import_created",
+ "import_last_seen",
+ "removed",
+ "svcgrp_flat_id",
+ "svcgrp_flat_member_id"
+ ],
+ "filter": {}
+ },
+ "comment": ""
+ },
{
"role": "middleware-server",
"permission": {
"columns": [
+ "removed",
"active",
"negated",
"import_created",
@@ -17947,6 +20702,7 @@
"svcgrp_flat_member_id",
"import_created",
"import_last_seen",
+ "removed",
"active",
"negated"
],
@@ -17962,6 +20718,7 @@
"svcgrp_flat_member_id",
"import_created",
"import_last_seen",
+ "removed",
"active",
"negated"
],
@@ -17977,6 +20734,7 @@
"svcgrp_flat_member_id",
"import_created",
"import_last_seen",
+ "removed",
"active",
"negated"
],
@@ -17992,12 +20750,41 @@
"svcgrp_flat_member_id",
"import_created",
"import_last_seen",
+ "removed",
"active",
"negated"
],
"filter": {}
}
}
+ ],
+ "update_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "columns": [
+ "removed",
+ "active",
+ "negated",
+ "import_created",
+ "import_last_seen",
+ "svcgrp_flat_id",
+ "svcgrp_flat_member_id"
+ ],
+ "filter": {},
+ "check": {}
+ },
+ "comment": ""
+ }
+ ],
+ "delete_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "filter": {}
+ },
+ "comment": ""
+ }
]
},
{
@@ -19710,6 +22497,17 @@
"filter": {}
}
},
+ {
+ "role": "importer",
+ "permission": {
+ "columns": [
+ "uiuser_id",
+ "uiuser_username"
+ ],
+ "filter": {}
+ },
+ "comment": ""
+ },
{
"role": "middleware-server",
"permission": {
@@ -20129,6 +22927,23 @@
}
}
],
+ "insert_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "check": {},
+ "columns": [
+ "import_created",
+ "import_last_seen",
+ "removed",
+ "usergrp_id",
+ "usergrp_member_id",
+ "active"
+ ]
+ },
+ "comment": ""
+ }
+ ],
"select_permissions": [
{
"role": "approver",
@@ -20137,6 +22952,7 @@
"usergrp_id",
"usergrp_member_id",
"import_created",
+ "removed",
"import_last_seen",
"active"
],
@@ -20152,6 +22968,7 @@
"usergrp_member_id",
"import_created",
"import_last_seen",
+ "removed",
"active"
],
"filter": {},
@@ -20164,6 +22981,7 @@
"columns": [
"import_created",
"import_last_seen",
+ "removed",
"usergrp_id",
"usergrp_member_id",
"active"
@@ -20179,23 +22997,43 @@
"usergrp_member_id",
"import_created",
"import_last_seen",
+ "removed",
"active"
],
"filter": {},
"allow_aggregations": true
}
},
+ {
+ "role": "importer",
+ "permission": {
+ "columns": [
+ "import_created",
+ "import_last_seen",
+ "removed",
+ "removed",
+ "usergrp_id",
+ "usergrp_member_id",
+ "active"
+ ],
+ "filter": {},
+ "allow_aggregations": true
+ },
+ "comment": ""
+ },
{
"role": "middleware-server",
"permission": {
"columns": [
"import_created",
"import_last_seen",
+ "removed",
"usergrp_id",
"usergrp_member_id",
"active"
],
- "filter": {}
+ "filter": {},
+ "allow_aggregations": true
},
"comment": ""
},
@@ -20207,6 +23045,7 @@
"usergrp_member_id",
"import_created",
"import_last_seen",
+ "removed",
"active"
],
"filter": {},
@@ -20221,6 +23060,7 @@
"usergrp_member_id",
"import_created",
"import_last_seen",
+ "removed",
"active"
],
"filter": {},
@@ -20235,6 +23075,7 @@
"usergrp_member_id",
"import_created",
"import_last_seen",
+ "removed",
"active"
],
"filter": {},
@@ -20249,6 +23090,7 @@
"usergrp_member_id",
"import_created",
"import_last_seen",
+ "removed",
"active"
],
"filter": {},
@@ -20263,6 +23105,7 @@
"usergrp_member_id",
"import_created",
"import_last_seen",
+ "removed",
"active"
],
"filter": {}
@@ -20276,6 +23119,7 @@
"usergrp_member_id",
"import_created",
"import_last_seen",
+ "removed",
"active"
],
"filter": {},
@@ -20290,12 +23134,40 @@
"usergrp_member_id",
"import_created",
"import_last_seen",
+ "removed",
"active"
],
"filter": {},
"allow_aggregations": true
}
}
+ ],
+ "update_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "columns": [
+ "import_created",
+ "import_last_seen",
+ "removed",
+ "usergrp_id",
+ "usergrp_member_id",
+ "active"
+ ],
+ "filter": {},
+ "check": {}
+ },
+ "comment": ""
+ }
+ ],
+ "delete_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "filter": {}
+ },
+ "comment": ""
+ }
]
},
{
@@ -20329,6 +23201,23 @@
}
}
],
+ "insert_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "check": {},
+ "columns": [
+ "import_created",
+ "import_last_seen",
+ "removed",
+ "usergrp_flat_id",
+ "usergrp_flat_member_id",
+ "active"
+ ]
+ },
+ "comment": ""
+ }
+ ],
"select_permissions": [
{
"role": "auditor",
@@ -20338,7 +23227,8 @@
"usergrp_flat_id",
"usergrp_flat_member_id",
"import_created",
- "import_last_seen"
+ "import_last_seen",
+ "removed"
],
"filter": {},
"allow_aggregations": true
@@ -20350,6 +23240,7 @@
"columns": [
"import_created",
"import_last_seen",
+ "removed",
"usergrp_flat_id",
"usergrp_flat_member_id",
"active"
@@ -20357,17 +23248,35 @@
"filter": {}
}
},
+ {
+ "role": "importer",
+ "permission": {
+ "columns": [
+ "import_created",
+ "import_last_seen",
+ "removed",
+ "usergrp_flat_id",
+ "usergrp_flat_member_id",
+ "active"
+ ],
+ "filter": {},
+ "allow_aggregations": true
+ },
+ "comment": ""
+ },
{
"role": "middleware-server",
"permission": {
"columns": [
"import_created",
"import_last_seen",
+ "removed",
"usergrp_flat_id",
"usergrp_flat_member_id",
"active"
],
- "filter": {}
+ "filter": {},
+ "allow_aggregations": true
},
"comment": ""
},
@@ -20379,6 +23288,7 @@
"usergrp_flat_id",
"usergrp_flat_member_id",
"import_created",
+ "removed",
"import_last_seen"
],
"filter": {},
@@ -20393,6 +23303,7 @@
"usergrp_flat_id",
"usergrp_flat_member_id",
"import_created",
+ "removed",
"import_last_seen"
],
"filter": {},
@@ -20407,6 +23318,7 @@
"usergrp_flat_id",
"usergrp_flat_member_id",
"import_created",
+ "removed",
"import_last_seen"
],
"filter": {},
@@ -20421,11 +23333,39 @@
"usergrp_flat_id",
"usergrp_flat_member_id",
"import_created",
+ "removed",
"import_last_seen"
],
"filter": {}
}
}
+ ],
+ "update_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "columns": [
+ "import_created",
+ "import_last_seen",
+ "removed",
+ "usergrp_flat_id",
+ "usergrp_flat_member_id",
+ "active"
+ ],
+ "filter": {},
+ "check": {}
+ },
+ "comment": ""
+ }
+ ],
+ "delete_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "filter": {}
+ },
+ "comment": ""
+ }
]
},
{
@@ -20635,6 +23575,40 @@
}
}
],
+ "insert_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "check": {},
+ "columns": [
+ "removed",
+ "user_create",
+ "user_id",
+ "user_last_seen",
+ "active",
+ "user_authmethod",
+ "user_firstname",
+ "user_lastname",
+ "user_name",
+ "user_valid_from",
+ "user_valid_until",
+ "last_change_admin",
+ "mgm_id",
+ "tenant_id",
+ "user_color_id",
+ "usr_typ_id",
+ "dst_restrict",
+ "src_restrict",
+ "time_restrict",
+ "user_comment",
+ "user_member_names",
+ "user_member_refs",
+ "user_uid"
+ ]
+ },
+ "comment": ""
+ }
+ ],
"select_permissions": [
{
"role": "auditor",
@@ -20656,6 +23630,7 @@
"time_restrict",
"user_create",
"user_last_seen",
+ "removed",
"user_comment",
"user_uid",
"user_firstname",
@@ -20674,6 +23649,7 @@
"user_create",
"user_id",
"user_last_seen",
+ "removed",
"active",
"user_authmethod",
"user_firstname",
@@ -20698,6 +23674,39 @@
"allow_aggregations": true
}
},
+ {
+ "role": "importer",
+ "permission": {
+ "columns": [
+ "removed",
+ "user_create",
+ "user_id",
+ "user_last_seen",
+ "active",
+ "user_authmethod",
+ "user_firstname",
+ "user_lastname",
+ "user_name",
+ "user_valid_from",
+ "user_valid_until",
+ "last_change_admin",
+ "mgm_id",
+ "tenant_id",
+ "user_color_id",
+ "usr_typ_id",
+ "dst_restrict",
+ "src_restrict",
+ "time_restrict",
+ "user_comment",
+ "user_member_names",
+ "user_member_refs",
+ "user_uid"
+ ],
+ "filter": {},
+ "allow_aggregations": true
+ },
+ "comment": ""
+ },
{
"role": "middleware-server",
"permission": {
@@ -20705,6 +23714,7 @@
"user_create",
"user_id",
"user_last_seen",
+ "removed",
"active",
"user_authmethod",
"user_firstname",
@@ -20749,6 +23759,7 @@
"time_restrict",
"user_create",
"user_last_seen",
+ "removed",
"user_comment",
"user_uid",
"user_firstname",
@@ -20784,6 +23795,7 @@
"time_restrict",
"user_create",
"user_last_seen",
+ "removed",
"user_comment",
"user_uid",
"user_firstname",
@@ -20819,6 +23831,7 @@
"time_restrict",
"user_create",
"user_last_seen",
+ "removed",
"user_comment",
"user_uid",
"user_firstname",
@@ -20854,6 +23867,7 @@
"time_restrict",
"user_create",
"user_last_seen",
+ "removed",
"user_comment",
"user_uid",
"user_firstname",
@@ -20865,6 +23879,50 @@
"allow_aggregations": true
}
}
+ ],
+ "update_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "columns": [
+ "removed",
+ "user_create",
+ "user_id",
+ "user_last_seen",
+ "active",
+ "user_authmethod",
+ "user_firstname",
+ "user_lastname",
+ "user_name",
+ "user_valid_from",
+ "user_valid_until",
+ "last_change_admin",
+ "mgm_id",
+ "tenant_id",
+ "user_color_id",
+ "usr_typ_id",
+ "dst_restrict",
+ "src_restrict",
+ "time_restrict",
+ "user_comment",
+ "user_member_names",
+ "user_member_refs",
+ "user_uid"
+ ],
+ "filter": {},
+ "check": {}
+ },
+ "comment": ""
+ }
+ ],
+ "delete_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "filter": {}
+ },
+ "comment": ""
+ }
]
},
{
@@ -20921,21 +23979,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": {
@@ -20951,22 +23994,6 @@
}
}
},
- {
- "name": "rule_metadatum",
- "using": {
- "manual_configuration": {
- "column_mapping": {
- "dev_id": "dev_id",
- "rule_uid": "rule_uid"
- },
- "insertion_order": null,
- "remote_table": {
- "name": "rule_metadata",
- "schema": "public"
- }
- }
- }
- },
{
"name": "src_zone",
"using": {
@@ -21046,7 +24073,6 @@
"owner_name",
"rule_name",
"action_id",
- "dev_id",
"mgm_id",
"owner_id",
"recert_interval",
@@ -21063,11 +24089,7 @@
"rule_uid",
"rule_last_certified"
],
- "filter": {
- "dev_id": {
- "_in": "x-hasura-visible-devices"
- }
- },
+ "filter": {},
"allow_aggregations": true
}
},
@@ -21086,7 +24108,6 @@
"owner_name",
"rule_name",
"action_id",
- "dev_id",
"mgm_id",
"owner_id",
"recert_interval",
@@ -21109,11 +24130,6 @@
"mgm_id": {
"_in": "x-hasura-visible-managements"
}
- },
- {
- "dev_id": {
- "_in": "x-hasura-visible-devices"
- }
}
]
},
@@ -21135,7 +24151,6 @@
"owner_name",
"rule_name",
"action_id",
- "dev_id",
"mgm_id",
"owner_id",
"recert_interval",
@@ -21158,11 +24173,6 @@
"mgm_id": {
"_in": "x-hasura-visible-managements"
}
- },
- {
- "dev_id": {
- "_in": "x-hasura-visible-devices"
- }
}
]
},
@@ -21184,7 +24194,6 @@
"owner_name",
"rule_name",
"action_id",
- "dev_id",
"mgm_id",
"owner_id",
"recert_interval",
@@ -21257,6 +24266,30 @@
}
}
},
+ {
+ "name": "rule_from_zones",
+ "using": {
+ "foreign_key_constraint_on": {
+ "column": "zone_id",
+ "table": {
+ "name": "rule_from_zone",
+ "schema": "public"
+ }
+ }
+ }
+ },
+ {
+ "name": "rule_to_zones",
+ "using": {
+ "foreign_key_constraint_on": {
+ "column": "zone_id",
+ "table": {
+ "name": "rule_to_zone",
+ "schema": "public"
+ }
+ }
+ }
+ },
{
"name": "rules",
"using": {
@@ -21282,6 +24315,24 @@
}
}
],
+ "insert_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "check": {},
+ "columns": [
+ "removed",
+ "zone_create",
+ "zone_last_seen",
+ "active",
+ "zone_name",
+ "mgm_id",
+ "zone_id"
+ ]
+ },
+ "comment": ""
+ }
+ ],
"select_permissions": [
{
"role": "auditor",
@@ -21290,6 +24341,7 @@
"zone_id",
"zone_create",
"zone_last_seen",
+ "removed",
"mgm_id",
"zone_name",
"active"
@@ -21304,6 +24356,7 @@
"columns": [
"zone_create",
"zone_last_seen",
+ "removed",
"active",
"zone_name",
"mgm_id",
@@ -21313,12 +24366,30 @@
"allow_aggregations": true
}
},
+ {
+ "role": "importer",
+ "permission": {
+ "columns": [
+ "removed",
+ "zone_create",
+ "zone_last_seen",
+ "active",
+ "zone_name",
+ "mgm_id",
+ "zone_id"
+ ],
+ "filter": {},
+ "allow_aggregations": true
+ },
+ "comment": ""
+ },
{
"role": "middleware-server",
"permission": {
"columns": [
"zone_create",
"zone_last_seen",
+ "removed",
"active",
"zone_name",
"mgm_id",
@@ -21335,6 +24406,7 @@
"zone_id",
"zone_create",
"zone_last_seen",
+ "removed",
"mgm_id",
"zone_name",
"active"
@@ -21354,6 +24426,7 @@
"zone_id",
"zone_create",
"zone_last_seen",
+ "removed",
"mgm_id",
"zone_name",
"active"
@@ -21373,6 +24446,7 @@
"zone_id",
"zone_create",
"zone_last_seen",
+ "removed",
"mgm_id",
"zone_name",
"active"
@@ -21393,6 +24467,7 @@
"mgm_id",
"zone_create",
"zone_last_seen",
+ "removed",
"zone_id",
"zone_name"
],
@@ -21400,6 +24475,30 @@
"allow_aggregations": true
}
}
+ ],
+ "update_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "columns": [
+ "removed",
+ "zone_last_seen",
+ "active"
+ ],
+ "filter": {},
+ "check": {}
+ },
+ "comment": ""
+ }
+ ],
+ "delete_permissions": [
+ {
+ "role": "importer",
+ "permission": {
+ "filter": {}
+ },
+ "comment": ""
+ }
]
},
{
@@ -26157,6 +29256,12 @@
"schema": "public"
}
},
+ {
+ "function": {
+ "name": "get_rules_for_owner",
+ "schema": "public"
+ }
+ },
{
"function": {
"name": "get_visible_devices_per_tenant",
diff --git a/roles/api/tasks/hasura-install.yml b/roles/api/tasks/hasura-install.yml
index 7238bf48a4..185f9140ef 100644
--- a/roles/api/tasks/hasura-install.yml
+++ b/roles/api/tasks/hasura-install.yml
@@ -2,111 +2,111 @@
- name: Install packages for python pip3 n virtualenv
package:
- name: "{{ item }}"
- state: present
+ name: "{{ item }}"
+ state: present
loop:
- - python3-pip
- - python3-virtualenv
- - python3-docker
+ - python3-pip
+ - python3-virtualenv
+ - python3-docker
become: true
- name: read dbadmin pwd from secrets file
slurp:
- src: "{{ dbadmin_password_file }}"
+ src: "{{ dbadmin_password_file }}"
register: api_user_password
become: true
- name: decode dbadmin pwd
set_fact:
- api_user_password: "{{ api_user_password['content'] | b64decode | trim }}"
+ api_user_password: "{{ api_user_password['content'] | b64decode | trim }}"
- name: read jwt public key from file as JWT secret
slurp:
- src: "{{ jwt_public_key_file }}"
+ src: "{{ jwt_public_key_file }}"
register: api_hasura_jwt_secret_dict
become: true
- name: decode key
set_fact:
- api_hasura_jwt_secret: "{{ api_hasura_jwt_secret_dict['content'] | b64decode }}"
+ api_hasura_jwt_secret: "{{ api_hasura_jwt_secret_dict['content'] | b64decode }}"
- name: make sure {{ fworch_secrets_dir }} exists
file:
- path: "{{ fworch_secrets_dir }}"
- state: directory
- mode: "0750"
- owner: "{{ fworch_user }}"
- group: "{{ postgres_group }}"
+ path: "{{ fworch_secrets_dir }}"
+ state: directory
+ mode: "0750"
+ owner: "{{ fworch_user }}"
+ group: "{{ postgres_group }}"
become: true
- name: set static hasura admin pwd for test purposes only
set_fact:
- api_hasura_admin_secret: "{{ api_hasura_admin_test_password }}"
+ api_hasura_admin_secret: "{{ api_hasura_admin_test_password }}"
when: testkeys is defined and testkeys|bool
- name: set random hasura admin password
set_fact:
- api_hasura_admin_secret: "{{ randomly_generated_pwd }}"
+ api_hasura_admin_secret: "{{ randomly_generated_pwd }}"
when: testkeys is not defined or not testkeys|bool
- name: write hasura admin password to secrets directory
copy:
- content: "{{ api_hasura_admin_secret }}\n"
- dest: "{{ fworch_secrets_dir }}/hasura_admin_pwd"
- mode: "0600"
- owner: "{{ fworch_user }}"
- group: "{{ fworch_group }}"
+ content: "{{ api_hasura_admin_secret }}\n"
+ dest: "{{ fworch_secrets_dir }}/hasura_admin_pwd"
+ mode: "0600"
+ owner: "{{ fworch_user }}"
+ group: "{{ fworch_group }}"
become: true
- name: check for existing hasura cli file
stat:
- path: "{{ api_hasura_cli_bin }}"
+ path: "{{ api_hasura_cli_bin }}"
register: api_cli_check
# only download new version of api cli, when not restoring from backup:
- name: Get Hasura release info from GitHub (authenticated)
uri:
- url: "https://api.github.com/repos/hasura/graphql-engine/releases/tags/{{ api_hasura_version }}"
- method: GET
- headers:
- Accept: "application/vnd.github+json"
- return_content: true
+ url: "https://api.github.com/repos/hasura/graphql-engine/releases/tags/{{ api_hasura_version }}"
+ method: GET
+ headers:
+ Accept: "application/vnd.github+json"
+ return_content: true
register: hasura_release
environment: "{{ proxy_env }}"
when: not api_cli_check.stat.exists
- name: Extract Hasura CLI asset id for {{ linux_architecture }}
set_fact:
- hasura_cli_asset_id: >-
- {{
- hasura_release.json.assets
- | selectattr('name', 'equalto', 'cli-hasura-linux-' ~ linux_architecture)
- | map(attribute='id')
- | list
- | first
- }}
+ hasura_cli_asset_id: >-
+ {{
+ hasura_release.json.assets
+ | selectattr('name', 'equalto', 'cli-hasura-linux-' ~ linux_architecture)
+ | map(attribute='id')
+ | list
+ | first
+ }}
when: not api_cli_check.stat.exists
- name: download {{ api_hasura_version }} hasura cli binary via authenticated GitHub access
get_url:
- url: "https://api.github.com/repos/hasura/graphql-engine/releases/assets/{{ hasura_cli_asset_id }}"
- dest: "{{ api_hasura_cli_bin }}"
- headers:
- Accept: "application/octet-stream"
- force: true
- mode: "0755"
- owner: "{{ fworch_user }}"
- group: "{{ fworch_group }}"
+ url: "https://api.github.com/repos/hasura/graphql-engine/releases/assets/{{ hasura_cli_asset_id }}"
+ dest: "{{ api_hasura_cli_bin }}"
+ headers:
+ Accept: "application/octet-stream"
+ force: true
+ mode: "0755"
+ owner: "{{ fworch_user }}"
+ group: "{{ fworch_group }}"
environment: "{{ proxy_env }}"
become: true
when:
- - not api_cli_check.stat.exists
- - hasura_cli_asset_id is defined
+ - not api_cli_check.stat.exists
+ - hasura_cli_asset_id is defined
- name: initialize hasura cli directory
command: "{{ api_hasura_cli_bin }} init {{ product_name }} --skip-update-check --endpoint http://{{ api_local_listening_ip_address }}:{{ api_port }} --admin-secret {{ api_hasura_admin_secret }}"
args:
- chdir: "{{ api_home }}"
+ chdir: "{{ api_home }}"
become: true
become_user: "{{ fworch_user }}"
environment: "{{ proxy_env }}"
@@ -114,55 +114,56 @@
- name: set hasura env variable
set_fact:
- hasura_env:
- HASURA_GRAPHQL_DATABASE_URL: "postgres://{{ api_user }}:{{ api_user_password }}@{{ fworch_db_host }}:{{ fworch_db_port }}/{{ fworch_db_name }}"
- HASURA_GRAPHQL_ENABLE_CONSOLE: "true"
- HASURA_GRAPHQL_ENABLE_TELEMETRY: "false"
- HASURA_GRAPHQL_ADMIN_SECRET: "{{ api_hasura_admin_secret }}"
- HASURA_GRAPHQL_SERVER_HOST: "127.0.0.1"
- HASURA_GRAPHQL_SERVER_PORT: "8080"
- HASURA_GRAPHQL_LOG_LEVEL: "{{ api_log_level }}"
- HASURA_GRAPHQL_ENABLED_LOG_TYPES: "{{ api_HASURA_GRAPHQL_ENABLED_LOG_TYPES }}"
- HASURA_GRAPHQL_CONSOLE_ASSETS_DIR: "/srv/console-assets"
- HASURA_GRAPHQL_V1_BOOLEAN_NULL_COLLAPSE: "true"
- HASURA_GRAPHQL_CORS_DOMAIN: "*"
- HASURA_GRAPHQL_INFER_FUNCTION_PERMISSIONS: "{{ api_HASURA_GRAPHQL_INFER_FUNCTION_PERMISSIONS }}"
- HASURA_GRAPHQL_JWT_SECRET: '
- {
- "type": "{{ api_hasura_jwt_alg|quote }}",
- "key": "{{ api_hasura_jwt_secret | regex_replace(''\n'', ''\\n'') }}",
- "claims_namespace_path": "$"
- }
- '
- HTTP_PROXY: "{{ http_proxy }}"
- HTTPS_PROXY: "{{ https_proxy }}"
- http_proxy: "{{ http_proxy }}"
- https_proxy: "{{ https_proxy }}"
- no_proxy: "{{ no_proxy }}"
- NO_PROXY: "{{ no_proxy }}"
+ hasura_env:
+ HASURA_GRAPHQL_DATABASE_URL: "postgres://{{ api_user }}:{{ api_user_password }}@{{ fworch_db_host }}:{{ fworch_db_port }}/{{ fworch_db_name }}"
+ HASURA_GRAPHQL_ENABLE_CONSOLE: "true"
+ HASURA_GRAPHQL_ENABLE_TELEMETRY: "false"
+ HASURA_GRAPHQL_ADMIN_SECRET: "{{ api_hasura_admin_secret }}"
+ HASURA_GRAPHQL_SERVER_HOST: "127.0.0.1"
+ HASURA_GRAPHQL_SERVER_PORT: "8080"
+ HASURA_GRAPHQL_LOG_LEVEL: "{{ api_log_level }}"
+ HASURA_GRAPHQL_ENABLED_LOG_TYPES: "{{ api_HASURA_GRAPHQL_ENABLED_LOG_TYPES }}"
+ HASURA_GRAPHQL_CONSOLE_ASSETS_DIR: "/srv/console-assets"
+ HASURA_GRAPHQL_V1_BOOLEAN_NULL_COLLAPSE: "true"
+ HASURA_GRAPHQL_CORS_DOMAIN: "*"
+ HASURA_GRAPHQL_INFER_FUNCTION_PERMISSIONS: "{{ api_HASURA_GRAPHQL_INFER_FUNCTION_PERMISSIONS }}"
+ HASURA_GRAPHQL_JWT_SECRET: '
+ {
+ "type": "{{ api_hasura_jwt_alg|quote }}",
+ "key": "{{ api_hasura_jwt_secret | regex_replace(''\n'', ''\\n'') }}",
+ "claims_namespace_path": "$"
+ }
+ '
+ HTTP_PROXY: "{{ http_proxy }}"
+ HTTPS_PROXY: "{{ https_proxy }}"
+ http_proxy: "{{ http_proxy }}"
+ https_proxy: "{{ https_proxy }}"
+ no_proxy: "{{ no_proxy }}"
+ NO_PROXY: "{{ no_proxy }}"
- name: show hasura env for debugging
debug:
- var: hasura_env
+ var: hasura_env
when: debug_level > '1'
- name: start hasura container
docker_container:
- name: "{{ api_container_name }}"
- image: hasura/graphql-engine:{{ api_hasura_version }}
- state: started
- network_mode: host
- networks_cli_compatible: true
- log_driver: syslog
- log_options:
- syslog-address: "{{ syslog_proto }}://{{ syslog_host }}:{{ syslog_port }}"
- syslog-facility: daemon
- tag: "{{ api_container_name }}"
- recreate: true
- env: "{{ hasura_env }}"
- container_default_behavior: no_defaults
- user: "1001:1001" # hasura user and group id
- pull: no
+ name: "{{ api_container_name }}"
+ image: hasura/graphql-engine:{{ api_hasura_version }}
+ state: started
+ network_mode: host
+ networks_cli_compatible: true
+ log_driver: syslog
+ log_options:
+ syslog-address: "{{ syslog_proto }}://{{ syslog_host }}:{{ syslog_port }}"
+ syslog-facility: daemon
+ tag: "{{ api_container_name }}"
+ recreate: true
+ image_name_mismatch: recreate
+ env: "{{ hasura_env }}"
+ container_default_behavior: no_defaults
+ user: "1001:1001" # hasura user and group id
+ pull: no
register: docker_return
become: true
become_user: "{{ fworch_user }}"
@@ -170,64 +171,76 @@
- name: show docker result
debug:
- var: docker_return
+ var: docker_return
when: debug_level > '1'
- name: Get info on container
docker_container_info:
- name: "{{ api_container_name }}"
+ name: "{{ api_container_name }}"
register: result
become: true
become_user: "{{ fworch_user }}"
- name: Print the status of the container in case of problems only
fail:
- msg: "The container status is: {{ result }}"
+ msg: "The container status is: {{ result }}"
when: result.exists and result.container['State']['Status'] == 'exited'
- name: copy hasura systemd service script
template:
- src: "{{ api_service_name }}.service.j2"
- dest: "/lib/systemd/system/{{ api_service_name }}.service"
- backup: true
- mode: "0644"
- owner: "root"
+ src: "{{ api_service_name }}.service.j2"
+ dest: "/lib/systemd/system/{{ api_service_name }}.service"
+ backup: true
+ mode: "0644"
+ owner: "root"
become: true
- name: make hasura docker container run at host startup
systemd:
- name: "{{ api_service_name }}"
- daemon_reload: true
- enabled: true
+ name: "{{ api_service_name }}"
+ daemon_reload: true
+ enabled: true
become: true
- name: wait for hasura port to become available
wait_for:
- port: "{{ api_port }}"
- host: "{{ api_local_listening_ip_address }}"
- connect_timeout: 1
- delay: 10
- timeout: 25
+ port: "{{ api_port }}"
+ host: "{{ api_local_listening_ip_address }}"
+ connect_timeout: 1
+ delay: 10
+ timeout: 25
- name: check for existing api dir from restore
stat:
- path: "{{ api_home }}/{{ product_name }}"
+ path: "{{ api_home }}/{{ product_name }}"
register: api_metadata_check
- name: import API metadata via metadata API directly from local file
uri:
- url: "http://{{ api_local_listening_ip_address }}:{{ api_port }}/v1/metadata"
- method: POST
- return_content: true
- body_format: json
- headers:
- Content-Type: application/json
- x-hasura-admin-secret: "{{ api_hasura_admin_secret }}"
- x-hasura-role: "admin"
- body: "{{ lookup('file','replace_metadata.json') | from_json }}"
+ url: "http://{{ api_local_listening_ip_address }}:{{ api_port }}/v1/metadata"
+ method: POST
+ return_content: true
+ body_format: json
+ headers:
+ Content-Type: application/json
+ x-hasura-admin-secret: "{{ api_hasura_admin_secret }}"
+ x-hasura-role: "admin"
+ body: "{{ lookup('file','replace_metadata.json') | from_json }}"
when: not api_rollback_is_running | bool
# do not install latest metadata in case of rollback
environment:
- http_proxy: ""
- https_proxy: ""
+ http_proxy: ""
+ https_proxy: ""
# do not use http proxy for metadata import
+
+- name: clear hasura service failed state
+ command: "systemctl reset-failed {{ api_service_name }}"
+ become: true
+
+- name: ensure hasura systemd service is running
+ systemd:
+ name: "{{ api_service_name }}"
+ state: started
+ enabled: true
+ daemon_reload: true
+ become: true
diff --git a/roles/api/tasks/main.yml b/roles/api/tasks/main.yml
index 0c1cf482eb..8491dcd4c9 100644
--- a/roles/api/tasks/main.yml
+++ b/roles/api/tasks/main.yml
@@ -15,6 +15,24 @@
become: true
when: "'middlewareserver' in group_names and installation_mode == 'upgrade'"
+- name: stop API service for upgrading
+ ansible.builtin.systemd:
+ name: "{{ api_service_name }}"
+ state: stopped
+ become: true
+ when: installation_mode == "upgrade"
+ failed_when: false
+
+- name: stop API container for upgrading
+ docker_container:
+ name: "{{ api_container_name }}"
+ state: stopped
+ container_default_behavior: no_defaults
+ become: true
+ become_user: "{{ fworch_user }}"
+ when: installation_mode == "upgrade"
+ failed_when: false
+
- name: stop UI for upgrading
ansible.builtin.systemd:
name: "{{ product_name }}-ui"
diff --git a/roles/api/templates/fworch-hasura-docker-api.service.j2 b/roles/api/templates/fworch-hasura-docker-api.service.j2
index 6606b02972..ae602635b4 100644
--- a/roles/api/templates/fworch-hasura-docker-api.service.j2
+++ b/roles/api/templates/fworch-hasura-docker-api.service.j2
@@ -8,8 +8,14 @@ After=network.target remote-fs.target nss-lookup.target docker.service
{%- endif %}
[Service]
Restart=on-failure
+SuccessExitStatus=0 137 143
WorkingDirectory={{ fworch_home }}
-ExecStart=/usr/bin/docker start -a {{ api_container_name }}
+ExecStart=/bin/bash -c '\
+ if /usr/bin/docker inspect -f "{% raw %}{{.State.Running}}{% endraw %}" {{ api_container_name }} 2>/dev/null | grep -q true; then \
+ exec /usr/bin/docker attach {{ api_container_name }}; \
+ fi; \
+ exec /usr/bin/docker start -a {{ api_container_name }} \
+'
ExecStop=/usr/bin/docker stop -t 2 {{ api_container_name }}
StandardOutput=journal
StandardError=journal
@@ -17,3 +23,10 @@ SyslogIdentifier={{ product_name }}-api
User={{ fworch_user }}
[Install]
WantedBy=default.target
+
+# Explanation of SuccessExitStatus:
+# extra exit codes that systemd should treat as “success” instead of failure:
+# 0 normal success.
+# 137 process exited due to signal 9 (128+9), i.e., killed/SIGKILL (common when a container is stopped forcefully).
+# 143 process exited due to signal 15 (128+15), i.e., SIGTERM (the normal stop path).
+# Now a clean container stop (SIGTERM) or a forced stop (SIGKILL) does not leave the unit in a failed state.
\ No newline at end of file
diff --git a/roles/api/templates/httpd.conf.j2 b/roles/api/templates/httpd.conf.j2
index 7b36842632..1c1ea85c4d 100644
--- a/roles/api/templates/httpd.conf.j2
+++ b/roles/api/templates/httpd.conf.j2
@@ -4,15 +4,25 @@
# DocumentRoot stays empty, only proxying
Timeout {{ apache_fwo_api_timeout }}
- ProxyRequests On
- Order deny,allow
- Allow from all
+ Require all granted
+ #
+ # LimitRequestBody 250M
+ #
+
+ # set buffer to 128KB
+ ProxyIOBufferSize 131072
+
+ Timeout 3600
+ ProxyTimeout 3600
+
# terminate https incoming calls and proxy to http
ProxyPass /api http://{{ api_local_listening_ip_address }}:{{ api_port }}/
- # websocket protocol is needed for subscriptions
+ ProxyPassReverse /api http://{{ api_local_listening_ip_address }}:{{ api_port }}/
+
+ # WebSocket support for GraphQL subscriptions
RewriteEngine on
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
@@ -21,6 +31,7 @@
ErrorLog /var/log/{{ webserver_package_name }}/error.log
TransferLog /var/log/{{ webserver_package_name }}/access.log
CustomLog /var/log/{{ webserver_package_name }}/ssl_request_log "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
+
SSLEngine on
SSLCipherSuite RSA:!EXP:!NULL:+HIGH:+MEDIUM:-LOW
SSLCertificateFile /etc/{{ webserver_package_name }}/ssl/server.crt
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/allObjects/deleteOldObjectsCascading.graphql b/roles/common/files/fwo-api-calls/allObjects/deleteOldObjectsCascading.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/allObjects/deleteOldObjectsCascading.graphql
rename to roles/common/files/fwo-api-calls/allObjects/deleteOldObjectsCascading.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/allObjects/getAllObjectDetails.graphql b/roles/common/files/fwo-api-calls/allObjects/getAllObjectDetails.graphql
similarity index 82%
rename from roles/lib/files/FWO.Api.Client/APIcalls/allObjects/getAllObjectDetails.graphql
rename to roles/common/files/fwo-api-calls/allObjects/getAllObjectDetails.graphql
index 79e50e569f..03a8a286cd 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/allObjects/getAllObjectDetails.graphql
+++ b/roles/common/files/fwo-api-calls/allObjects/getAllObjectDetails.graphql
@@ -28,7 +28,10 @@ query getAllObjectDetails (
where: {
stm_obj_typ: { obj_typ_name: { _in: $type } }
active: { _eq: $active }
- obj_last_seen: { _gte: $import_id_start }
+ _or: [
+ { removed: { _is_null: true } },
+ { removed: { _gte: $import_id_start } }
+ ]
obj_create: { _lte: $import_id_end }
obj_name: { _in: $obj_name }
obj_uid: { _in: $uid }
@@ -43,7 +46,10 @@ query getAllObjectDetails (
where: {
stm_svc_typ: { svc_typ_name: { _in: $type } }
active: { _eq: $active }
- svc_last_seen: { _gte: $import_id_start }
+ _or: [
+ { removed: { _is_null: true } },
+ { removed: { _gte: $import_id_start } }
+ ]
svc_create: { _lte: $import_id_end }
svc_name: { _in: $obj_name }
svc_uid: { _in: $uid }
@@ -58,7 +64,10 @@ query getAllObjectDetails (
where: {
stm_usr_typ: { usr_typ_name: { _in: $type } }
active: { _eq: $active }
- user_last_seen: { _gte: $import_id_start }
+ _or: [
+ { removed: { _is_null: true } },
+ { removed: { _gte: $import_id_start } }
+ ]
user_create: { _lte: $import_id_end }
user_name: { _in: $obj_name }
user_uid: { _in: $uid }
diff --git a/roles/common/files/fwo-api-calls/allObjects/upsertObjects.graphql b/roles/common/files/fwo-api-calls/allObjects/upsertObjects.graphql
new file mode 100644
index 0000000000..4994630b0d
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/allObjects/upsertObjects.graphql
@@ -0,0 +1,99 @@
+mutation upsertObjects(
+ $mgmId: Int!
+ $importId: bigint!
+ $removedNwObjectUids: [String!]!
+ $removedSvcObjectUids: [String!]!
+ $removedUserUids: [String!]!
+ $removedZoneUids: [String!]!
+ $newNwObjects: [object_insert_input!]!
+ $newSvcObjects: [service_insert_input!]!
+ $newUsers: [usr_insert_input!]!
+ $newZones: [zone_insert_input!]!
+) {
+ update_object(
+ where: {
+ mgm_id: { _eq: $mgmId }
+ obj_uid: { _in: $removedNwObjectUids }
+ removed: { _is_null: true }
+ }
+ _set: { removed: $importId, active: false }
+ ) {
+ affected_rows
+ returning {
+ obj_id
+ obj_uid
+ obj_typ_id
+ }
+ }
+ update_service(
+ where: {
+ mgm_id: { _eq: $mgmId }
+ svc_uid: { _in: $removedSvcObjectUids }
+ removed: { _is_null: true }
+ }
+ _set: { removed: $importId, active: false }
+ ) {
+ affected_rows
+ returning {
+ svc_id
+ svc_uid
+ }
+ }
+ update_usr(
+ where: {
+ mgm_id: { _eq: $mgmId }
+ user_uid: { _in: $removedUserUids }
+ removed: { _is_null: true }
+ }
+ _set: { removed: $importId, active: false }
+ ) {
+ affected_rows
+ returning {
+ user_id
+ user_uid
+ }
+ }
+ update_zone(
+ where: {
+ mgm_id: { _eq: $mgmId }
+ zone_name: { _in: $removedZoneUids }
+ removed: { _is_null: true }
+ }
+ _set: { removed: $importId, active: false }
+ ) {
+ affected_rows
+ returning {
+ zone_id
+ zone_name
+ }
+ }
+ insert_object(objects: $newNwObjects) {
+ affected_rows
+ returning {
+ obj_id
+ obj_uid
+ obj_typ_id
+ }
+ }
+ insert_service(objects: $newSvcObjects) {
+ affected_rows
+ returning {
+ svc_id
+ svc_uid
+ }
+ }
+ insert_usr(objects: $newUsers) {
+ affected_rows
+ returning {
+ user_id
+ user_uid
+ }
+ }
+ insert_zone(objects: $newZones) {
+ affected_rows
+ returning {
+ zone_id
+ zone_name
+ }
+ }
+}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/auth/addDeviceToTenant.graphql b/roles/common/files/fwo-api-calls/auth/addDeviceToTenant.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/auth/addDeviceToTenant.graphql
rename to roles/common/files/fwo-api-calls/auth/addDeviceToTenant.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/auth/addTenant.graphql b/roles/common/files/fwo-api-calls/auth/addTenant.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/auth/addTenant.graphql
rename to roles/common/files/fwo-api-calls/auth/addTenant.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/auth/addTenantNetwork.graphql b/roles/common/files/fwo-api-calls/auth/addTenantNetwork.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/auth/addTenantNetwork.graphql
rename to roles/common/files/fwo-api-calls/auth/addTenantNetwork.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/auth/addTenantToGateway.graphql b/roles/common/files/fwo-api-calls/auth/addTenantToGateway.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/auth/addTenantToGateway.graphql
rename to roles/common/files/fwo-api-calls/auth/addTenantToGateway.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/auth/addTenantToManagement.graphql b/roles/common/files/fwo-api-calls/auth/addTenantToManagement.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/auth/addTenantToManagement.graphql
rename to roles/common/files/fwo-api-calls/auth/addTenantToManagement.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/auth/assertUserExists.graphql b/roles/common/files/fwo-api-calls/auth/assertUserExists.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/auth/assertUserExists.graphql
rename to roles/common/files/fwo-api-calls/auth/assertUserExists.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/auth/deleteAllGatewaysOfTenant.graphql b/roles/common/files/fwo-api-calls/auth/deleteAllGatewaysOfTenant.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/auth/deleteAllGatewaysOfTenant.graphql
rename to roles/common/files/fwo-api-calls/auth/deleteAllGatewaysOfTenant.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/auth/deleteAllManagementsOfTenant.graphql b/roles/common/files/fwo-api-calls/auth/deleteAllManagementsOfTenant.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/auth/deleteAllManagementsOfTenant.graphql
rename to roles/common/files/fwo-api-calls/auth/deleteAllManagementsOfTenant.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/auth/deleteLdapConnection.graphql b/roles/common/files/fwo-api-calls/auth/deleteLdapConnection.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/auth/deleteLdapConnection.graphql
rename to roles/common/files/fwo-api-calls/auth/deleteLdapConnection.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/auth/deleteTenant.graphql b/roles/common/files/fwo-api-calls/auth/deleteTenant.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/auth/deleteTenant.graphql
rename to roles/common/files/fwo-api-calls/auth/deleteTenant.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/auth/deleteTenantNetwork.graphql b/roles/common/files/fwo-api-calls/auth/deleteTenantNetwork.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/auth/deleteTenantNetwork.graphql
rename to roles/common/files/fwo-api-calls/auth/deleteTenantNetwork.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/auth/deleteUser.graphql b/roles/common/files/fwo-api-calls/auth/deleteUser.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/auth/deleteUser.graphql
rename to roles/common/files/fwo-api-calls/auth/deleteUser.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/auth/getAllLdapConnections.graphql b/roles/common/files/fwo-api-calls/auth/getAllLdapConnections.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/auth/getAllLdapConnections.graphql
rename to roles/common/files/fwo-api-calls/auth/getAllLdapConnections.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/auth/getLdapConnectionForUserSearchById.graphql b/roles/common/files/fwo-api-calls/auth/getLdapConnectionForUserSearchById.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/auth/getLdapConnectionForUserSearchById.graphql
rename to roles/common/files/fwo-api-calls/auth/getLdapConnectionForUserSearchById.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/auth/getLdapConnections.graphql b/roles/common/files/fwo-api-calls/auth/getLdapConnections.graphql
similarity index 84%
rename from roles/lib/files/FWO.Api.Client/APIcalls/auth/getLdapConnections.graphql
rename to roles/common/files/fwo-api-calls/auth/getLdapConnections.graphql
index 7f0986dae9..b88dcce7b6 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/auth/getLdapConnections.graphql
+++ b/roles/common/files/fwo-api-calls/auth/getLdapConnections.graphql
@@ -2,7 +2,10 @@
# the field ldap_searchpath_for_roles is not null only for the internal ldap
query getLdapConnections {
- ldap_connection(where: {active: {_eq: true}} order_by: {ldap_connection_id: desc}) {
+ ldap_connection(
+ where: { active: { _eq: true } }
+ order_by: { ldap_connection_id: desc }
+ ) {
ldap_name
ldap_server
ldap_port
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/auth/getLdapConnectionsSubscription.graphql b/roles/common/files/fwo-api-calls/auth/getLdapConnectionsSubscription.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/auth/getLdapConnectionsSubscription.graphql
rename to roles/common/files/fwo-api-calls/auth/getLdapConnectionsSubscription.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/auth/getTenantId.graphql b/roles/common/files/fwo-api-calls/auth/getTenantId.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/auth/getTenantId.graphql
rename to roles/common/files/fwo-api-calls/auth/getTenantId.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/auth/getTenantNetworks.graphql b/roles/common/files/fwo-api-calls/auth/getTenantNetworks.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/auth/getTenantNetworks.graphql
rename to roles/common/files/fwo-api-calls/auth/getTenantNetworks.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/auth/getTenantVisibleDeviceIds.graphql b/roles/common/files/fwo-api-calls/auth/getTenantVisibleDeviceIds.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/auth/getTenantVisibleDeviceIds.graphql
rename to roles/common/files/fwo-api-calls/auth/getTenantVisibleDeviceIds.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/auth/getTenantVisibleManagementIds.graphql b/roles/common/files/fwo-api-calls/auth/getTenantVisibleManagementIds.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/auth/getTenantVisibleManagementIds.graphql
rename to roles/common/files/fwo-api-calls/auth/getTenantVisibleManagementIds.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/auth/getTenants.graphql b/roles/common/files/fwo-api-calls/auth/getTenants.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/auth/getTenants.graphql
rename to roles/common/files/fwo-api-calls/auth/getTenants.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/auth/getUserByDbId.graphql b/roles/common/files/fwo-api-calls/auth/getUserByDbId.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/auth/getUserByDbId.graphql
rename to roles/common/files/fwo-api-calls/auth/getUserByDbId.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/auth/getUserByDn.graphql b/roles/common/files/fwo-api-calls/auth/getUserByDn.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/auth/getUserByDn.graphql
rename to roles/common/files/fwo-api-calls/auth/getUserByDn.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/auth/getUserEmails.graphql b/roles/common/files/fwo-api-calls/auth/getUserEmails.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/auth/getUserEmails.graphql
rename to roles/common/files/fwo-api-calls/auth/getUserEmails.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/auth/getUsers.graphql b/roles/common/files/fwo-api-calls/auth/getUsers.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/auth/getUsers.graphql
rename to roles/common/files/fwo-api-calls/auth/getUsers.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/auth/getVisibleManagementIdsPerTenant.graphql b/roles/common/files/fwo-api-calls/auth/getVisibleManagementIdsPerTenant.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/auth/getVisibleManagementIdsPerTenant.graphql
rename to roles/common/files/fwo-api-calls/auth/getVisibleManagementIdsPerTenant.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/auth/newLdapConnection.graphql b/roles/common/files/fwo-api-calls/auth/newLdapConnection.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/auth/newLdapConnection.graphql
rename to roles/common/files/fwo-api-calls/auth/newLdapConnection.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/auth/updateLdapConnection.graphql b/roles/common/files/fwo-api-calls/auth/updateLdapConnection.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/auth/updateLdapConnection.graphql
rename to roles/common/files/fwo-api-calls/auth/updateLdapConnection.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/auth/updateTenant.graphql b/roles/common/files/fwo-api-calls/auth/updateTenant.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/auth/updateTenant.graphql
rename to roles/common/files/fwo-api-calls/auth/updateTenant.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/auth/updateUserEmail.graphql b/roles/common/files/fwo-api-calls/auth/updateUserEmail.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/auth/updateUserEmail.graphql
rename to roles/common/files/fwo-api-calls/auth/updateUserEmail.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/auth/updateUserLanguage.graphql b/roles/common/files/fwo-api-calls/auth/updateUserLanguage.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/auth/updateUserLanguage.graphql
rename to roles/common/files/fwo-api-calls/auth/updateUserLanguage.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/auth/updateUserLastLogin.graphql b/roles/common/files/fwo-api-calls/auth/updateUserLastLogin.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/auth/updateUserLastLogin.graphql
rename to roles/common/files/fwo-api-calls/auth/updateUserLastLogin.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/auth/updateUserPasswordChange.graphql b/roles/common/files/fwo-api-calls/auth/updateUserPasswordChange.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/auth/updateUserPasswordChange.graphql
rename to roles/common/files/fwo-api-calls/auth/updateUserPasswordChange.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/auth/upsertUiUser.graphql b/roles/common/files/fwo-api-calls/auth/upsertUiUser.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/auth/upsertUiUser.graphql
rename to roles/common/files/fwo-api-calls/auth/upsertUiUser.graphql
diff --git a/roles/common/files/fwo-api-calls/compliance/addCritToPolicy.graphql b/roles/common/files/fwo-api-calls/compliance/addCritToPolicy.graphql
new file mode 100644
index 0000000000..eb463cc01a
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/compliance/addCritToPolicy.graphql
@@ -0,0 +1,13 @@
+mutation addCritToPolicy(
+ $policyId: Int!
+ $criterionId: Int!
+ ) {
+ insert_compliance_policy_criterion(objects: {
+ policy_id: $policyId
+ criterion_id: $criterionId
+ }) {
+ returning {
+ insertedId: criterion_id
+ }
+ }
+}
diff --git a/roles/common/files/fwo-api-calls/compliance/addCriterion.graphql b/roles/common/files/fwo-api-calls/compliance/addCriterion.graphql
new file mode 100644
index 0000000000..e931cadc8f
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/compliance/addCriterion.graphql
@@ -0,0 +1,19 @@
+mutation addCriterion(
+ $name: String!
+ $criterionType: String!
+ $content: String
+ $comment: String
+ $importSource: String
+ ) {
+ insert_compliance_criterion(objects: {
+ name: $name
+ criterion_type: $criterionType
+ content: $content
+ comment: $comment
+ import_source: $importSource
+ }) {
+ returning {
+ insertedId: id
+ }
+ }
+}
diff --git a/roles/common/files/fwo-api-calls/compliance/addNetworkZone.graphql b/roles/common/files/fwo-api-calls/compliance/addNetworkZone.graphql
new file mode 100644
index 0000000000..19b5497253
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/compliance/addNetworkZone.graphql
@@ -0,0 +1,39 @@
+mutation addNetworkZone (
+ $name: String!
+ $description: String!
+ $idString: String!
+ $ipRanges: [compliance_ip_range_insert_input!]!
+ $superNetworkZoneId: bigint
+ $communicationSources: [compliance_network_zone_communication_insert_input!]!
+ $communicationDestinations: [compliance_network_zone_communication_insert_input!]!
+ $subNetworkZones: [compliance_network_zone_insert_input!]!
+ $criterionId: Int
+ $isAutoCalculatedInternetZone: Boolean
+ $isAutoCalculatedUndefinedInternalZone: Boolean
+) {
+ insert_compliance_network_zone_one (
+ object: {
+ super_network_zone_id: $superNetworkZoneId,
+ name: $name,
+ id_string: $idString,
+ description: $description,
+ ip_ranges: {
+ data: $ipRanges
+ },
+ network_zone_communication_destinations: {
+ data: $communicationDestinations
+ },
+ network_zone_communication_sources: {
+ data: $communicationSources
+ },
+ sub_network_zones: {
+ data: $subNetworkZones
+ },
+ criterion_id: $criterionId,
+ is_auto_calculated_internet_zone: $isAutoCalculatedInternetZone,
+ is_auto_calculated_undefined_internal_zone: $isAutoCalculatedUndefinedInternalZone
+ }
+ ) {
+ id
+ }
+}
\ No newline at end of file
diff --git a/roles/common/files/fwo-api-calls/compliance/addPolicy.graphql b/roles/common/files/fwo-api-calls/compliance/addPolicy.graphql
new file mode 100644
index 0000000000..15050a9796
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/compliance/addPolicy.graphql
@@ -0,0 +1,12 @@
+mutation addPolicy(
+ $name: String!
+ ) {
+ insert_compliance_policy(objects: {
+ name: $name
+ disabled: false
+ }) {
+ returning {
+ insertedId: id
+ }
+ }
+}
diff --git a/roles/common/files/fwo-api-calls/compliance/addViolations.graphql b/roles/common/files/fwo-api-calls/compliance/addViolations.graphql
new file mode 100644
index 0000000000..b1ef39eb49
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/compliance/addViolations.graphql
@@ -0,0 +1,10 @@
+mutation addViolations($violations: [compliance_violation_insert_input!]!) {
+ insert_compliance_violation(
+ objects: $violations
+ ) {
+ affected_rows
+ returning { id }
+
+ }
+}
+
diff --git a/roles/common/files/fwo-api-calls/compliance/disablePolicy.graphql b/roles/common/files/fwo-api-calls/compliance/disablePolicy.graphql
new file mode 100644
index 0000000000..42bf67dd6d
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/compliance/disablePolicy.graphql
@@ -0,0 +1,11 @@
+mutation disablePolicy(
+ $id: Int!
+) {
+ update_compliance_policy_by_pk(
+ pk_columns: { id: $id }
+ _set: {
+ disabled: true
+ }) {
+ updatedId: id
+ }
+}
diff --git a/roles/common/files/fwo-api-calls/compliance/getCriteria.graphql b/roles/common/files/fwo-api-calls/compliance/getCriteria.graphql
new file mode 100644
index 0000000000..aca9380197
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/compliance/getCriteria.graphql
@@ -0,0 +1,9 @@
+query getCriteria {
+ compliance_criterion (where: { removed: {_is_null: true} }){
+ id
+ name
+ criterion_type
+ content
+ comment
+ }
+}
diff --git a/roles/common/files/fwo-api-calls/compliance/getMatrices.graphql b/roles/common/files/fwo-api-calls/compliance/getMatrices.graphql
new file mode 100644
index 0000000000..d51218d040
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/compliance/getMatrices.graphql
@@ -0,0 +1,7 @@
+query getMatrices {
+ compliance_criterion (where: { criterion_type: {_eq: "Matrix"}, removed: {_is_null: true} }){
+ id
+ name
+ import_source
+ }
+}
diff --git a/roles/common/files/fwo-api-calls/compliance/getMatrixByName.graphql b/roles/common/files/fwo-api-calls/compliance/getMatrixByName.graphql
new file mode 100644
index 0000000000..44cad88176
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/compliance/getMatrixByName.graphql
@@ -0,0 +1,8 @@
+query getMatrixByName ($name: String!){
+ compliance_criterion (where: { criterion_type: {_eq: "Matrix"}, name: {_eq: $name}, removed: {_is_null: true} }){
+ id
+ name
+ comment
+ import_source
+ }
+}
diff --git a/roles/common/files/fwo-api-calls/compliance/getNetworkZonesForMatrix.graphql b/roles/common/files/fwo-api-calls/compliance/getNetworkZonesForMatrix.graphql
new file mode 100644
index 0000000000..83966c94ce
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/compliance/getNetworkZonesForMatrix.graphql
@@ -0,0 +1,35 @@
+query getNetworkZonesForMatrix ($criterionId: Int!){
+ compliance_network_zone (where: { criterion_id: {_eq: $criterionId}, removed: {_is_null: true} } order_by: {name: asc}) {
+ id
+ name
+ description
+ criterion_id
+ id_string
+ ip_ranges (where: { removed: {_is_null: true} }){
+ ip_range_start
+ ip_range_end
+ }
+ super_network_zone {
+ id
+ name
+ }
+ sub_network_zones (where: { removed: {_is_null: true} }){
+ id
+ name
+ }
+ network_zone_communication_destinations (where: { criterion_id: {_eq: $criterionId}, removed: {_is_null: true} }){
+ to_network_zone {
+ id
+ name
+ }
+ }
+ network_zone_communication_sources (where: { criterion_id: {_eq: $criterionId}, removed: {_is_null: true} }){
+ from_network_zone {
+ id
+ name
+ }
+ }
+ is_auto_calculated_internet_zone
+ is_auto_calculated_undefined_internal_zone
+ }
+}
diff --git a/roles/common/files/fwo-api-calls/compliance/getPolicies.graphql b/roles/common/files/fwo-api-calls/compliance/getPolicies.graphql
new file mode 100644
index 0000000000..eaefb6911a
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/compliance/getPolicies.graphql
@@ -0,0 +1,17 @@
+query getPolicies {
+ compliance_policy(where: {disabled: {_eq: false}}) {
+ id
+ name
+ created_date
+ disabled
+ criteria: policy_criterions (where: {removed: {_is_null: true}}) {
+ criterion: criterion {
+ content
+ criterion_type
+ id
+ name
+ }
+ }
+ }
+}
+
diff --git a/roles/common/files/fwo-api-calls/compliance/getPolicyById.graphql b/roles/common/files/fwo-api-calls/compliance/getPolicyById.graphql
new file mode 100644
index 0000000000..e5b2c51d4b
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/compliance/getPolicyById.graphql
@@ -0,0 +1,16 @@
+query getPolicyById($id: Int!) {
+ compliance_policy_by_pk(id: $id) {
+ id
+ name
+ created_date
+ disabled
+ criteria: policy_criterions(where: { removed: { _is_null: true } }) {
+ criterion: criterion {
+ content
+ criterion_type
+ id
+ name
+ }
+ }
+ }
+}
diff --git a/roles/common/files/fwo-api-calls/compliance/getPolicyIdsForCrit.graphql b/roles/common/files/fwo-api-calls/compliance/getPolicyIdsForCrit.graphql
new file mode 100644
index 0000000000..a8fc52e861
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/compliance/getPolicyIdsForCrit.graphql
@@ -0,0 +1,10 @@
+query getPolicyIdsForCrit ($critId: Int!){
+ compliance_policy_criterion(where: {
+ removed: {_is_null: true}
+ criterion_id: { _eq: $critId }
+ }) {
+ criterion_id
+ policy_id
+ }
+}
+
\ No newline at end of file
diff --git a/roles/common/files/fwo-api-calls/compliance/getViolations.graphql b/roles/common/files/fwo-api-calls/compliance/getViolations.graphql
new file mode 100644
index 0000000000..a321e27702
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/compliance/getViolations.graphql
@@ -0,0 +1,14 @@
+query getViolations {
+ compliance_violation {
+ id
+ criterion_id
+ details
+ found_date
+ policy_id
+ removed_date
+ risk_score
+ rule_id
+ rule_uid
+ mgmt_uid
+ }
+}
diff --git a/roles/common/files/fwo-api-calls/compliance/getViolationsByRuleID.graphql b/roles/common/files/fwo-api-calls/compliance/getViolationsByRuleID.graphql
new file mode 100644
index 0000000000..4e4b9b9a4d
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/compliance/getViolationsByRuleID.graphql
@@ -0,0 +1,12 @@
+query getViolationsByRuleID($ruleId: bigint) {
+ compliance_violation(where: { rule_id: { _eq: $ruleId } }) {
+ id
+ criterion_id
+ details
+ found_date
+ policy_id
+ removed_date
+ risk_score
+ rule_id
+ }
+}
diff --git a/roles/common/files/fwo-api-calls/compliance/getViolationsByRuleUid.graphql b/roles/common/files/fwo-api-calls/compliance/getViolationsByRuleUid.graphql
new file mode 100644
index 0000000000..71dc7bad3e
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/compliance/getViolationsByRuleUid.graphql
@@ -0,0 +1,14 @@
+query getViolationsByRuleID($ruleUid: String, $mgmtUid: String) {
+ compliance_violation(where: {rule_uid: {_eq: $ruleUid} mgmt_uid: {_eq: $mgmtUid}}) {
+ id
+ criterion_id
+ details
+ found_date
+ policy_id
+ removed_date
+ risk_score
+ rule_id
+ rule_uid
+ mgmt_uid
+ }
+}
diff --git a/roles/common/files/fwo-api-calls/compliance/getViolationsChunk.graphql b/roles/common/files/fwo-api-calls/compliance/getViolationsChunk.graphql
new file mode 100644
index 0000000000..eae15ac5aa
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/compliance/getViolationsChunk.graphql
@@ -0,0 +1,12 @@
+query getViolationsChunk($limit: Int, $offset: Int, $active: Boolean) {
+ compliance_violation(limit: $limit, offset: $offset, order_by: { id: asc }) {
+ id
+ criterion_id
+ details
+ found_date
+ policy_id
+ removed_date
+ risk_score
+ rule_id
+ }
+}
\ No newline at end of file
diff --git a/roles/common/files/fwo-api-calls/compliance/removeCritFromPolicy.graphql b/roles/common/files/fwo-api-calls/compliance/removeCritFromPolicy.graphql
new file mode 100644
index 0000000000..b93806eff3
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/compliance/removeCritFromPolicy.graphql
@@ -0,0 +1,17 @@
+mutation removeCritFromPolicy(
+ $policyId: Int!
+ $criterionId: Int!
+ $removed: timestamptz
+) {
+ update_compliance_policy_criterion(
+ where: {
+ policy_id: {_eq: $policyId}
+ criterion_id: {_eq: $criterionId}
+ removed: {_is_null: true}
+ }
+ _set: {
+ removed: $removed
+ }) {
+ affected_rows
+ }
+}
diff --git a/roles/common/files/fwo-api-calls/compliance/removeCriterion.graphql b/roles/common/files/fwo-api-calls/compliance/removeCriterion.graphql
new file mode 100644
index 0000000000..d172e7ecd3
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/compliance/removeCriterion.graphql
@@ -0,0 +1,12 @@
+mutation removeCriterion(
+ $id: Int!
+ $removed: timestamptz
+) {
+ update_compliance_criterion_by_pk(
+ pk_columns: { id: $id }
+ _set: {
+ removed: $removed
+ }) {
+ updatedId: id
+ }
+}
diff --git a/roles/common/files/fwo-api-calls/compliance/removeNetworkZone.graphql b/roles/common/files/fwo-api-calls/compliance/removeNetworkZone.graphql
new file mode 100644
index 0000000000..cbd00d8167
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/compliance/removeNetworkZone.graphql
@@ -0,0 +1,39 @@
+mutation removeNetworkZone (
+ $id: bigint!
+ $removed: timestamptz
+ $deleteIpRangesExp: [compliance_ip_range_bool_exp!]!
+ $deleteZoneCommunicationExp: [compliance_network_zone_communication_bool_exp!]!
+) {
+ update_compliance_network_zone_by_pk (
+ pk_columns: { id: $id }
+ _set: {
+ removed: $removed
+ }) {
+ updatedId: id
+ }
+
+ update_compliance_ip_range (
+ where: {
+ network_zone_id: {_eq: $id},
+ _or: $deleteIpRangesExp
+ removed: {_is_null: true}
+ }
+ _set: {
+ removed: $removed
+ }
+ ) {
+ affected_rows
+ }
+
+ update_compliance_network_zone_communication (
+ where: {
+ _or: $deleteZoneCommunicationExp
+ removed: {_is_null: true}
+ },
+ _set: {
+ removed: $removed
+ }
+ ) {
+ affected_rows
+ }
+}
diff --git a/roles/common/files/fwo-api-calls/compliance/removeViolations.graphql b/roles/common/files/fwo-api-calls/compliance/removeViolations.graphql
new file mode 100644
index 0000000000..13672a27bd
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/compliance/removeViolations.graphql
@@ -0,0 +1,8 @@
+mutation removeViolations($ids: [bigint!]!, $removedAt: timestamptz!) {
+ update_compliance_violation(
+ where: { id: { _in: $ids } },
+ _set: { removed_date: $removedAt }
+ ) {
+ affected_rows
+ }
+}
diff --git a/roles/common/files/fwo-api-calls/compliance/updateCriterionMetadata.graphql b/roles/common/files/fwo-api-calls/compliance/updateCriterionMetadata.graphql
new file mode 100644
index 0000000000..4b39d6935c
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/compliance/updateCriterionMetadata.graphql
@@ -0,0 +1,15 @@
+mutation updateCriterionMetadata(
+ $id: Int!
+ $importSource: String
+ $comment: String
+ ) {
+ update_compliance_criterion_by_pk(
+ pk_columns: { id: $id },
+ _set: {
+ comment: $comment
+ import_source: $importSource
+ }
+ ) {
+ id
+ }
+}
diff --git a/roles/common/files/fwo-api-calls/compliance/updateNetworkZone.graphql b/roles/common/files/fwo-api-calls/compliance/updateNetworkZone.graphql
new file mode 100644
index 0000000000..1cbc0d8e0d
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/compliance/updateNetworkZone.graphql
@@ -0,0 +1,87 @@
+mutation updateNetworkZone (
+ $networkZoneId: bigint!
+ $name: String!
+ $description: String!
+ $superNetworkZoneId: bigint
+ $addIpRanges: [compliance_ip_range_insert_input!]!
+ $deleteIpRangesExp: [compliance_ip_range_bool_exp!]!
+ $addZoneCommunication: [compliance_network_zone_communication_insert_input!]!
+ $deleteZoneCommunicationExp: [compliance_network_zone_communication_bool_exp!]!
+ $addSubZonesExp: [compliance_network_zone_bool_exp!]!
+ $deleteSubZonesExp: [compliance_network_zone_bool_exp!]!
+ $removed: timestamptz
+ $isAutoCalculatedInternetZone: Boolean)
+{
+ update_compliance_network_zone (
+ where: {id: {_eq: $networkZoneId}}
+ _set: {
+ name: $name,
+ description: $description,
+ super_network_zone_id: $superNetworkZoneId,
+ is_auto_calculated_internet_zone: $isAutoCalculatedInternetZone
+ }
+ ) {
+ affected_rows
+ }
+
+ update_compliance_ip_range (
+ where: {
+ network_zone_id: {_eq: $networkZoneId},
+ _or: $deleteIpRangesExp
+ removed: {_is_null: true}
+ }
+ _set: {
+ removed: $removed
+ }
+ ) {
+ affected_rows
+ }
+
+ insert_compliance_ip_range (
+ objects: $addIpRanges
+ ) {
+ affected_rows
+ }
+
+ update_compliance_network_zone_communication (
+ where: {
+ _or: $deleteZoneCommunicationExp
+ removed: {_is_null: true}
+ },
+ _set: {
+ removed: $removed
+ }
+ ) {
+ affected_rows
+ }
+
+ insert_compliance_network_zone_communication (
+ objects: $addZoneCommunication
+ ) {
+ affected_rows
+ }
+
+ update_compliance_network_zone_many (
+ updates: [
+ {
+ where: {
+ _or: $deleteSubZonesExp
+ removed: {_is_null: true}
+ }
+ _set: {
+ super_network_zone_id: null
+ }
+ },
+ {
+ where: {
+ _or: $addSubZonesExp
+ }
+ _set: {
+ super_network_zone_id: $networkZoneId
+ }
+ }
+ ]
+ ) {
+ affected_rows
+ }
+}
\ No newline at end of file
diff --git a/roles/common/files/fwo-api-calls/compliance/updateNetworkZoneCommunication.graphql b/roles/common/files/fwo-api-calls/compliance/updateNetworkZoneCommunication.graphql
new file mode 100644
index 0000000000..4d550b9a6c
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/compliance/updateNetworkZoneCommunication.graphql
@@ -0,0 +1,24 @@
+mutation updateNetworkZoneCommunication(
+ $deleteZoneCommunicationExp: [compliance_network_zone_communication_bool_exp!]!
+ $addZoneCommunication: [compliance_network_zone_communication_insert_input!]!
+ $removed: timestamptz
+)
+{
+ update_compliance_network_zone_communication (
+ where: {
+ _or: $deleteZoneCommunicationExp
+ removed: {_is_null: true}
+ },
+ _set: {
+ removed: $removed
+ }
+ ) {
+ affected_rows
+ }
+
+ insert_compliance_network_zone_communication (
+ objects: $addZoneCommunication
+ ) {
+ affected_rows
+ }
+}
diff --git a/roles/common/files/fwo-api-calls/compliance/updateViolationById.graphql b/roles/common/files/fwo-api-calls/compliance/updateViolationById.graphql
new file mode 100644
index 0000000000..28d4fe9f0a
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/compliance/updateViolationById.graphql
@@ -0,0 +1,8 @@
+mutation updateViolation($id: bigint!, $changes: compliance_violation_set_input!) {
+ update_compliance_violation_by_pk(
+ pk_columns: { id: $id },
+ _set: $changes
+ ) {
+ id
+ }
+}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/config/addConfigItem.graphql b/roles/common/files/fwo-api-calls/config/addConfigItem.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/config/addConfigItem.graphql
rename to roles/common/files/fwo-api-calls/config/addConfigItem.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/config/deleteCustomText.graphql b/roles/common/files/fwo-api-calls/config/deleteCustomText.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/config/deleteCustomText.graphql
rename to roles/common/files/fwo-api-calls/config/deleteCustomText.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/config/getConfigItemByKey.graphql b/roles/common/files/fwo-api-calls/config/getConfigItemByKey.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/config/getConfigItemByKey.graphql
rename to roles/common/files/fwo-api-calls/config/getConfigItemByKey.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/config/getConfigItemsByUser.graphql b/roles/common/files/fwo-api-calls/config/getConfigItemsByUser.graphql
similarity index 98%
rename from roles/lib/files/FWO.Api.Client/APIcalls/config/getConfigItemsByUser.graphql
rename to roles/common/files/fwo-api-calls/config/getConfigItemsByUser.graphql
index 0c1f475864..4ce351abbe 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/config/getConfigItemsByUser.graphql
+++ b/roles/common/files/fwo-api-calls/config/getConfigItemsByUser.graphql
@@ -3,4 +3,4 @@ query getConfigItemsByUser($user: Int) {
config_key
config_value
}
-}
\ No newline at end of file
+}
diff --git a/roles/common/files/fwo-api-calls/config/getConfigValue.graphql b/roles/common/files/fwo-api-calls/config/getConfigValue.graphql
new file mode 100644
index 0000000000..ea6a4126f4
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/config/getConfigValue.graphql
@@ -0,0 +1,5 @@
+query getConfigValue($key: String) {
+ config(where: {config_key: {_eq: $key}}) {
+ config_value
+ }
+}
diff --git a/roles/common/files/fwo-api-calls/config/getConfigValuesByKeyFilter.graphql b/roles/common/files/fwo-api-calls/config/getConfigValuesByKeyFilter.graphql
new file mode 100644
index 0000000000..dc95fae647
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/config/getConfigValuesByKeyFilter.graphql
@@ -0,0 +1,5 @@
+query getConfigValuesByKeyFilter($keyFilter: String) {
+ config(where: {config_key: {_ilike: $keyFilter}}) {
+ config_key config_value
+ }
+}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/config/getCustomTextsPerLanguage.graphql b/roles/common/files/fwo-api-calls/config/getCustomTextsPerLanguage.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/config/getCustomTextsPerLanguage.graphql
rename to roles/common/files/fwo-api-calls/config/getCustomTextsPerLanguage.graphql
diff --git a/roles/common/files/fwo-api-calls/config/getDataRetention.graphql b/roles/common/files/fwo-api-calls/config/getDataRetention.graphql
new file mode 100644
index 0000000000..8c754a2216
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/config/getDataRetention.graphql
@@ -0,0 +1,5 @@
+query getDataRetention {
+ config(where: {config_key: {_eq: "dataRetentionTime"}, config_user: {_eq: 0}}) {
+ retentionInDays: config_value
+ }
+}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/config/getLanguages.graphql b/roles/common/files/fwo-api-calls/config/getLanguages.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/config/getLanguages.graphql
rename to roles/common/files/fwo-api-calls/config/getLanguages.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/config/getTexts.graphql b/roles/common/files/fwo-api-calls/config/getTexts.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/config/getTexts.graphql
rename to roles/common/files/fwo-api-calls/config/getTexts.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/config/getTextsPerLanguage.graphql b/roles/common/files/fwo-api-calls/config/getTextsPerLanguage.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/config/getTextsPerLanguage.graphql
rename to roles/common/files/fwo-api-calls/config/getTextsPerLanguage.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/config/subscribeAutodiscoveryConfigChanges.graphql b/roles/common/files/fwo-api-calls/config/subscribeAutodiscoveryConfigChanges.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/config/subscribeAutodiscoveryConfigChanges.graphql
rename to roles/common/files/fwo-api-calls/config/subscribeAutodiscoveryConfigChanges.graphql
diff --git a/roles/common/files/fwo-api-calls/config/subscribeComplianceCheckConfigChanges.graphql b/roles/common/files/fwo-api-calls/config/subscribeComplianceCheckConfigChanges.graphql
new file mode 100644
index 0000000000..7d455f3534
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/config/subscribeComplianceCheckConfigChanges.graphql
@@ -0,0 +1,48 @@
+subscription subscribeComplianceCheckConfigChanges {
+ config(
+ where: {
+ _or: [
+ { config_key: { _eq: "importCheckCertificates" } }
+ { config_key: { _eq: "importSuppressCertificateWarnings" } }
+ { config_key: { _eq: "complianceCheckInternetZoneObject" } }
+ { config_key: { _eq: "complianceCheckMailBody" } }
+ { config_key: { _eq: "complianceCheckMailRecipients" } }
+ { config_key: { _eq: "complianceCheckMailSubject" } }
+ { config_key: { _eq: "complianceCheckMaxPrintedViolations" } }
+ { config_key: { _eq: "complianceCheckPolicy" } }
+ { config_key: { _eq: "complianceCheckRelevantManagements" } }
+ { config_key: { _eq: "complianceCheckScheduledDiffReports" } }
+ { config_key: { _eq: "complianceCheckSleepTime" } }
+ { config_key: { _eq: "complianceCheckStartAt" } }
+ { config_key: { _eq: "autoCalculateInternetZone" } }
+ { config_key: { _eq: "autoCalculateUndefinedInternalZone" } }
+ { config_key: { _eq: "internalZoneRange_10_0_0_0_8" } }
+ { config_key: { _eq: "internalZoneRange_172_16_0_0_12" } }
+ { config_key: { _eq: "internalZoneRange_192_168_0_0_16" } }
+ { config_key: { _eq: "internalZoneRange_0_0_0_0_8" } }
+ { config_key: { _eq: "internalZoneRange_127_0_0_0_8" } }
+ { config_key: { _eq: "internalZoneRange_169_254_0_0_16" } }
+ { config_key: { _eq: "internalZoneRange_224_0_0_0_4" } }
+ { config_key: { _eq: "internalZoneRange_240_0_0_0_4" } }
+ { config_key: { _eq: "internalZoneRange_255_255_255_255_32" } }
+ { config_key: { _eq: "internalZoneRange_192_0_2_0_24" } }
+ { config_key: { _eq: "internalZoneRange_198_51_100_0_24" } }
+ { config_key: { _eq: "internalZoneRange_203_0_113_0_24" } }
+ { config_key: { _eq: "internalZoneRange_100_64_0_0_10" } }
+ { config_key: { _eq: "internalZoneRange_192_0_0_0_24" } }
+ { config_key: { _eq: "internalZoneRange_192_88_99_0_24" } }
+ { config_key: { _eq: "internalZoneRange_198_18_0_0_15" } }
+ { config_key: { _eq: "autoCalculatedZonesAtTheEnd" } }
+ { config_key: { _eq: "treatDynamicAndDomainObjectsAsInternet" } }
+ { config_key: { _eq: "showShortColumnsInComplianceReports" } }
+ { config_key: { _eq: "complianceCheckElementsPerFetch" } }
+ { config_key: { _eq: "complianceCheckAvailableProcessors" } }
+
+ ]
+ }
+ limit: 35
+ ) {
+ config_key
+ config_value
+ }
+}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/config/subscribeConfigChangesByUser.graphql b/roles/common/files/fwo-api-calls/config/subscribeConfigChangesByUser.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/config/subscribeConfigChangesByUser.graphql
rename to roles/common/files/fwo-api-calls/config/subscribeConfigChangesByUser.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/config/subscribeDailyCheckConfigChanges.graphql b/roles/common/files/fwo-api-calls/config/subscribeDailyCheckConfigChanges.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/config/subscribeDailyCheckConfigChanges.graphql
rename to roles/common/files/fwo-api-calls/config/subscribeDailyCheckConfigChanges.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/config/subscribeExternalRequestConfigChanges.graphql b/roles/common/files/fwo-api-calls/config/subscribeExternalRequestConfigChanges.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/config/subscribeExternalRequestConfigChanges.graphql
rename to roles/common/files/fwo-api-calls/config/subscribeExternalRequestConfigChanges.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/config/subscribeImportAppDataConfigChanges.graphql b/roles/common/files/fwo-api-calls/config/subscribeImportAppDataConfigChanges.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/config/subscribeImportAppDataConfigChanges.graphql
rename to roles/common/files/fwo-api-calls/config/subscribeImportAppDataConfigChanges.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/config/subscribeImportNotifyConfigChanges.graphql b/roles/common/files/fwo-api-calls/config/subscribeImportNotifyConfigChanges.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/config/subscribeImportNotifyConfigChanges.graphql
rename to roles/common/files/fwo-api-calls/config/subscribeImportNotifyConfigChanges.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/config/subscribeImportSubnetDataConfigChanges.graphql b/roles/common/files/fwo-api-calls/config/subscribeImportSubnetDataConfigChanges.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/config/subscribeImportSubnetDataConfigChanges.graphql
rename to roles/common/files/fwo-api-calls/config/subscribeImportSubnetDataConfigChanges.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/config/subscribeVarianceAnalysisConfigChanges.graphql b/roles/common/files/fwo-api-calls/config/subscribeVarianceAnalysisConfigChanges.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/config/subscribeVarianceAnalysisConfigChanges.graphql
rename to roles/common/files/fwo-api-calls/config/subscribeVarianceAnalysisConfigChanges.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/config/updateConfigItem.graphql b/roles/common/files/fwo-api-calls/config/updateConfigItem.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/config/updateConfigItem.graphql
rename to roles/common/files/fwo-api-calls/config/updateConfigItem.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/config/upsertConfig.graphql b/roles/common/files/fwo-api-calls/config/upsertConfig.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/config/upsertConfig.graphql
rename to roles/common/files/fwo-api-calls/config/upsertConfig.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/config/upsertConfigItem.graphql b/roles/common/files/fwo-api-calls/config/upsertConfigItem.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/config/upsertConfigItem.graphql
rename to roles/common/files/fwo-api-calls/config/upsertConfigItem.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/config/upsertConfigItems.graphql b/roles/common/files/fwo-api-calls/config/upsertConfigItems.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/config/upsertConfigItems.graphql
rename to roles/common/files/fwo-api-calls/config/upsertConfigItems.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/config/upsertCustomText.graphql b/roles/common/files/fwo-api-calls/config/upsertCustomText.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/config/upsertCustomText.graphql
rename to roles/common/files/fwo-api-calls/config/upsertCustomText.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/device/changeDeviceState.graphql b/roles/common/files/fwo-api-calls/device/changeDeviceState.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/device/changeDeviceState.graphql
rename to roles/common/files/fwo-api-calls/device/changeDeviceState.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/device/changeManagementState.graphql b/roles/common/files/fwo-api-calls/device/changeManagementState.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/device/changeManagementState.graphql
rename to roles/common/files/fwo-api-calls/device/changeManagementState.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/device/deleteCredential.graphql b/roles/common/files/fwo-api-calls/device/deleteCredential.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/device/deleteCredential.graphql
rename to roles/common/files/fwo-api-calls/device/deleteCredential.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/device/deleteDevice.graphql b/roles/common/files/fwo-api-calls/device/deleteDevice.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/device/deleteDevice.graphql
rename to roles/common/files/fwo-api-calls/device/deleteDevice.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/device/deleteManagement.graphql b/roles/common/files/fwo-api-calls/device/deleteManagement.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/device/deleteManagement.graphql
rename to roles/common/files/fwo-api-calls/device/deleteManagement.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/device/fragments/deviceDetails.graphql b/roles/common/files/fwo-api-calls/device/fragments/deviceDetails.graphql
similarity index 96%
rename from roles/lib/files/FWO.Api.Client/APIcalls/device/fragments/deviceDetails.graphql
rename to roles/common/files/fwo-api-calls/device/fragments/deviceDetails.graphql
index 9c7767d604..0b1ffef4ee 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/device/fragments/deviceDetails.graphql
+++ b/roles/common/files/fwo-api-calls/device/fragments/deviceDetails.graphql
@@ -3,6 +3,7 @@ fragment deviceDetails on device
{
id: dev_id
name: dev_name
+ uid: dev_uid
deviceType: stm_dev_typ { ...deviceTypeDetails }
management {
id: mgm_id
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/device/fragments/deviceTypeDetails.graphql b/roles/common/files/fwo-api-calls/device/fragments/deviceTypeDetails.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/device/fragments/deviceTypeDetails.graphql
rename to roles/common/files/fwo-api-calls/device/fragments/deviceTypeDetails.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/device/fragments/importCredentials.graphql b/roles/common/files/fwo-api-calls/device/fragments/importCredentials.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/device/fragments/importCredentials.graphql
rename to roles/common/files/fwo-api-calls/device/fragments/importCredentials.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/device/fragments/importCredentialsWithoutSecrets.graphql b/roles/common/files/fwo-api-calls/device/fragments/importCredentialsWithoutSecrets.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/device/fragments/importCredentialsWithoutSecrets.graphql
rename to roles/common/files/fwo-api-calls/device/fragments/importCredentialsWithoutSecrets.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/device/fragments/managementDetails.graphql b/roles/common/files/fwo-api-calls/device/fragments/managementDetails.graphql
similarity index 82%
rename from roles/lib/files/FWO.Api.Client/APIcalls/device/fragments/managementDetails.graphql
rename to roles/common/files/fwo-api-calls/device/fragments/managementDetails.graphql
index 31843d2168..c1d3121468 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/device/fragments/managementDetails.graphql
+++ b/roles/common/files/fwo-api-calls/device/fragments/managementDetails.graphql
@@ -1,11 +1,12 @@
fragment ManagementDetails on management {
id: mgm_id
name: mgm_name
+ uid: mgm_uid
hostname: ssh_hostname
port: ssh_port
import_credential_id
import_credential { ...ImportCredentialDetails }
- deviceType: stm_dev_typ { ...deviceTypeDetails } superManager: multi_device_manager_id
+ deviceType: stm_dev_typ { ...deviceTypeDetails }
configPath: config_path
domainUid: domain_uid
cloudSubscriptionId: cloud_subscription_id
@@ -23,7 +24,11 @@ fragment ManagementDetails on management {
devices {
id: dev_id
name: dev_name
+ uid: dev_uid
importDisabled: do_not_import
local_rulebase_name: local_rulebase_name
}
+ isSuperManager: is_super_manager
+ multi_device_manager_id
+ ...subManagements
}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/device/fragments/managementDetailsWithoutSecrets.graphql b/roles/common/files/fwo-api-calls/device/fragments/managementDetailsWithoutSecrets.graphql
similarity index 87%
rename from roles/lib/files/FWO.Api.Client/APIcalls/device/fragments/managementDetailsWithoutSecrets.graphql
rename to roles/common/files/fwo-api-calls/device/fragments/managementDetailsWithoutSecrets.graphql
index 8416f789ad..70a0caeec8 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/device/fragments/managementDetailsWithoutSecrets.graphql
+++ b/roles/common/files/fwo-api-calls/device/fragments/managementDetailsWithoutSecrets.graphql
@@ -1,11 +1,13 @@
fragment ManagementDetailsWithoutSecrets on management {
id: mgm_id
name: mgm_name
+ uid: mgm_uid
hostname: ssh_hostname
port: ssh_port
import_credential_id
import_credential { ...ImportCredentialDetailsWithoutSecrets }
- deviceType: stm_dev_typ { ...deviceTypeDetails } superManager: multi_device_manager_id
+ deviceType: stm_dev_typ { ...deviceTypeDetails }
+ multi_device_manager_id
configPath: config_path
domainUid: domain_uid
cloudSubscriptionId: cloud_subscription_id
@@ -23,6 +25,7 @@ fragment ManagementDetailsWithoutSecrets on management {
devices {
id: dev_id
name: dev_name
+ uid: dev_uid
importDisabled: do_not_import
local_rulebase_name: local_rulebase_name
}
diff --git a/roles/common/files/fwo-api-calls/device/fragments/subManagements.graphql b/roles/common/files/fwo-api-calls/device/fragments/subManagements.graphql
new file mode 100644
index 0000000000..c47a42e2eb
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/device/fragments/subManagements.graphql
@@ -0,0 +1,34 @@
+fragment subManagements on management {
+ subManagers: managementByMultiDeviceManagerId {
+ id: mgm_id
+ name: mgm_name
+ uid: mgm_uid
+ hostname: ssh_hostname
+ port: ssh_port
+ deviceType: stm_dev_typ {
+ ...deviceTypeDetails
+ }
+ configPath: config_path
+ domainUid: domain_uid
+ importDisabled: do_not_import
+ hideInUi: hide_in_gui
+ extMgtData: ext_mgm_data
+ creationDate: mgm_create
+ updateDate: mgm_update
+ devices {
+ id: dev_id
+ name: dev_name
+ uid: dev_uid
+ importDisabled: do_not_import
+ local_rulebase_name: local_rulebase_name
+ }
+ importerHostname: importer_hostname
+ import_credential {
+ ...ImportCredentialDetails
+ }
+ isSuperManager: is_super_manager
+ subManagers: managementByMultiDeviceManagerId {
+ mgm_id
+ }
+ }
+}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/device/getCredentials.graphql b/roles/common/files/fwo-api-calls/device/getCredentials.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/device/getCredentials.graphql
rename to roles/common/files/fwo-api-calls/device/getCredentials.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/device/getCredentialsWithoutSecrets.graphql b/roles/common/files/fwo-api-calls/device/getCredentialsWithoutSecrets.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/device/getCredentialsWithoutSecrets.graphql
rename to roles/common/files/fwo-api-calls/device/getCredentialsWithoutSecrets.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/device/getDeviceDetails.graphql b/roles/common/files/fwo-api-calls/device/getDeviceDetails.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/device/getDeviceDetails.graphql
rename to roles/common/files/fwo-api-calls/device/getDeviceDetails.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/device/getDeviceTypeDetails.graphql b/roles/common/files/fwo-api-calls/device/getDeviceTypeDetails.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/device/getDeviceTypeDetails.graphql
rename to roles/common/files/fwo-api-calls/device/getDeviceTypeDetails.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/device/getDevicesByManagement.graphql b/roles/common/files/fwo-api-calls/device/getDevicesByManagement.graphql
similarity index 94%
rename from roles/lib/files/FWO.Api.Client/APIcalls/device/getDevicesByManagement.graphql
rename to roles/common/files/fwo-api-calls/device/getDevicesByManagement.graphql
index 2a8f23a1d8..d519310e92 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/device/getDevicesByManagement.graphql
+++ b/roles/common/files/fwo-api-calls/device/getDevicesByManagement.graphql
@@ -12,6 +12,7 @@ query getDevicesByManagement($devIds:[Int!]) {
) {
id: mgm_id
name: mgm_name
+ uid: mgm_uid
devices(
where: {
dev_id:{_in:$devIds}
@@ -22,6 +23,7 @@ query getDevicesByManagement($devIds:[Int!]) {
) {
id: dev_id
name: dev_name
+ uid: dev_uid
}
}
}
diff --git a/roles/common/files/fwo-api-calls/device/getDevicesWithRulebaseLinks.graphql b/roles/common/files/fwo-api-calls/device/getDevicesWithRulebaseLinks.graphql
new file mode 100644
index 0000000000..2643afece0
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/device/getDevicesWithRulebaseLinks.graphql
@@ -0,0 +1,14 @@
+query getDevicesWithRulebaseLinks($mgmId: Int!)
+{
+ devices (where: {
+ mgm_id: { _eq:$mgmId }
+ hide_in_gui: { _eq: false }
+ stm_dev_typ: { is_pure_routing_device: { _eq: false } }
+ })
+ {
+ id: dev_id
+ rulebase_links (where: {removed:{_is_null:true} }) {
+ from_rule_id
+ }
+ }
+}
diff --git a/roles/common/files/fwo-api-calls/device/getGatewayId.graphql b/roles/common/files/fwo-api-calls/device/getGatewayId.graphql
new file mode 100644
index 0000000000..7510a3a014
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/device/getGatewayId.graphql
@@ -0,0 +1,10 @@
+query getGatewayId($gwName:String!, $mgmUid: String!) {
+ device(
+ where: {
+ dev_name:{_eq:$gwName}
+ management: { mgm_uid: { _eq: $mgmUid } }
+ }
+ ) {
+ id: dev_id
+ }
+}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/device/getManagementDetailsWithoutSecrets.graphql b/roles/common/files/fwo-api-calls/device/getManagementDetailsWithoutSecrets.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/device/getManagementDetailsWithoutSecrets.graphql
rename to roles/common/files/fwo-api-calls/device/getManagementDetailsWithoutSecrets.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/device/getManagementNames.graphql b/roles/common/files/fwo-api-calls/device/getManagementNames.graphql
similarity index 84%
rename from roles/lib/files/FWO.Api.Client/APIcalls/device/getManagementNames.graphql
rename to roles/common/files/fwo-api-calls/device/getManagementNames.graphql
index 64d0f7d5d9..b81b78b02a 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/device/getManagementNames.graphql
+++ b/roles/common/files/fwo-api-calls/device/getManagementNames.graphql
@@ -3,10 +3,12 @@
management(order_by: {mgm_name:asc}){
id: mgm_id
name: mgm_name
+ uid: mgm_uid
extMgtData: ext_mgm_data
devices {
id: dev_id
name: dev_name
+ uid: dev_uid
}
}
}
diff --git a/roles/common/files/fwo-api-calls/device/getManagementWithSubs.graphql b/roles/common/files/fwo-api-calls/device/getManagementWithSubs.graphql
new file mode 100644
index 0000000000..04cc70de53
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/device/getManagementWithSubs.graphql
@@ -0,0 +1,13 @@
+query getManagementWithSubs {
+ management(
+ where: {
+ _not: {management: {}}
+ do_not_import: { _eq: false }
+ }
+ ) {
+ id: mgm_id
+ subManager: managementByMultiDeviceManagerId {
+ mgm_id
+ }
+ }
+}
\ No newline at end of file
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/device/getManagementsDetails.graphql b/roles/common/files/fwo-api-calls/device/getManagementsDetails.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/device/getManagementsDetails.graphql
rename to roles/common/files/fwo-api-calls/device/getManagementsDetails.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/device/getMgmtNumberUsingCred.graphql b/roles/common/files/fwo-api-calls/device/getMgmtNumberUsingCred.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/device/getMgmtNumberUsingCred.graphql
rename to roles/common/files/fwo-api-calls/device/getMgmtNumberUsingCred.graphql
diff --git a/roles/common/files/fwo-api-calls/device/getSingleManagementDetails.graphql b/roles/common/files/fwo-api-calls/device/getSingleManagementDetails.graphql
new file mode 100644
index 0000000000..f42ffc366b
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/device/getSingleManagementDetails.graphql
@@ -0,0 +1,6 @@
+query getManagementDetails($mgmId: Int!)
+{
+ management(where: {mgm_id: {_eq: $mgmId}}){
+ ...ManagementDetails
+ }
+}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/device/newCredential.graphql b/roles/common/files/fwo-api-calls/device/newCredential.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/device/newCredential.graphql
rename to roles/common/files/fwo-api-calls/device/newCredential.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/device/newDevice.graphql b/roles/common/files/fwo-api-calls/device/newDevice.graphql
similarity index 90%
rename from roles/lib/files/FWO.Api.Client/APIcalls/device/newDevice.graphql
rename to roles/common/files/fwo-api-calls/device/newDevice.graphql
index 740bc1c55b..d9599b6a62 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/device/newDevice.graphql
+++ b/roles/common/files/fwo-api-calls/device/newDevice.graphql
@@ -1,8 +1,9 @@
mutation newDevice(
$name: String!
+ $uid: String!
$devTypeId: Int!
$managementId: Int!
- $localRulebase: String!
+ $localRulebase: String
$globalRulebase: String
$package: String
$importDisabled: Boolean!
@@ -12,6 +13,7 @@ mutation newDevice(
insert_device(
objects: {
dev_name: $name
+ dev_uid: $uid
dev_typ_id: $devTypeId
mgm_id: $managementId
local_rulebase_name: $localRulebase
@@ -31,6 +33,7 @@ mutation newDevice(
# example variables (at least the following)
# {
# "name": "huhu",
+# "uid": "huhu",
# "devTypeId": 10,
# "managementId": 2,
# "rulebase": "layer1",
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/device/newManagement.graphql b/roles/common/files/fwo-api-calls/device/newManagement.graphql
similarity index 91%
rename from roles/lib/files/FWO.Api.Client/APIcalls/device/newManagement.graphql
rename to roles/common/files/fwo-api-calls/device/newManagement.graphql
index 29a965a056..412e51d587 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/device/newManagement.graphql
+++ b/roles/common/files/fwo-api-calls/device/newManagement.graphql
@@ -1,5 +1,6 @@
mutation newManagementWithExistingCredentials(
$name: String!
+ $uid: String
$devTypeId: Int!
$importCredentialId: Int!
$hostname: String!
@@ -16,10 +17,12 @@ mutation newManagementWithExistingCredentials(
$debugLevel: Int
$superManager: Int
$extMgtData: String
+ $isSuperManager: Boolean
) {
insert_management(
objects: {
mgm_name: $name
+ mgm_uid: $uid
dev_typ_id: $devTypeId
ssh_hostname: $hostname
import_credential_id: $importCredentialId
@@ -36,6 +39,7 @@ mutation newManagementWithExistingCredentials(
debug_level: $debugLevel
multi_device_manager_id: $superManager
ext_mgm_data: $extMgtData
+ is_super_manager: $isSuperManager
}
) {
returning {
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/device/updateCredential.graphql b/roles/common/files/fwo-api-calls/device/updateCredential.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/device/updateCredential.graphql
rename to roles/common/files/fwo-api-calls/device/updateCredential.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/device/updateDevice.graphql b/roles/common/files/fwo-api-calls/device/updateDevice.graphql
similarity index 91%
rename from roles/lib/files/FWO.Api.Client/APIcalls/device/updateDevice.graphql
rename to roles/common/files/fwo-api-calls/device/updateDevice.graphql
index 41167974c2..6946d94b8e 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/device/updateDevice.graphql
+++ b/roles/common/files/fwo-api-calls/device/updateDevice.graphql
@@ -1,11 +1,12 @@
mutation updateDevice(
$id: Int!
+ $uid: String
$name: String!
$managementId: Int!
$devTypeId: Int!
$importDisabled: Boolean!
$hideInUi: Boolean!
- $localRulebase: String!
+ $localRulebase: String
$globalRulebase: String
$package: String
$comment: String
@@ -14,6 +15,7 @@ mutation updateDevice(
pk_columns: { dev_id: $id }
_set: {
dev_name: $name
+ dev_uid: $uid
dev_typ_id: $devTypeId
mgm_id: $managementId
local_rulebase_name: $localRulebase
@@ -32,6 +34,7 @@ mutation updateDevice(
# {
# "id": 17,
# "name": "huhu",
+# "uid": "huhu",
# "devTypeId": 10,
# "managementId": 2,
# "rulebase": "layer1",
diff --git a/roles/common/files/fwo-api-calls/device/updateGatewayUid.graphql b/roles/common/files/fwo-api-calls/device/updateGatewayUid.graphql
new file mode 100644
index 0000000000..191ca50d33
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/device/updateGatewayUid.graphql
@@ -0,0 +1,13 @@
+mutation updateDevice(
+ $id: Int!
+ $uid: String!
+) {
+ update_device_by_pk(
+ pk_columns: { dev_id: $id }
+ _set: {
+ dev_uid: $uid
+ }
+ ) {
+ UpdatedId: dev_id
+ }
+}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/device/updateManagement.graphql b/roles/common/files/fwo-api-calls/device/updateManagement.graphql
similarity index 91%
rename from roles/lib/files/FWO.Api.Client/APIcalls/device/updateManagement.graphql
rename to roles/common/files/fwo-api-calls/device/updateManagement.graphql
index c7c07449af..4f6920c5bb 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/device/updateManagement.graphql
+++ b/roles/common/files/fwo-api-calls/device/updateManagement.graphql
@@ -1,6 +1,7 @@
mutation updateManagement(
$id: Int!
$name: String!
+ $uid: String
$devTypeId: Int!
$importCredentialId: Int!
$hostname: String!
@@ -17,11 +18,13 @@ mutation updateManagement(
$debugLevel: Int
$superManager: Int
$extMgtData: String
+ $isSuperManager: Boolean
) {
update_management_by_pk(
pk_columns: { mgm_id: $id }
_set: {
mgm_name: $name
+ mgm_uid: $uid
dev_typ_id: $devTypeId
ssh_hostname: $hostname
import_credential_id: $importCredentialId
@@ -38,6 +41,7 @@ mutation updateManagement(
debug_level: $debugLevel
multi_device_manager_id: $superManager
ext_mgm_data: $extMgtData
+ is_super_manager: $isSuperManager
}
) {
updatedId: mgm_id
diff --git a/roles/common/files/fwo-api-calls/device/updateManagementUid.graphql b/roles/common/files/fwo-api-calls/device/updateManagementUid.graphql
new file mode 100644
index 0000000000..10a633228f
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/device/updateManagementUid.graphql
@@ -0,0 +1,13 @@
+mutation updateManagementUid(
+ $id: Int!
+ $uid: String
+) {
+ update_management_by_pk(
+ pk_columns: { mgm_id: $id }
+ _set: {
+ mgm_uid: $uid
+ }
+ ) {
+ UpdatedId: mgm_id
+ }
+}
diff --git a/roles/common/files/fwo-api-calls/device/updateManagementUids.graphql b/roles/common/files/fwo-api-calls/device/updateManagementUids.graphql
new file mode 100644
index 0000000000..103ca3a50c
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/device/updateManagementUids.graphql
@@ -0,0 +1,15 @@
+mutation updateManagementUids(
+ $id: Int!
+ $uid: String
+ $domainUid: String
+) {
+ update_management_by_pk(
+ pk_columns: { mgm_id: $id }
+ _set: {
+ mgm_uid: $uid
+ domain_uid: $domainUid
+ }
+ ) {
+ UpdatedId: mgm_id
+ }
+}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/extRequest/addExtRequest.graphql b/roles/common/files/fwo-api-calls/extRequest/addExtRequest.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/extRequest/addExtRequest.graphql
rename to roles/common/files/fwo-api-calls/extRequest/addExtRequest.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/extRequest/addTicketId.graphql b/roles/common/files/fwo-api-calls/extRequest/addTicketId.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/extRequest/addTicketId.graphql
rename to roles/common/files/fwo-api-calls/extRequest/addTicketId.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/extRequest/fragments/extRequestDetails.graphql b/roles/common/files/fwo-api-calls/extRequest/fragments/extRequestDetails.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/extRequest/fragments/extRequestDetails.graphql
rename to roles/common/files/fwo-api-calls/extRequest/fragments/extRequestDetails.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/extRequest/getAndLockOpenRequests.graphql b/roles/common/files/fwo-api-calls/extRequest/getAndLockOpenRequests.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/extRequest/getAndLockOpenRequests.graphql
rename to roles/common/files/fwo-api-calls/extRequest/getAndLockOpenRequests.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/extRequest/getLastRequest.graphql b/roles/common/files/fwo-api-calls/extRequest/getLastRequest.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/extRequest/getLastRequest.graphql
rename to roles/common/files/fwo-api-calls/extRequest/getLastRequest.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/extRequest/getLatestTicketId.graphql b/roles/common/files/fwo-api-calls/extRequest/getLatestTicketId.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/extRequest/getLatestTicketId.graphql
rename to roles/common/files/fwo-api-calls/extRequest/getLatestTicketId.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/extRequest/getLatestTicketIds.graphql b/roles/common/files/fwo-api-calls/extRequest/getLatestTicketIds.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/extRequest/getLatestTicketIds.graphql
rename to roles/common/files/fwo-api-calls/extRequest/getLatestTicketIds.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/extRequest/getOpenRequests.graphql b/roles/common/files/fwo-api-calls/extRequest/getOpenRequests.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/extRequest/getOpenRequests.graphql
rename to roles/common/files/fwo-api-calls/extRequest/getOpenRequests.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/extRequest/subscribeExtRequestStateUpdate.graphql b/roles/common/files/fwo-api-calls/extRequest/subscribeExtRequestStateUpdate.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/extRequest/subscribeExtRequestStateUpdate.graphql
rename to roles/common/files/fwo-api-calls/extRequest/subscribeExtRequestStateUpdate.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/extRequest/updateExtRequestCreation.graphql b/roles/common/files/fwo-api-calls/extRequest/updateExtRequestCreation.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/extRequest/updateExtRequestCreation.graphql
rename to roles/common/files/fwo-api-calls/extRequest/updateExtRequestCreation.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/extRequest/updateExtRequestFinal.graphql b/roles/common/files/fwo-api-calls/extRequest/updateExtRequestFinal.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/extRequest/updateExtRequestFinal.graphql
rename to roles/common/files/fwo-api-calls/extRequest/updateExtRequestFinal.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/extRequest/updateExtRequestProcess.graphql b/roles/common/files/fwo-api-calls/extRequest/updateExtRequestProcess.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/extRequest/updateExtRequestProcess.graphql
rename to roles/common/files/fwo-api-calls/extRequest/updateExtRequestProcess.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/extRequest/updateExternalRequestLock.graphql b/roles/common/files/fwo-api-calls/extRequest/updateExternalRequestLock.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/extRequest/updateExternalRequestLock.graphql
rename to roles/common/files/fwo-api-calls/extRequest/updateExternalRequestLock.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/extRequest/updateExternalRequestWaitCycles.graphql b/roles/common/files/fwo-api-calls/extRequest/updateExternalRequestWaitCycles.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/extRequest/updateExternalRequestWaitCycles.graphql
rename to roles/common/files/fwo-api-calls/extRequest/updateExternalRequestWaitCycles.graphql
diff --git a/roles/common/files/fwo-api-calls/import/addImport.graphql b/roles/common/files/fwo-api-calls/import/addImport.graphql
new file mode 100644
index 0000000000..638e0313df
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/import/addImport.graphql
@@ -0,0 +1,4 @@
+mutation addImport($mgmId: Int!, $isFullImport: Boolean!, $isInitialImport: Boolean!) {
+ insert_import_control(objects: {mgm_id: $mgmId, is_full_import: $isFullImport, is_initial_import: $isInitialImport})
+ { returning { control_id } }
+}
diff --git a/roles/common/files/fwo-api-calls/import/addImportConfig.grahpql b/roles/common/files/fwo-api-calls/import/addImportConfig.grahpql
new file mode 100644
index 0000000000..58ed3c1360
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/import/addImportConfig.grahpql
@@ -0,0 +1,5 @@
+mutation addImportConfig($importId: bigint!, $mgmId: Int!, $config: jsonb!, $start_import_flag: Boolean!, $debug_mode: Boolean!) {
+ insert_import_config(objects: {start_import_flag: $start_import_flag, import_id: $importId, mgm_id: $mgmId, config: $config, debug_mode: $debug_mode}) {
+ affected_rows
+ }
+}
\ No newline at end of file
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/device/deleteImport.graphql b/roles/common/files/fwo-api-calls/import/deleteImport.graphql
similarity index 76%
rename from roles/lib/files/FWO.Api.Client/APIcalls/device/deleteImport.graphql
rename to roles/common/files/fwo-api-calls/import/deleteImport.graphql
index fc5de78230..6f601dbce9 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/device/deleteImport.graphql
+++ b/roles/common/files/fwo-api-calls/import/deleteImport.graphql
@@ -1,4 +1,4 @@
-mutation delete_import($mgmId: Int!) {
+deletemutation delete_import($mgmId: Int!) {
delete_import_control(where: {mgm_id: {_eq: $mgmId}, successful_import: {_eq: false}, stop_time: {_is_null: true}}) {
affected_rows
}
diff --git a/roles/common/files/fwo-api-calls/import/deleteImportConfig.graphql b/roles/common/files/fwo-api-calls/import/deleteImportConfig.graphql
new file mode 100644
index 0000000000..0e0433d3af
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/import/deleteImportConfig.graphql
@@ -0,0 +1,3 @@
+mutation deleteImportConfig($importId: bigint!) {
+ delete_import_config(where: {import_id: {_eq: $importId}}) { affected_rows }
+}
diff --git a/roles/common/files/fwo-api-calls/import/deleteLatestConfigOfManagement.graphql b/roles/common/files/fwo-api-calls/import/deleteLatestConfigOfManagement.graphql
new file mode 100644
index 0000000000..927fcbf0d5
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/import/deleteLatestConfigOfManagement.graphql
@@ -0,0 +1,5 @@
+mutation deleteLatestConfigOfManagement($mgmId: Int!) {
+ delete_latest_config(where: { mgm_id: { _eq: $mgmId } }) {
+ affected_rows
+ }
+}
diff --git a/roles/common/files/fwo-api-calls/import/deleteOldImports.graphql b/roles/common/files/fwo-api-calls/import/deleteOldImports.graphql
new file mode 100644
index 0000000000..7686f348ed
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/import/deleteOldImports.graphql
@@ -0,0 +1,7 @@
+mutation deleteOldImports($mgmId: Int!, $lastImportToKeep: bigint!) {
+ delete_import_control(where: {mgm_id: {_eq: $mgmId}, control_id: {_lt: $lastImportToKeep}}) {
+ returning {
+ control_id
+ }
+ }
+}
\ No newline at end of file
diff --git a/roles/common/files/fwo-api-calls/import/getChangesPerImport.graphql b/roles/common/files/fwo-api-calls/import/getChangesPerImport.graphql
new file mode 100644
index 0000000000..b37d7f3980
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/import/getChangesPerImport.graphql
@@ -0,0 +1,6 @@
+query getChangesPerImport($importId: bigint!) {
+ changelog_object_aggregate(where: {control_id: {_eq: $importId}}) { aggregate { count } }
+ changelog_service_aggregate(where: {control_id: {_eq: $importId}}) { aggregate { count } }
+ changelog_user_aggregate(where: {control_id: {_eq: $importId}}) { aggregate { count } }
+ changelog_rule_aggregate(where: {control_id: {_eq: $importId}}) { aggregate { count } }
+}
diff --git a/roles/common/files/fwo-api-calls/import/getLastCompleteImport.graphql b/roles/common/files/fwo-api-calls/import/getLastCompleteImport.graphql
new file mode 100644
index 0000000000..67f41c5299
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/import/getLastCompleteImport.graphql
@@ -0,0 +1,11 @@
+query getLastCompleteImport($mgmId: Int!) {
+ import_control(
+ order_by: { control_id: desc }
+ limit: 1
+ where: { mgm_id: { _eq: $mgmId }, stop_time: {_is_null: false} }
+ ) {
+ start_time
+ control_id
+ }
+}
+
diff --git a/roles/common/files/fwo-api-calls/import/getLastImport.graphql b/roles/common/files/fwo-api-calls/import/getLastImport.graphql
new file mode 100644
index 0000000000..e92afe7c09
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/import/getLastImport.graphql
@@ -0,0 +1,11 @@
+query getLastImport($mgmId: Int!) {
+ import_control(
+ order_by: { control_id: desc }
+ limit: 1
+ where: { mgm_id: { _eq: $mgmId }}
+ ) {
+ start_time
+ control_id
+ }
+}
+
diff --git a/roles/common/files/fwo-api-calls/import/getLastSuccessImport.graphql b/roles/common/files/fwo-api-calls/import/getLastSuccessImport.graphql
new file mode 100644
index 0000000000..74fbdc3c29
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/import/getLastSuccessImport.graphql
@@ -0,0 +1,11 @@
+query getLastSuccessImport($mgmId: Int!) {
+ import_control(
+ order_by: { control_id: desc }
+ limit: 1
+ where: { mgm_id: { _eq: $mgmId }, successful_import: { _eq: true } }
+ ) {
+ start_time
+ control_id
+ }
+}
+
diff --git a/roles/common/files/fwo-api-calls/import/getLatestConfig.graphql b/roles/common/files/fwo-api-calls/import/getLatestConfig.graphql
new file mode 100644
index 0000000000..bc692d5ac1
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/import/getLatestConfig.graphql
@@ -0,0 +1,6 @@
+query getLatestConfig($mgmId: Int!) {
+ latest_config(where: { mgm_id: { _eq: $mgmId } }) {
+ import_id
+ config
+ }
+}
diff --git a/roles/common/files/fwo-api-calls/import/getMaxImportId.graphql b/roles/common/files/fwo-api-calls/import/getMaxImportId.graphql
new file mode 100644
index 0000000000..70fb2072a5
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/import/getMaxImportId.graphql
@@ -0,0 +1,9 @@
+query getMaxImportId {
+ import_control_aggregate {
+ aggregate {
+ max {
+ id: control_id
+ }
+ }
+ }
+}
diff --git a/roles/common/files/fwo-api-calls/import/getRuleChangesPerImport.graphql b/roles/common/files/fwo-api-calls/import/getRuleChangesPerImport.graphql
new file mode 100644
index 0000000000..4d96b63126
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/import/getRuleChangesPerImport.graphql
@@ -0,0 +1,5 @@
+query getRuleChangesPerImport($importId: bigint!) {
+ changelog_rule_aggregate(where: {control_id: {_eq: $importId}}) {
+ aggregate { count }
+ }
+}
diff --git a/roles/common/files/fwo-api-calls/import/rollbackImport.graphql b/roles/common/files/fwo-api-calls/import/rollbackImport.graphql
new file mode 100644
index 0000000000..8bcdeef9ad
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/import/rollbackImport.graphql
@@ -0,0 +1,47 @@
+mutation rollbackImport($importId: bigint!) {
+ delete_objgrp(where: {import_created: {_eq: $importId}}) { affected_rows }
+ delete_svcgrp(where: {import_created: {_eq: $importId}}) { affected_rows }
+ delete_usergrp(where: {import_created: {_eq: $importId}}) { affected_rows }
+ delete_objgrp_flat(where: {import_created: {_eq: $importId}}) { affected_rows }
+ delete_svcgrp_flat(where: {import_created: {_eq: $importId}}) { affected_rows }
+ delete_usergrp_flat(where: {import_created: {_eq: $importId}}) { affected_rows }
+ delete_rule_to(where: {rt_create: {_eq: $importId}}) { affected_rows }
+ delete_rule_from(where: {rf_create: {_eq: $importId}}) { affected_rows }
+ delete_rule_service(where: {rs_create: {_eq: $importId}}) { affected_rows }
+ 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_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_object(where: {obj_create: {_eq: $importId}}) { affected_rows }
+ delete_service(where: {svc_create: {_eq: $importId}}) { affected_rows }
+ delete_usr(where: {user_create: {_eq: $importId}}) { affected_rows }
+ delete_zone(where: {zone_create: {_eq: $importId}}) { affected_rows }
+ delete_rule(where: {rule_create: {_eq: $importId}}) { affected_rows }
+ delete_rulebase_link(where: {created: {_eq: $importId}}) { affected_rows }
+ delete_rulebase(where: {created: {_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 }
+ update_object(where: {removed: {_eq: $importId}}, _set: {removed: null}) { affected_rows }
+ update_service(where: {removed: {_eq: $importId}}, _set: {removed: null}) { affected_rows }
+ update_usr(where: {removed: {_eq: $importId}}, _set: {removed: null}) { affected_rows }
+ update_zone(where: {removed: {_eq: $importId}}, _set: {removed: null}) { affected_rows }
+ update_objgrp(where: {removed: {_eq: $importId}}, _set: {removed: null}) { affected_rows }
+ update_svcgrp(where: {removed: {_eq: $importId}}, _set: {removed: null}) { affected_rows }
+ update_usergrp(where: {removed: {_eq: $importId}}, _set: {removed: null}) { affected_rows }
+ update_objgrp_flat(where: {removed: {_eq: $importId}}, _set: {removed: null}) { affected_rows }
+ update_svcgrp_flat(where: {removed: {_eq: $importId}}, _set: {removed: null}) { affected_rows }
+ update_usergrp_flat(where: {removed: {_eq: $importId}}, _set: {removed: null}) { affected_rows }
+ update_rule_to(where: {removed: {_eq: $importId}}, _set: {removed: null}) { affected_rows }
+ update_rule_from(where: {removed: {_eq: $importId}}, _set: {removed: null}) { affected_rows }
+ update_rule_service(where: {removed: {_eq: $importId}}, _set: {removed: null}) { affected_rows }
+ 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_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/import/storeLatestConfig.graphql b/roles/common/files/fwo-api-calls/import/storeLatestConfig.graphql
new file mode 100644
index 0000000000..bbde916cbe
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/import/storeLatestConfig.graphql
@@ -0,0 +1,5 @@
+mutation storeLatestConfig($importId: bigint!, $mgmId: Int!, $config: jsonb!) {
+ insert_latest_config(objects: {import_id: $importId, mgm_id: $mgmId, config: $config}) {
+ affected_rows
+ }
+}
diff --git a/roles/common/files/fwo-api-calls/import/updateImportStopTime.graphql b/roles/common/files/fwo-api-calls/import/updateImportStopTime.graphql
new file mode 100644
index 0000000000..83ebea57cb
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/import/updateImportStopTime.graphql
@@ -0,0 +1,6 @@
+
+mutation updateImportStopTime($importId: bigint!, $stopTime: timestamp!, $success: Boolean, $anyChangesFound: Boolean!, $ruleChangesFound: Boolean!, $changeNumber: Int!) {
+ update_import_control(where: {control_id: {_eq: $importId}}, _set: {stop_time: $stopTime, successful_import: $success, any_changes_found: $anyChangesFound, rule_changes_found: $ruleChangesFound, security_relevant_changes_counter: $changeNumber}) {
+ affected_rows
+ }
+}
\ No newline at end of file
diff --git a/roles/common/files/fwo-api-calls/import/updateManagementLastImportAttempt.graphql b/roles/common/files/fwo-api-calls/import/updateManagementLastImportAttempt.graphql
new file mode 100644
index 0000000000..af2b6a95f2
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/import/updateManagementLastImportAttempt.graphql
@@ -0,0 +1,3 @@
+mutation updateManagementLastImportAttempt($mgmId: Int!, $timeStamp: timestamp!, $success: Boolean) {
+ update_management(where: {mgm_id: {_eq: $mgmId}}, _set: {last_import_attempt: $timeStamp, last_import_attempt_successful: $success } ) { affected_rows }
+}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/addAppServerToConnection.graphql b/roles/common/files/fwo-api-calls/modelling/addAppServerToConnection.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/addAppServerToConnection.graphql
rename to roles/common/files/fwo-api-calls/modelling/addAppServerToConnection.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/addHistoryEntry.graphql b/roles/common/files/fwo-api-calls/modelling/addHistoryEntry.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/addHistoryEntry.graphql
rename to roles/common/files/fwo-api-calls/modelling/addHistoryEntry.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/addNwAppZone.graphql b/roles/common/files/fwo-api-calls/modelling/addNwAppZone.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/addNwAppZone.graphql
rename to roles/common/files/fwo-api-calls/modelling/addNwAppZone.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/addNwGroupToConnection.graphql b/roles/common/files/fwo-api-calls/modelling/addNwGroupToConnection.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/addNwGroupToConnection.graphql
rename to roles/common/files/fwo-api-calls/modelling/addNwGroupToConnection.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/addNwObjectToNwGroup.graphql b/roles/common/files/fwo-api-calls/modelling/addNwObjectToNwGroup.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/addNwObjectToNwGroup.graphql
rename to roles/common/files/fwo-api-calls/modelling/addNwObjectToNwGroup.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/addSelectedConnection.graphql b/roles/common/files/fwo-api-calls/modelling/addSelectedConnection.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/addSelectedConnection.graphql
rename to roles/common/files/fwo-api-calls/modelling/addSelectedConnection.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/addSelectedNwGroupObject.graphql b/roles/common/files/fwo-api-calls/modelling/addSelectedNwGroupObject.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/addSelectedNwGroupObject.graphql
rename to roles/common/files/fwo-api-calls/modelling/addSelectedNwGroupObject.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/addServiceGroupToConnection.graphql b/roles/common/files/fwo-api-calls/modelling/addServiceGroupToConnection.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/addServiceGroupToConnection.graphql
rename to roles/common/files/fwo-api-calls/modelling/addServiceGroupToConnection.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/addServiceToConnection.graphql b/roles/common/files/fwo-api-calls/modelling/addServiceToConnection.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/addServiceToConnection.graphql
rename to roles/common/files/fwo-api-calls/modelling/addServiceToConnection.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/addServiceToServiceGroup.graphql b/roles/common/files/fwo-api-calls/modelling/addServiceToServiceGroup.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/addServiceToServiceGroup.graphql
rename to roles/common/files/fwo-api-calls/modelling/addServiceToServiceGroup.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/deleteAppServer.graphql b/roles/common/files/fwo-api-calls/modelling/deleteAppServer.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/deleteAppServer.graphql
rename to roles/common/files/fwo-api-calls/modelling/deleteAppServer.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/deleteConnection.graphql b/roles/common/files/fwo-api-calls/modelling/deleteConnection.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/deleteConnection.graphql
rename to roles/common/files/fwo-api-calls/modelling/deleteConnection.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/deleteNwGroup.graphql b/roles/common/files/fwo-api-calls/modelling/deleteNwGroup.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/deleteNwGroup.graphql
rename to roles/common/files/fwo-api-calls/modelling/deleteNwGroup.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/deleteService.graphql b/roles/common/files/fwo-api-calls/modelling/deleteService.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/deleteService.graphql
rename to roles/common/files/fwo-api-calls/modelling/deleteService.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/deleteServiceGroup.graphql b/roles/common/files/fwo-api-calls/modelling/deleteServiceGroup.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/deleteServiceGroup.graphql
rename to roles/common/files/fwo-api-calls/modelling/deleteServiceGroup.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/fragments/appRoleDetails.graphql b/roles/common/files/fwo-api-calls/modelling/fragments/appRoleDetails.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/fragments/appRoleDetails.graphql
rename to roles/common/files/fwo-api-calls/modelling/fragments/appRoleDetails.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/fragments/appServerDetails.graphql b/roles/common/files/fwo-api-calls/modelling/fragments/appServerDetails.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/fragments/appServerDetails.graphql
rename to roles/common/files/fwo-api-calls/modelling/fragments/appServerDetails.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/fragments/areaDetails.graphql b/roles/common/files/fwo-api-calls/modelling/fragments/areaDetails.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/fragments/areaDetails.graphql
rename to roles/common/files/fwo-api-calls/modelling/fragments/areaDetails.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/fragments/connectionDetails.graphql b/roles/common/files/fwo-api-calls/modelling/fragments/connectionDetails.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/fragments/connectionDetails.graphql
rename to roles/common/files/fwo-api-calls/modelling/fragments/connectionDetails.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/fragments/connectionResolvedDetails.graphql b/roles/common/files/fwo-api-calls/modelling/fragments/connectionResolvedDetails.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/fragments/connectionResolvedDetails.graphql
rename to roles/common/files/fwo-api-calls/modelling/fragments/connectionResolvedDetails.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/fragments/serviceDetails.graphql b/roles/common/files/fwo-api-calls/modelling/fragments/serviceDetails.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/fragments/serviceDetails.graphql
rename to roles/common/files/fwo-api-calls/modelling/fragments/serviceDetails.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/fragments/serviceGroupDetails.graphql b/roles/common/files/fwo-api-calls/modelling/fragments/serviceGroupDetails.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/fragments/serviceGroupDetails.graphql
rename to roles/common/files/fwo-api-calls/modelling/fragments/serviceGroupDetails.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getAllAppServers.graphql b/roles/common/files/fwo-api-calls/modelling/getAllAppServers.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getAllAppServers.graphql
rename to roles/common/files/fwo-api-calls/modelling/getAllAppServers.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getAppRoles.graphql b/roles/common/files/fwo-api-calls/modelling/getAppRoles.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getAppRoles.graphql
rename to roles/common/files/fwo-api-calls/modelling/getAppRoles.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getAppRolesForAppServer.graphql b/roles/common/files/fwo-api-calls/modelling/getAppRolesForAppServer.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getAppRolesForAppServer.graphql
rename to roles/common/files/fwo-api-calls/modelling/getAppRolesForAppServer.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getAppServersByIp.graphql b/roles/common/files/fwo-api-calls/modelling/getAppServersByIp.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getAppServersByIp.graphql
rename to roles/common/files/fwo-api-calls/modelling/getAppServersByIp.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getAppServersByName.graphql b/roles/common/files/fwo-api-calls/modelling/getAppServersByName.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getAppServersByName.graphql
rename to roles/common/files/fwo-api-calls/modelling/getAppServersByName.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getAppServersBySource.graphql b/roles/common/files/fwo-api-calls/modelling/getAppServersBySource.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getAppServersBySource.graphql
rename to roles/common/files/fwo-api-calls/modelling/getAppServersBySource.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getAppServersForOwner.graphql b/roles/common/files/fwo-api-calls/modelling/getAppServersForOwner.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getAppServersForOwner.graphql
rename to roles/common/files/fwo-api-calls/modelling/getAppServersForOwner.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getAppZonesByAppId.graphql b/roles/common/files/fwo-api-calls/modelling/getAppZonesByAppId.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getAppZonesByAppId.graphql
rename to roles/common/files/fwo-api-calls/modelling/getAppZonesByAppId.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getAreas.graphql b/roles/common/files/fwo-api-calls/modelling/getAreas.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getAreas.graphql
rename to roles/common/files/fwo-api-calls/modelling/getAreas.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getCommonServices.graphql b/roles/common/files/fwo-api-calls/modelling/getCommonServices.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getCommonServices.graphql
rename to roles/common/files/fwo-api-calls/modelling/getCommonServices.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getConnectionById.graphql b/roles/common/files/fwo-api-calls/modelling/getConnectionById.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getConnectionById.graphql
rename to roles/common/files/fwo-api-calls/modelling/getConnectionById.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getConnectionIdsForAppServer.graphql b/roles/common/files/fwo-api-calls/modelling/getConnectionIdsForAppServer.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getConnectionIdsForAppServer.graphql
rename to roles/common/files/fwo-api-calls/modelling/getConnectionIdsForAppServer.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getConnectionIdsForNwGroup.graphql b/roles/common/files/fwo-api-calls/modelling/getConnectionIdsForNwGroup.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getConnectionIdsForNwGroup.graphql
rename to roles/common/files/fwo-api-calls/modelling/getConnectionIdsForNwGroup.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getConnectionIdsForService.graphql b/roles/common/files/fwo-api-calls/modelling/getConnectionIdsForService.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getConnectionIdsForService.graphql
rename to roles/common/files/fwo-api-calls/modelling/getConnectionIdsForService.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getConnectionIdsForServiceGroup.graphql b/roles/common/files/fwo-api-calls/modelling/getConnectionIdsForServiceGroup.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getConnectionIdsForServiceGroup.graphql
rename to roles/common/files/fwo-api-calls/modelling/getConnectionIdsForServiceGroup.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getConnections.graphql b/roles/common/files/fwo-api-calls/modelling/getConnections.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getConnections.graphql
rename to roles/common/files/fwo-api-calls/modelling/getConnections.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getConnectionsByTicketId.graphql b/roles/common/files/fwo-api-calls/modelling/getConnectionsByTicketId.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getConnectionsByTicketId.graphql
rename to roles/common/files/fwo-api-calls/modelling/getConnectionsByTicketId.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getConnectionsForNwGroup.graphql b/roles/common/files/fwo-api-calls/modelling/getConnectionsForNwGroup.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getConnectionsForNwGroup.graphql
rename to roles/common/files/fwo-api-calls/modelling/getConnectionsForNwGroup.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getConnectionsResolved.graphql b/roles/common/files/fwo-api-calls/modelling/getConnectionsResolved.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getConnectionsResolved.graphql
rename to roles/common/files/fwo-api-calls/modelling/getConnectionsResolved.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getDeletedConnections.graphql b/roles/common/files/fwo-api-calls/modelling/getDeletedConnections.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getDeletedConnections.graphql
rename to roles/common/files/fwo-api-calls/modelling/getDeletedConnections.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getDummyAppRole.graphql b/roles/common/files/fwo-api-calls/modelling/getDummyAppRole.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getDummyAppRole.graphql
rename to roles/common/files/fwo-api-calls/modelling/getDummyAppRole.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getGlobalServiceGroups.graphql b/roles/common/files/fwo-api-calls/modelling/getGlobalServiceGroups.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getGlobalServiceGroups.graphql
rename to roles/common/files/fwo-api-calls/modelling/getGlobalServiceGroups.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getGlobalServices.graphql b/roles/common/files/fwo-api-calls/modelling/getGlobalServices.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getGlobalServices.graphql
rename to roles/common/files/fwo-api-calls/modelling/getGlobalServices.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getHistory.graphql b/roles/common/files/fwo-api-calls/modelling/getHistory.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getHistory.graphql
rename to roles/common/files/fwo-api-calls/modelling/getHistory.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getHistoryForApp.graphql b/roles/common/files/fwo-api-calls/modelling/getHistoryForApp.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getHistoryForApp.graphql
rename to roles/common/files/fwo-api-calls/modelling/getHistoryForApp.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getInterfaceUsers.graphql b/roles/common/files/fwo-api-calls/modelling/getInterfaceUsers.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getInterfaceUsers.graphql
rename to roles/common/files/fwo-api-calls/modelling/getInterfaceUsers.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getNewestAppRoles.graphql b/roles/common/files/fwo-api-calls/modelling/getNewestAppRoles.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getNewestAppRoles.graphql
rename to roles/common/files/fwo-api-calls/modelling/getNewestAppRoles.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getNwGroupObjects.graphql b/roles/common/files/fwo-api-calls/modelling/getNwGroupObjects.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getNwGroupObjects.graphql
rename to roles/common/files/fwo-api-calls/modelling/getNwGroupObjects.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getPublishedInterfaces.graphql b/roles/common/files/fwo-api-calls/modelling/getPublishedInterfaces.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getPublishedInterfaces.graphql
rename to roles/common/files/fwo-api-calls/modelling/getPublishedInterfaces.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getSelectedConnections.graphql b/roles/common/files/fwo-api-calls/modelling/getSelectedConnections.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getSelectedConnections.graphql
rename to roles/common/files/fwo-api-calls/modelling/getSelectedConnections.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getSelectedNwGroupObjects.graphql b/roles/common/files/fwo-api-calls/modelling/getSelectedNwGroupObjects.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getSelectedNwGroupObjects.graphql
rename to roles/common/files/fwo-api-calls/modelling/getSelectedNwGroupObjects.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getServiceGroupById.graphql b/roles/common/files/fwo-api-calls/modelling/getServiceGroupById.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getServiceGroupById.graphql
rename to roles/common/files/fwo-api-calls/modelling/getServiceGroupById.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getServiceGroupIdsForService.graphql b/roles/common/files/fwo-api-calls/modelling/getServiceGroupIdsForService.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getServiceGroupIdsForService.graphql
rename to roles/common/files/fwo-api-calls/modelling/getServiceGroupIdsForService.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getServiceGroupsForApp.graphql b/roles/common/files/fwo-api-calls/modelling/getServiceGroupsForApp.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getServiceGroupsForApp.graphql
rename to roles/common/files/fwo-api-calls/modelling/getServiceGroupsForApp.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getServicesForApp.graphql b/roles/common/files/fwo-api-calls/modelling/getServicesForApp.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/getServicesForApp.graphql
rename to roles/common/files/fwo-api-calls/modelling/getServicesForApp.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/newAppRole.graphql b/roles/common/files/fwo-api-calls/modelling/newAppRole.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/newAppRole.graphql
rename to roles/common/files/fwo-api-calls/modelling/newAppRole.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/newAppServer.graphql b/roles/common/files/fwo-api-calls/modelling/newAppServer.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/newAppServer.graphql
rename to roles/common/files/fwo-api-calls/modelling/newAppServer.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/newArea.graphql b/roles/common/files/fwo-api-calls/modelling/newArea.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/newArea.graphql
rename to roles/common/files/fwo-api-calls/modelling/newArea.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/newAreaIpData.graphql b/roles/common/files/fwo-api-calls/modelling/newAreaIpData.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/newAreaIpData.graphql
rename to roles/common/files/fwo-api-calls/modelling/newAreaIpData.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/newConnection.graphql b/roles/common/files/fwo-api-calls/modelling/newConnection.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/newConnection.graphql
rename to roles/common/files/fwo-api-calls/modelling/newConnection.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/newService.graphql b/roles/common/files/fwo-api-calls/modelling/newService.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/newService.graphql
rename to roles/common/files/fwo-api-calls/modelling/newService.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/newServiceGroup.graphql b/roles/common/files/fwo-api-calls/modelling/newServiceGroup.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/newServiceGroup.graphql
rename to roles/common/files/fwo-api-calls/modelling/newServiceGroup.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/removeAllAppServersFromConnection.graphql b/roles/common/files/fwo-api-calls/modelling/removeAllAppServersFromConnection.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/removeAllAppServersFromConnection.graphql
rename to roles/common/files/fwo-api-calls/modelling/removeAllAppServersFromConnection.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/removeAllNwGroupsFromConnection.graphql b/roles/common/files/fwo-api-calls/modelling/removeAllNwGroupsFromConnection.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/removeAllNwGroupsFromConnection.graphql
rename to roles/common/files/fwo-api-calls/modelling/removeAllNwGroupsFromConnection.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/removeAllServiceGroupsFromConnection.graphql b/roles/common/files/fwo-api-calls/modelling/removeAllServiceGroupsFromConnection.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/removeAllServiceGroupsFromConnection.graphql
rename to roles/common/files/fwo-api-calls/modelling/removeAllServiceGroupsFromConnection.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/removeAllServicesFromConnection.graphql b/roles/common/files/fwo-api-calls/modelling/removeAllServicesFromConnection.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/removeAllServicesFromConnection.graphql
rename to roles/common/files/fwo-api-calls/modelling/removeAllServicesFromConnection.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/removeAppServerFromConnection.graphql b/roles/common/files/fwo-api-calls/modelling/removeAppServerFromConnection.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/removeAppServerFromConnection.graphql
rename to roles/common/files/fwo-api-calls/modelling/removeAppServerFromConnection.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/removeNwGroupFromConnection.graphql b/roles/common/files/fwo-api-calls/modelling/removeNwGroupFromConnection.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/removeNwGroupFromConnection.graphql
rename to roles/common/files/fwo-api-calls/modelling/removeNwGroupFromConnection.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/removeNwObjectFromNwGroup.graphql b/roles/common/files/fwo-api-calls/modelling/removeNwObjectFromNwGroup.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/removeNwObjectFromNwGroup.graphql
rename to roles/common/files/fwo-api-calls/modelling/removeNwObjectFromNwGroup.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/removeSelectedConnection.graphql b/roles/common/files/fwo-api-calls/modelling/removeSelectedConnection.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/removeSelectedConnection.graphql
rename to roles/common/files/fwo-api-calls/modelling/removeSelectedConnection.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/removeSelectedConnectionFromApp.graphql b/roles/common/files/fwo-api-calls/modelling/removeSelectedConnectionFromApp.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/removeSelectedConnectionFromApp.graphql
rename to roles/common/files/fwo-api-calls/modelling/removeSelectedConnectionFromApp.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/removeSelectedNwGroupObject.graphql b/roles/common/files/fwo-api-calls/modelling/removeSelectedNwGroupObject.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/removeSelectedNwGroupObject.graphql
rename to roles/common/files/fwo-api-calls/modelling/removeSelectedNwGroupObject.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/removeSelectedNwGroupObjectFromAllApps.graphql b/roles/common/files/fwo-api-calls/modelling/removeSelectedNwGroupObjectFromAllApps.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/removeSelectedNwGroupObjectFromAllApps.graphql
rename to roles/common/files/fwo-api-calls/modelling/removeSelectedNwGroupObjectFromAllApps.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/removeServiceFromConnection.graphql b/roles/common/files/fwo-api-calls/modelling/removeServiceFromConnection.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/removeServiceFromConnection.graphql
rename to roles/common/files/fwo-api-calls/modelling/removeServiceFromConnection.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/removeServiceFromServiceGroup.graphql b/roles/common/files/fwo-api-calls/modelling/removeServiceFromServiceGroup.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/removeServiceFromServiceGroup.graphql
rename to roles/common/files/fwo-api-calls/modelling/removeServiceFromServiceGroup.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/removeServiceGroupFromConnection.graphql b/roles/common/files/fwo-api-calls/modelling/removeServiceGroupFromConnection.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/removeServiceGroupFromConnection.graphql
rename to roles/common/files/fwo-api-calls/modelling/removeServiceGroupFromConnection.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/replaceUsedInterface.graphql b/roles/common/files/fwo-api-calls/modelling/replaceUsedInterface.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/replaceUsedInterface.graphql
rename to roles/common/files/fwo-api-calls/modelling/replaceUsedInterface.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/setAppServerDeletedState.graphql b/roles/common/files/fwo-api-calls/modelling/setAppServerDeletedState.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/setAppServerDeletedState.graphql
rename to roles/common/files/fwo-api-calls/modelling/setAppServerDeletedState.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/setAppServerName.graphql b/roles/common/files/fwo-api-calls/modelling/setAppServerName.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/setAppServerName.graphql
rename to roles/common/files/fwo-api-calls/modelling/setAppServerName.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/setAppServerType.graphql b/roles/common/files/fwo-api-calls/modelling/setAppServerType.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/setAppServerType.graphql
rename to roles/common/files/fwo-api-calls/modelling/setAppServerType.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/setNwGroupDeletedState.graphql b/roles/common/files/fwo-api-calls/modelling/setNwGroupDeletedState.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/setNwGroupDeletedState.graphql
rename to roles/common/files/fwo-api-calls/modelling/setNwGroupDeletedState.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateAppRole.graphql b/roles/common/files/fwo-api-calls/modelling/updateAppRole.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateAppRole.graphql
rename to roles/common/files/fwo-api-calls/modelling/updateAppRole.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateAppServer.graphql b/roles/common/files/fwo-api-calls/modelling/updateAppServer.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateAppServer.graphql
rename to roles/common/files/fwo-api-calls/modelling/updateAppServer.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateConnection.graphql b/roles/common/files/fwo-api-calls/modelling/updateConnection.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateConnection.graphql
rename to roles/common/files/fwo-api-calls/modelling/updateConnection.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateConnectionDecommission.graphql b/roles/common/files/fwo-api-calls/modelling/updateConnectionDecommission.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateConnectionDecommission.graphql
rename to roles/common/files/fwo-api-calls/modelling/updateConnectionDecommission.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateConnectionFwRequested.graphql b/roles/common/files/fwo-api-calls/modelling/updateConnectionFwRequested.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateConnectionFwRequested.graphql
rename to roles/common/files/fwo-api-calls/modelling/updateConnectionFwRequested.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateConnectionProperties.graphql b/roles/common/files/fwo-api-calls/modelling/updateConnectionProperties.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateConnectionProperties.graphql
rename to roles/common/files/fwo-api-calls/modelling/updateConnectionProperties.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateConnectionPublish.graphql b/roles/common/files/fwo-api-calls/modelling/updateConnectionPublish.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateConnectionPublish.graphql
rename to roles/common/files/fwo-api-calls/modelling/updateConnectionPublish.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateConnectionRemove.graphql b/roles/common/files/fwo-api-calls/modelling/updateConnectionRemove.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateConnectionRemove.graphql
rename to roles/common/files/fwo-api-calls/modelling/updateConnectionRemove.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateNwObjectInConnection.graphql b/roles/common/files/fwo-api-calls/modelling/updateNwObjectInConnection.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateNwObjectInConnection.graphql
rename to roles/common/files/fwo-api-calls/modelling/updateNwObjectInConnection.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateNwObjectInNwGroup.graphql b/roles/common/files/fwo-api-calls/modelling/updateNwObjectInNwGroup.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateNwObjectInNwGroup.graphql
rename to roles/common/files/fwo-api-calls/modelling/updateNwObjectInNwGroup.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateProposedConnectionOwner.graphql b/roles/common/files/fwo-api-calls/modelling/updateProposedConnectionOwner.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateProposedConnectionOwner.graphql
rename to roles/common/files/fwo-api-calls/modelling/updateProposedConnectionOwner.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateService.graphql b/roles/common/files/fwo-api-calls/modelling/updateService.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateService.graphql
rename to roles/common/files/fwo-api-calls/modelling/updateService.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateServiceGroup.graphql b/roles/common/files/fwo-api-calls/modelling/updateServiceGroup.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateServiceGroup.graphql
rename to roles/common/files/fwo-api-calls/modelling/updateServiceGroup.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/upsertAppServer.graphql b/roles/common/files/fwo-api-calls/modelling/upsertAppServer.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/modelling/upsertAppServer.graphql
rename to roles/common/files/fwo-api-calls/modelling/upsertAppServer.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/monitor/acknowledgeAlert.graphql b/roles/common/files/fwo-api-calls/monitor/acknowledgeAlert.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/monitor/acknowledgeAlert.graphql
rename to roles/common/files/fwo-api-calls/monitor/acknowledgeAlert.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/monitor/addAlert.graphql b/roles/common/files/fwo-api-calls/monitor/addAlert.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/monitor/addAlert.graphql
rename to roles/common/files/fwo-api-calls/monitor/addAlert.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/monitor/addAutodiscoveryLogEntry.graphql b/roles/common/files/fwo-api-calls/monitor/addAutodiscoveryLogEntry.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/monitor/addAutodiscoveryLogEntry.graphql
rename to roles/common/files/fwo-api-calls/monitor/addAutodiscoveryLogEntry.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/monitor/addDataImportLogEntry.graphql b/roles/common/files/fwo-api-calls/monitor/addDataImportLogEntry.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/monitor/addDataImportLogEntry.graphql
rename to roles/common/files/fwo-api-calls/monitor/addDataImportLogEntry.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/monitor/addLogEntry.graphql b/roles/common/files/fwo-api-calls/monitor/addLogEntry.graphql
similarity index 97%
rename from roles/lib/files/FWO.Api.Client/APIcalls/monitor/addLogEntry.graphql
rename to roles/common/files/fwo-api-calls/monitor/addLogEntry.graphql
index d192aa740e..b9156ab197 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/monitor/addLogEntry.graphql
+++ b/roles/common/files/fwo-api-calls/monitor/addLogEntry.graphql
@@ -1,6 +1,6 @@
mutation addLogEntry(
$source: String!
- $discoverUser: Int!
+ $discoverUser: Int
$severity: Int!
$suspectedCause: String
$description: String
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/monitor/addUiLogEntry.graphql b/roles/common/files/fwo-api-calls/monitor/addUiLogEntry.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/monitor/addUiLogEntry.graphql
rename to roles/common/files/fwo-api-calls/monitor/addUiLogEntry.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/monitor/getAlertById.graphql b/roles/common/files/fwo-api-calls/monitor/getAlertById.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/monitor/getAlertById.graphql
rename to roles/common/files/fwo-api-calls/monitor/getAlertById.graphql
diff --git a/roles/common/files/fwo-api-calls/monitor/getAlertByManagement.graphql b/roles/common/files/fwo-api-calls/monitor/getAlertByManagement.graphql
new file mode 100644
index 0000000000..8ba50982f1
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/monitor/getAlertByManagement.graphql
@@ -0,0 +1,9 @@
+query getAlertByManagement($mgmId: Int!, $alertCode: Int!, $currentAlertId: bigint!) {
+ alert(where: {
+ alert_mgm_id: {_eq: $mgmId}, alert_code: {_eq: $alertCode}
+ ack_timestamp: {_is_null: true}
+ alert_id: {_neq: $currentAlertId}})
+ {
+ alert_id
+ }
+}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/monitor/getAlerts.graphql b/roles/common/files/fwo-api-calls/monitor/getAlerts.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/monitor/getAlerts.graphql
rename to roles/common/files/fwo-api-calls/monitor/getAlerts.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/monitor/getAllUiLogEntrys.graphql b/roles/common/files/fwo-api-calls/monitor/getAllUiLogEntrys.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/monitor/getAllUiLogEntrys.graphql
rename to roles/common/files/fwo-api-calls/monitor/getAllUiLogEntrys.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/monitor/getAutodiscoveryLogEntrys.graphql b/roles/common/files/fwo-api-calls/monitor/getAutodiscoveryLogEntrys.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/monitor/getAutodiscoveryLogEntrys.graphql
rename to roles/common/files/fwo-api-calls/monitor/getAutodiscoveryLogEntrys.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/monitor/getDailyCheckLogEntrys.graphql b/roles/common/files/fwo-api-calls/monitor/getDailyCheckLogEntrys.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/monitor/getDailyCheckLogEntrys.graphql
rename to roles/common/files/fwo-api-calls/monitor/getDailyCheckLogEntrys.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/monitor/getDataImportLogEntrys.graphql b/roles/common/files/fwo-api-calls/monitor/getDataImportLogEntrys.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/monitor/getDataImportLogEntrys.graphql
rename to roles/common/files/fwo-api-calls/monitor/getDataImportLogEntrys.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/monitor/getImportLogEntrys.graphql b/roles/common/files/fwo-api-calls/monitor/getImportLogEntrys.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/monitor/getImportLogEntrys.graphql
rename to roles/common/files/fwo-api-calls/monitor/getImportLogEntrys.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/monitor/getImportStatus.graphql b/roles/common/files/fwo-api-calls/monitor/getImportStatus.graphql
similarity index 53%
rename from roles/lib/files/FWO.Api.Client/APIcalls/monitor/getImportStatus.graphql
rename to roles/common/files/fwo-api-calls/monitor/getImportStatus.graphql
index 906af35195..1494097018 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/monitor/getImportStatus.graphql
+++ b/roles/common/files/fwo-api-calls/monitor/getImportStatus.graphql
@@ -1,5 +1,5 @@
query getImportStatus {
- management(where: {stm_dev_typ: {dev_typ_is_multi_mgmt: {_eq: false}}} order_by: { mgm_name: asc }) {
+ management(where: {_or: [{multi_device_manager_id: {_is_null: true}}, {is_super_manager: {_eq: true}}]}, order_by: {mgm_name: asc}) {
mgm_id
mgm_name
importDisabled: do_not_import
@@ -12,38 +12,37 @@ query getImportStatus {
manufacturer: dev_typ_manufacturer
isPureRoutingDevice: is_pure_routing_device
}
- last_import: import_controls(order_by: { control_id: desc }, limit: 1) {
+ last_import: import_controls(order_by: {control_id: desc}, limit: 1) {
control_id
start_time
stop_time
successful_import
import_errors
}
- last_successful_import: import_controls(where: { successful_import: {_eq: true} } order_by: {control_id: desc}, limit: 1) {
+ last_successful_import: import_controls(where: {successful_import: {_eq: true}}, order_by: {control_id: desc}, limit: 1) {
control_id
start_time
stop_time
successful_import
import_errors
}
- last_incomplete_import: import_controls(where: { successful_import: {_eq: false}, stop_time: { _is_null: true } } order_by: {control_id: desc}, limit: 1) {
+ last_incomplete_import: import_controls(where: {successful_import: {_eq: false}, stop_time: {_is_null: true}}, order_by: {control_id: desc}, limit: 1) {
control_id
start_time
stop_time
successful_import
import_errors
}
- first_import: import_controls(order_by: { control_id: asc }, limit: 1) {
+ first_import: import_controls(order_by: {control_id: asc}, limit: 1) {
control_id
start_time
stop_time
successful_import
import_errors
}
- erroneous_imports: import_controls(where: { successful_import: {_eq: false} } order_by: {control_id: desc}) {
+ erroneous_imports: import_controls(where: {successful_import: {_eq: false}}, order_by: {control_id: desc}) {
control_id
import_errors
}
-
}
}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/monitor/getLogEntrys.graphql b/roles/common/files/fwo-api-calls/monitor/getLogEntrys.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/monitor/getLogEntrys.graphql
rename to roles/common/files/fwo-api-calls/monitor/getLogEntrys.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/monitor/getOpenAlerts.graphql b/roles/common/files/fwo-api-calls/monitor/getOpenAlerts.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/monitor/getOpenAlerts.graphql
rename to roles/common/files/fwo-api-calls/monitor/getOpenAlerts.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/monitor/getOwnerTicketIds.graphql b/roles/common/files/fwo-api-calls/monitor/getOwnerTicketIds.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/monitor/getOwnerTicketIds.graphql
rename to roles/common/files/fwo-api-calls/monitor/getOwnerTicketIds.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/monitor/getOwnerTickets.graphql b/roles/common/files/fwo-api-calls/monitor/getOwnerTickets.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/monitor/getOwnerTickets.graphql
rename to roles/common/files/fwo-api-calls/monitor/getOwnerTickets.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/monitor/getUiLogEntrys.graphql b/roles/common/files/fwo-api-calls/monitor/getUiLogEntrys.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/monitor/getUiLogEntrys.graphql
rename to roles/common/files/fwo-api-calls/monitor/getUiLogEntrys.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/monitor/subscribeAlertChanges.graphql b/roles/common/files/fwo-api-calls/monitor/subscribeAlertChanges.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/monitor/subscribeAlertChanges.graphql
rename to roles/common/files/fwo-api-calls/monitor/subscribeAlertChanges.graphql
diff --git a/roles/common/files/fwo-api-calls/monitor/updateAlert.graphql b/roles/common/files/fwo-api-calls/monitor/updateAlert.graphql
new file mode 100644
index 0000000000..b107bd4d1c
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/monitor/updateAlert.graphql
@@ -0,0 +1,5 @@
+mutation updateAlert($userId: Int, $alertId: bigint, $ackTimeStamp: timestamp) {
+ update_alert(where: {alert_id: {_eq: $alertId}}, _set: {ack_by: $userId, ack_timestamp: $ackTimeStamp}) {
+ affected_rows
+ }
+}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/networkObject/fragments/networkObjectDetails.graphql b/roles/common/files/fwo-api-calls/networkObject/fragments/networkObjectDetails.graphql
similarity index 51%
rename from roles/lib/files/FWO.Api.Client/APIcalls/networkObject/fragments/networkObjectDetails.graphql
rename to roles/common/files/fwo-api-calls/networkObject/fragments/networkObjectDetails.graphql
index 51c93746d2..d59ef7bb29 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/networkObject/fragments/networkObjectDetails.graphql
+++ b/roles/common/files/fwo-api-calls/networkObject/fragments/networkObjectDetails.graphql
@@ -20,27 +20,45 @@ fragment networkObjectDetails on object {
obj_comment
obj_member_names
obj_member_refs
- objgrps(order_by: {objgrp_member_id: asc}) {
+ objgrps(
+ where: {
+ active: { _eq: $active }
+ import_created: { _lte: $import_id_end }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt: $import_id_start } }
+ ]
+ }
+ order_by: { objgrp_member_id: asc }
+ ) {
id: objgrp_member_id
- byId: objectByObjgrpMemberId {
+ byId: objectByObjgrpMemberId {
obj_id
obj_name
type: stm_obj_typ {
+ id: obj_typ_id
name: obj_typ_name
}
}
}
- objgrp_flats(where: {
- active: { _eq: $active }
- import_last_seen: { _gte: $import_id_start }
- import_created: { _lte: $import_id_end }
- } order_by: {objgrp_flat_member_id: asc}) {
+ objgrp_flats(
+ where: {
+ active: { _eq: $active }
+ import_created: { _lte: $import_id_end }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt: $import_id_start } }
+ ]
+ }
+ order_by: { objgrp_flat_member_id: asc }
+ ) {
id_flat: objgrp_flat_id
byFlatId: objectByObjgrpFlatMemberId {
obj_id
obj_name
obj_ip
obj_ip_end
+ obj_member_refs
type: stm_obj_typ {
name: obj_typ_name
}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/networkObject/fragments/networkObjectDetailsChangesNew.graphql b/roles/common/files/fwo-api-calls/networkObject/fragments/networkObjectDetailsChangesNew.graphql
similarity index 72%
rename from roles/lib/files/FWO.Api.Client/APIcalls/networkObject/fragments/networkObjectDetailsChangesNew.graphql
rename to roles/common/files/fwo-api-calls/networkObject/fragments/networkObjectDetailsChangesNew.graphql
index af07da9c41..2dabf0ae47 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/networkObject/fragments/networkObjectDetailsChangesNew.graphql
+++ b/roles/common/files/fwo-api-calls/networkObject/fragments/networkObjectDetailsChangesNew.graphql
@@ -20,7 +20,14 @@ fragment networkObjectDetailsChangesNew on object {
obj_comment
obj_member_names
obj_member_refs
- objgrps(order_by: {objgrp_member_id: asc}) {
+ objgrps(where: {
+ active: { _eq: $active }
+ import_created: { _lte: $import_id_new }
+ _or: [
+ {removed: {_is_null: true}}
+ {removed: {_gt: $import_id_new}}
+ ]
+ } order_by: {objgrp_member_id: asc}) {
id: objgrp_member_id
byId: objectByObjgrpMemberId {
obj_id
@@ -32,8 +39,11 @@ fragment networkObjectDetailsChangesNew on object {
}
objgrp_flats(where: {
active: { _eq: $active }
- import_last_seen: { _gte: $import_id_new }
import_created: { _lte: $import_id_new }
+ _or: [
+ {removed: {_is_null: true}}
+ {removed: {_gt: $import_id_new}}
+ ]
} order_by: {objgrp_flat_member_id: asc}) {
id_flat: objgrp_flat_id
byFlatId: objectByObjgrpFlatMemberId {
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/networkObject/fragments/networkObjectDetailsChangesOld.graphql b/roles/common/files/fwo-api-calls/networkObject/fragments/networkObjectDetailsChangesOld.graphql
similarity index 72%
rename from roles/lib/files/FWO.Api.Client/APIcalls/networkObject/fragments/networkObjectDetailsChangesOld.graphql
rename to roles/common/files/fwo-api-calls/networkObject/fragments/networkObjectDetailsChangesOld.graphql
index ce44dd2f69..e5d3f4e6dd 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/networkObject/fragments/networkObjectDetailsChangesOld.graphql
+++ b/roles/common/files/fwo-api-calls/networkObject/fragments/networkObjectDetailsChangesOld.graphql
@@ -20,7 +20,14 @@ fragment networkObjectDetailsChangesOld on object {
obj_comment
obj_member_names
obj_member_refs
- objgrps(order_by: {objgrp_member_id: asc}) {
+ objgrps(where: {
+ active: { _eq: $active }
+ import_created: { _lte: $import_id_old }
+ _or: [
+ {removed: {_is_null: true}}
+ {removed: {_gt: $import_id_old}}
+ ]
+ } order_by: {objgrp_member_id: asc}) {
id: objgrp_member_id
byId: objectByObjgrpMemberId {
obj_id
@@ -32,8 +39,11 @@ fragment networkObjectDetailsChangesOld on object {
}
objgrp_flats(where: {
active: { _eq: $active }
- import_last_seen: { _gte: $import_id_old }
import_created: { _lte: $import_id_old }
+ _or: [
+ {removed: {_is_null: true}}
+ {removed: {_gt: $import_id_old}}
+ ]
} order_by: {objgrp_flat_member_id: asc}) {
id_flat: objgrp_flat_id
byFlatId: objectByObjgrpFlatMemberId {
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/networkObject/fragments/networkObjectDetailsForVariance.graphql b/roles/common/files/fwo-api-calls/networkObject/fragments/networkObjectDetailsForVariance.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/networkObject/fragments/networkObjectDetailsForVariance.graphql
rename to roles/common/files/fwo-api-calls/networkObject/fragments/networkObjectDetailsForVariance.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/networkObject/fragments/networkObjectOverview.graphql b/roles/common/files/fwo-api-calls/networkObject/fragments/networkObjectOverview.graphql
similarity index 94%
rename from roles/lib/files/FWO.Api.Client/APIcalls/networkObject/fragments/networkObjectOverview.graphql
rename to roles/common/files/fwo-api-calls/networkObject/fragments/networkObjectOverview.graphql
index 35ce2218dd..508cb89c2f 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/networkObject/fragments/networkObjectOverview.graphql
+++ b/roles/common/files/fwo-api-calls/networkObject/fragments/networkObjectOverview.graphql
@@ -3,6 +3,7 @@ fragment networkObjectOverview on object {
obj_ip_end
obj_name
obj_id
+ obj_uid
type: stm_obj_typ {
id: obj_typ_id
name: obj_typ_name
diff --git a/roles/common/files/fwo-api-calls/networkObject/getMapOfUid2Id.graphql b/roles/common/files/fwo-api-calls/networkObject/getMapOfUid2Id.graphql
new file mode 100644
index 0000000000..e5568dd1fc
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/networkObject/getMapOfUid2Id.graphql
@@ -0,0 +1,13 @@
+query getMapOfUid2Id($uids: [String!], $mgmId: Int!) {
+ object(where: {
+ obj_uid: {_in: $uids},
+ _or: [
+ {mgm_id: {_eq: $mgmId}},
+ {management: {multi_device_manager_id: {_eq: $mgmId}}}
+ ],
+ removed: {_is_null: true}
+ }) {
+ obj_id
+ obj_uid
+ }
+}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/networkObject/getNetworkObjectDetails.graphql b/roles/common/files/fwo-api-calls/networkObject/getNetworkObjectDetails.graphql
similarity index 87%
rename from roles/lib/files/FWO.Api.Client/APIcalls/networkObject/getNetworkObjectDetails.graphql
rename to roles/common/files/fwo-api-calls/networkObject/getNetworkObjectDetails.graphql
index ac7b677c15..8767e1249b 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/networkObject/getNetworkObjectDetails.graphql
+++ b/roles/common/files/fwo-api-calls/networkObject/getNetworkObjectDetails.graphql
@@ -19,7 +19,10 @@ query getNetworkObjectDetails(
where: {
stm_obj_typ: { obj_typ_name: { _in: $nwObjTyp } }
active: { _eq: $active }
- obj_last_seen: { _gte: $import_id_start }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gte: $import_id_start } }
+ ]
obj_create: { _lte: $import_id_end }
obj_name: { _in: $obj_name }
obj_ip: { _in: $obj_ip }
diff --git a/roles/common/files/fwo-api-calls/networkObject/getNetworkObjectsForManagement.graphql b/roles/common/files/fwo-api-calls/networkObject/getNetworkObjectsForManagement.graphql
new file mode 100644
index 0000000000..515a9d166a
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/networkObject/getNetworkObjectsForManagement.graphql
@@ -0,0 +1,20 @@
+query getNetworkObjectsForManagement (
+ $mgmId: Int
+ $objTypeIds: [Int!]
+ $active: Boolean
+ $import_id_start: bigint
+ $import_id_end: bigint
+){
+ object(where: {
+ mgm_id: { _eq: $mgmId }
+ active: {_eq: true}
+ obj_typ_id: {_in: $objTypeIds}
+ obj_create: {_lte: $import_id_end}
+ _or: [
+ {removed: {_is_null: true}}
+ {removed: {_gt: $import_id_start}}
+ ]
+ }) {
+ ...networkObjectDetailsForVariance
+ }
+}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/networkService/fragments/networkServiceDetails.graphql b/roles/common/files/fwo-api-calls/networkService/fragments/networkServiceDetails.graphql
similarity index 82%
rename from roles/lib/files/FWO.Api.Client/APIcalls/networkService/fragments/networkServiceDetails.graphql
rename to roles/common/files/fwo-api-calls/networkService/fragments/networkServiceDetails.graphql
index 9fdda6c4df..8443db31c1 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/networkService/fragments/networkServiceDetails.graphql
+++ b/roles/common/files/fwo-api-calls/networkService/fragments/networkServiceDetails.graphql
@@ -28,8 +28,12 @@ fragment networkServiceDetails on service {
svc_member_names
svc_member_refs
svcgrps(where: {
- import_last_seen: { _gte: $import_id_start }
+ active: { _eq: $active }
import_created: { _lte: $import_id_end }
+ _or: [
+ {removed: {_is_null: true}}
+ {removed: {_gt: $import_id_start}}
+ ]
} order_by: { svcgrp_member_id: asc }) {
id: svcgrp_member_id
byId: serviceBySvcgrpMemberId {
@@ -42,6 +46,11 @@ fragment networkServiceDetails on service {
}
svcgrp_flats(where: {
active: { _eq: $active }
+ import_created: { _lte: $import_id_end }
+ _or: [
+ {removed: {_is_null: true}}
+ {removed: {_gt: $import_id_start}}
+ ]
} order_by: { svcgrp_flat_member_id: asc }) {
flat_id: svcgrp_flat_id
byFlatId: serviceBySvcgrpFlatMemberId {
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/networkService/fragments/networkServiceDetailsChangesNew.graphql b/roles/common/files/fwo-api-calls/networkService/fragments/networkServiceDetailsChangesNew.graphql
similarity index 82%
rename from roles/lib/files/FWO.Api.Client/APIcalls/networkService/fragments/networkServiceDetailsChangesNew.graphql
rename to roles/common/files/fwo-api-calls/networkService/fragments/networkServiceDetailsChangesNew.graphql
index 283f91909e..f07c1815af 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/networkService/fragments/networkServiceDetailsChangesNew.graphql
+++ b/roles/common/files/fwo-api-calls/networkService/fragments/networkServiceDetailsChangesNew.graphql
@@ -28,8 +28,12 @@ fragment networkServiceDetailsChangesNew on service {
svc_member_names
svc_member_refs
svcgrps(where: {
- import_last_seen: { _gte: $import_id_new }
+ active: { _eq: $active }
import_created: { _lte: $import_id_new }
+ _or: [
+ {removed: {_is_null: true}}
+ {removed: {_gt: $import_id_new}}
+ ]
} order_by: { svcgrp_member_id: asc }) {
id: svcgrp_member_id
byId: serviceBySvcgrpMemberId {
@@ -42,6 +46,11 @@ fragment networkServiceDetailsChangesNew on service {
}
svcgrp_flats(where: {
active: { _eq: $active }
+ import_created: { _lte: $import_id_new }
+ _or: [
+ {removed: {_is_null: true}}
+ {removed: {_gt: $import_id_new}}
+ ]
} order_by: { svcgrp_flat_member_id: asc }) {
flat_id: svcgrp_flat_id
byFlatId: serviceBySvcgrpFlatMemberId {
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/networkService/fragments/networkServiceDetailsChangesOld.graphql b/roles/common/files/fwo-api-calls/networkService/fragments/networkServiceDetailsChangesOld.graphql
similarity index 82%
rename from roles/lib/files/FWO.Api.Client/APIcalls/networkService/fragments/networkServiceDetailsChangesOld.graphql
rename to roles/common/files/fwo-api-calls/networkService/fragments/networkServiceDetailsChangesOld.graphql
index 93534a1f0f..267fb14897 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/networkService/fragments/networkServiceDetailsChangesOld.graphql
+++ b/roles/common/files/fwo-api-calls/networkService/fragments/networkServiceDetailsChangesOld.graphql
@@ -28,8 +28,12 @@ fragment networkServiceDetailsChangesOld on service {
svc_member_names
svc_member_refs
svcgrps(where: {
- import_last_seen: { _gte: $import_id_old }
+ active: { _eq: $active }
import_created: { _lte: $import_id_old }
+ _or: [
+ {removed: {_is_null: true}}
+ {removed: {_gt: $import_id_old}}
+ ]
} order_by: { svcgrp_member_id: asc }) {
id: svcgrp_member_id
byId: serviceBySvcgrpMemberId {
@@ -42,6 +46,11 @@ fragment networkServiceDetailsChangesOld on service {
}
svcgrp_flats(where: {
active: { _eq: $active }
+ import_created: { _lte: $import_id_old }
+ _or: [
+ {removed: {_is_null: true}}
+ {removed: {_gt: $import_id_old}}
+ ]
} order_by: { svcgrp_flat_member_id: asc }) {
flat_id: svcgrp_flat_id
byFlatId: serviceBySvcgrpFlatMemberId {
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/networkService/fragments/networkServiceOverview.graphql b/roles/common/files/fwo-api-calls/networkService/fragments/networkServiceOverview.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/networkService/fragments/networkServiceOverview.graphql
rename to roles/common/files/fwo-api-calls/networkService/fragments/networkServiceOverview.graphql
diff --git a/roles/common/files/fwo-api-calls/networkService/getMapOfUid2Id.graphql b/roles/common/files/fwo-api-calls/networkService/getMapOfUid2Id.graphql
new file mode 100644
index 0000000000..48a62fadba
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/networkService/getMapOfUid2Id.graphql
@@ -0,0 +1,13 @@
+query getMapOfUid2Id($uids: [String!], $mgmId: Int!) {
+ service(where: {
+ svc_uid: {_in: $uids},
+ _or: [
+ {mgm_id: {_eq: $mgmId}},
+ {management: {multi_device_manager_id: {_eq: $mgmId}}}
+ ],
+ removed: {_is_null: true}
+ }) {
+ svc_id
+ svc_uid
+ }
+}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/networkService/getNetworkServiceDetails.graphql b/roles/common/files/fwo-api-calls/networkService/getNetworkServiceDetails.graphql
similarity index 85%
rename from roles/lib/files/FWO.Api.Client/APIcalls/networkService/getNetworkServiceDetails.graphql
rename to roles/common/files/fwo-api-calls/networkService/getNetworkServiceDetails.graphql
index c52090ffb2..36d7b23c9e 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/networkService/getNetworkServiceDetails.graphql
+++ b/roles/common/files/fwo-api-calls/networkService/getNetworkServiceDetails.graphql
@@ -15,8 +15,11 @@ query getNetworkServiceDetails(
limit: $limit
offset: $offset
where: {
+ _or: [
+ { removed: { _is_null: true } },
+ { removed: { _gte: $import_id_start } }
+ ]
active: { _eq: $active }
- svc_last_seen: { _gte: $import_id_start }
svc_create: { _lte: $import_id_end }
svc_name: { _in: $svc_name }
svc_port: { _in: $svc_port }
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/networking/analyzePath.graphql b/roles/common/files/fwo-api-calls/networking/analyzePath.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/networking/analyzePath.graphql
rename to roles/common/files/fwo-api-calls/networking/analyzePath.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/networking/getAllNetworkInfosPerDevice.graphql b/roles/common/files/fwo-api-calls/networking/getAllNetworkInfosPerDevice.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/networking/getAllNetworkInfosPerDevice.graphql
rename to roles/common/files/fwo-api-calls/networking/getAllNetworkInfosPerDevice.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/networking/getAllNetworkInfosTable.graphql b/roles/common/files/fwo-api-calls/networking/getAllNetworkInfosTable.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/networking/getAllNetworkInfosTable.graphql
rename to roles/common/files/fwo-api-calls/networking/getAllNetworkInfosTable.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/notification/addNotification.graphql b/roles/common/files/fwo-api-calls/notification/addNotification.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/notification/addNotification.graphql
rename to roles/common/files/fwo-api-calls/notification/addNotification.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/notification/deleteNotification.graphql b/roles/common/files/fwo-api-calls/notification/deleteNotification.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/notification/deleteNotification.graphql
rename to roles/common/files/fwo-api-calls/notification/deleteNotification.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/notification/getNotifications.graphql b/roles/common/files/fwo-api-calls/notification/getNotifications.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/notification/getNotifications.graphql
rename to roles/common/files/fwo-api-calls/notification/getNotifications.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/notification/updateNotification.graphql b/roles/common/files/fwo-api-calls/notification/updateNotification.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/notification/updateNotification.graphql
rename to roles/common/files/fwo-api-calls/notification/updateNotification.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/notification/updateNotificationLastSent.graphql b/roles/common/files/fwo-api-calls/notification/updateNotificationLastSent.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/notification/updateNotificationLastSent.graphql
rename to roles/common/files/fwo-api-calls/notification/updateNotificationLastSent.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/owner/deactivateOwner.graphql b/roles/common/files/fwo-api-calls/owner/deactivateOwner.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/owner/deactivateOwner.graphql
rename to roles/common/files/fwo-api-calls/owner/deactivateOwner.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/owner/deleteAreaIpData.graphql b/roles/common/files/fwo-api-calls/owner/deleteAreaIpData.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/owner/deleteAreaIpData.graphql
rename to roles/common/files/fwo-api-calls/owner/deleteAreaIpData.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/owner/deleteLifeCycle.graphql b/roles/common/files/fwo-api-calls/owner/deleteLifeCycle.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/owner/deleteLifeCycle.graphql
rename to roles/common/files/fwo-api-calls/owner/deleteLifeCycle.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/owner/deleteNetworkOwnership.graphql b/roles/common/files/fwo-api-calls/owner/deleteNetworkOwnership.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/owner/deleteNetworkOwnership.graphql
rename to roles/common/files/fwo-api-calls/owner/deleteNetworkOwnership.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/owner/deleteOwner.graphql b/roles/common/files/fwo-api-calls/owner/deleteOwner.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/owner/deleteOwner.graphql
rename to roles/common/files/fwo-api-calls/owner/deleteOwner.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/owner/deleteRuleOwnership.graphql b/roles/common/files/fwo-api-calls/owner/deleteRuleOwnership.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/owner/deleteRuleOwnership.graphql
rename to roles/common/files/fwo-api-calls/owner/deleteRuleOwnership.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/owner/fragments/ownerDetails.graphql b/roles/common/files/fwo-api-calls/owner/fragments/ownerDetails.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/owner/fragments/ownerDetails.graphql
rename to roles/common/files/fwo-api-calls/owner/fragments/ownerDetails.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/owner/getEditableOwners.graphql b/roles/common/files/fwo-api-calls/owner/getEditableOwners.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/owner/getEditableOwners.graphql
rename to roles/common/files/fwo-api-calls/owner/getEditableOwners.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/owner/getEditableOwnersWithConn.graphql b/roles/common/files/fwo-api-calls/owner/getEditableOwnersWithConn.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/owner/getEditableOwnersWithConn.graphql
rename to roles/common/files/fwo-api-calls/owner/getEditableOwnersWithConn.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/owner/getNetworkOwnerships.graphql b/roles/common/files/fwo-api-calls/owner/getNetworkOwnerships.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/owner/getNetworkOwnerships.graphql
rename to roles/common/files/fwo-api-calls/owner/getNetworkOwnerships.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/owner/getOwnerId.graphql b/roles/common/files/fwo-api-calls/owner/getOwnerId.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/owner/getOwnerId.graphql
rename to roles/common/files/fwo-api-calls/owner/getOwnerId.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/owner/getOwnerLifeCycleStates.graphql b/roles/common/files/fwo-api-calls/owner/getOwnerLifeCycleStates.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/owner/getOwnerLifeCycleStates.graphql
rename to roles/common/files/fwo-api-calls/owner/getOwnerLifeCycleStates.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/owner/getOwners.graphql b/roles/common/files/fwo-api-calls/owner/getOwners.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/owner/getOwners.graphql
rename to roles/common/files/fwo-api-calls/owner/getOwners.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/owner/getOwnersForUser.graphql b/roles/common/files/fwo-api-calls/owner/getOwnersForUser.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/owner/getOwnersForUser.graphql
rename to roles/common/files/fwo-api-calls/owner/getOwnersForUser.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/owner/getOwnersFromGroups.graphql b/roles/common/files/fwo-api-calls/owner/getOwnersFromGroups.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/owner/getOwnersFromGroups.graphql
rename to roles/common/files/fwo-api-calls/owner/getOwnersFromGroups.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/owner/getOwnersWithConn.graphql b/roles/common/files/fwo-api-calls/owner/getOwnersWithConn.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/owner/getOwnersWithConn.graphql
rename to roles/common/files/fwo-api-calls/owner/getOwnersWithConn.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/owner/getRuleOwnerships.graphql b/roles/common/files/fwo-api-calls/owner/getRuleOwnerships.graphql
similarity index 93%
rename from roles/lib/files/FWO.Api.Client/APIcalls/owner/getRuleOwnerships.graphql
rename to roles/common/files/fwo-api-calls/owner/getRuleOwnerships.graphql
index 148cb95d9d..912ca11eb8 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/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/lib/files/FWO.Api.Client/APIcalls/owner/newLifeCycle.graphql b/roles/common/files/fwo-api-calls/owner/newLifeCycle.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/owner/newLifeCycle.graphql
rename to roles/common/files/fwo-api-calls/owner/newLifeCycle.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/owner/newNetworkOwnership.graphql b/roles/common/files/fwo-api-calls/owner/newNetworkOwnership.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/owner/newNetworkOwnership.graphql
rename to roles/common/files/fwo-api-calls/owner/newNetworkOwnership.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/owner/newOwner.graphql b/roles/common/files/fwo-api-calls/owner/newOwner.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/owner/newOwner.graphql
rename to roles/common/files/fwo-api-calls/owner/newOwner.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/owner/newRuleOwnership.graphql b/roles/common/files/fwo-api-calls/owner/newRuleOwnership.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/owner/newRuleOwnership.graphql
rename to roles/common/files/fwo-api-calls/owner/newRuleOwnership.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/owner/setDefaultOwner.graphql b/roles/common/files/fwo-api-calls/owner/setDefaultOwner.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/owner/setDefaultOwner.graphql
rename to roles/common/files/fwo-api-calls/owner/setDefaultOwner.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/owner/setOwnerLastCheck.graphql b/roles/common/files/fwo-api-calls/owner/setOwnerLastCheck.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/owner/setOwnerLastCheck.graphql
rename to roles/common/files/fwo-api-calls/owner/setOwnerLastCheck.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/owner/setOwnerLastRecert.graphql b/roles/common/files/fwo-api-calls/owner/setOwnerLastRecert.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/owner/setOwnerLastRecert.graphql
rename to roles/common/files/fwo-api-calls/owner/setOwnerLastRecert.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/owner/updateLifeCycle.graphql b/roles/common/files/fwo-api-calls/owner/updateLifeCycle.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/owner/updateLifeCycle.graphql
rename to roles/common/files/fwo-api-calls/owner/updateLifeCycle.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/owner/updateOwner.graphql b/roles/common/files/fwo-api-calls/owner/updateOwner.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/owner/updateOwner.graphql
rename to roles/common/files/fwo-api-calls/owner/updateOwner.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/recertification/addRecertEntries.graphql b/roles/common/files/fwo-api-calls/recertification/addRecertEntries.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/recertification/addRecertEntries.graphql
rename to roles/common/files/fwo-api-calls/recertification/addRecertEntries.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/recertification/clearOpenRecerts.graphql b/roles/common/files/fwo-api-calls/recertification/clearOpenRecerts.graphql
similarity index 59%
rename from roles/lib/files/FWO.Api.Client/APIcalls/recertification/clearOpenRecerts.graphql
rename to roles/common/files/fwo-api-calls/recertification/clearOpenRecerts.graphql
index 44cf37d21d..d882321e4a 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/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/lib/files/FWO.Api.Client/APIcalls/recertification/fragments/ruleOpenCertOverview.graphql b/roles/common/files/fwo-api-calls/recertification/fragments/ruleOpenCertOverview.graphql
similarity index 85%
rename from roles/lib/files/FWO.Api.Client/APIcalls/recertification/fragments/ruleOpenCertOverview.graphql
rename to roles/common/files/fwo-api-calls/recertification/fragments/ruleOpenCertOverview.graphql
index 5a8898fc81..01c52ec9ed 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/recertification/fragments/ruleOpenCertOverview.graphql
+++ b/roles/common/files/fwo-api-calls/recertification/fragments/ruleOpenCertOverview.graphql
@@ -2,9 +2,6 @@ fragment ruleOpenCertOverview on rule {
rule_id
rule_uid
rule_action
- device {
- dev_id
- }
section_header: rule_head_text
rule_comment
rule_track
@@ -54,8 +51,11 @@ fragment ruleOpenCertOverview on rule {
nat_rule
xlate_rule
rule_froms(where: {
- rf_last_seen: { _gte: $import_id_start }
rf_create: { _lte: $import_id_end }
+ _or: [
+ {removed: { _is_null: true }}
+ {removed: { _gt: $import_id_start }}
+ ]
}, order_by:{ object: { obj_name: asc } }) {
usr {
...userOverview
@@ -69,8 +69,11 @@ fragment ruleOpenCertOverview on rule {
zone_id
}
rule_tos(where: {
- rt_last_seen: { _gte: $import_id_start }
rt_create: { _lte: $import_id_end }
+ _or: [
+ {removed: { _is_null: true }}
+ {removed: { _gt: $import_id_start }}
+ ]
}, order_by:{ object: { obj_name: asc } }) {
usr {
...userOverview
@@ -80,8 +83,11 @@ fragment ruleOpenCertOverview on rule {
}
}
rule_services(where: {
- rs_last_seen: { _gte: $import_id_start }
rs_create: { _lte: $import_id_end }
+ _or: [
+ {removed: { _is_null: true }}
+ {removed: { _gt: $import_id_start }}
+ ]
}, order_by:{ service: { svc_name: asc } }) {
service {
...networkServiceOverview
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/recertification/getInitialOwnerRecert.graphql b/roles/common/files/fwo-api-calls/recertification/getInitialOwnerRecert.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/recertification/getInitialOwnerRecert.graphql
rename to roles/common/files/fwo-api-calls/recertification/getInitialOwnerRecert.graphql
diff --git a/roles/common/files/fwo-api-calls/recertification/getOpenRecertsForOwners.graphql b/roles/common/files/fwo-api-calls/recertification/getOpenRecertsForOwners.graphql
new file mode 100644
index 0000000000..019d4710bf
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/recertification/getOpenRecertsForOwners.graphql
@@ -0,0 +1,18 @@
+query getOpenRecertsForOwners($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 }
+ ) {
+ id
+ rule_metadata_id
+ rule_id
+ ip_match
+ owner_id
+ user_dn
+ recertified
+ next_recert_date
+ recert_date
+ comment
+ owner_recert_id
+ }
+}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/recertification/getOpenRecertsForRule.graphql b/roles/common/files/fwo-api-calls/recertification/getOpenRecertsForRule.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/recertification/getOpenRecertsForRule.graphql
rename to roles/common/files/fwo-api-calls/recertification/getOpenRecertsForRule.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/recertification/getOwnerRecerts.graphql b/roles/common/files/fwo-api-calls/recertification/getOwnerRecerts.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/recertification/getOwnerRecerts.graphql
rename to roles/common/files/fwo-api-calls/recertification/getOwnerRecerts.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/recertification/prepareNextRecertification.graphql b/roles/common/files/fwo-api-calls/recertification/prepareNextRecertification.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/recertification/prepareNextRecertification.graphql
rename to roles/common/files/fwo-api-calls/recertification/prepareNextRecertification.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/recertification/recertify.graphql b/roles/common/files/fwo-api-calls/recertification/recertify.graphql
similarity index 99%
rename from roles/lib/files/FWO.Api.Client/APIcalls/recertification/recertify.graphql
rename to roles/common/files/fwo-api-calls/recertification/recertify.graphql
index fadf0fe71e..f5c4459c1b 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/recertification/recertify.graphql
+++ b/roles/common/files/fwo-api-calls/recertification/recertify.graphql
@@ -19,4 +19,4 @@ mutation recertify(
) {
affected_rows
}
- }
+}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/recertification/recertifyOwner.graphql b/roles/common/files/fwo-api-calls/recertification/recertifyOwner.graphql
similarity index 86%
rename from roles/lib/files/FWO.Api.Client/APIcalls/recertification/recertifyOwner.graphql
rename to roles/common/files/fwo-api-calls/recertification/recertifyOwner.graphql
index ecbd39940c..245f6ac9f9 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/recertification/recertifyOwner.graphql
+++ b/roles/common/files/fwo-api-calls/recertification/recertifyOwner.graphql
@@ -5,7 +5,7 @@ mutation recertifyOwner(
$recertDate: timestamp
$nextRecertDate: timestamp
$comment: String
- ) {
+) {
insert_owner_recertification(
objects: {
owner_id: $ownerId
@@ -14,10 +14,10 @@ mutation recertifyOwner(
recert_date: $recertDate
next_recert_date: $nextRecertDate
comment: $comment
- }
- ) {
- returning {
- newIdLong: id
- }
+ }
+ ) {
+ returning {
+ newIdLong: id
}
}
+}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/recertification/recertifyRuleDirectly.graphql b/roles/common/files/fwo-api-calls/recertification/recertifyRuleDirectly.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/recertification/recertifyRuleDirectly.graphql
rename to roles/common/files/fwo-api-calls/recertification/recertifyRuleDirectly.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/recertification/refreshViewRuleWithOwner.graphql b/roles/common/files/fwo-api-calls/recertification/refreshViewRuleWithOwner.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/recertification/refreshViewRuleWithOwner.graphql
rename to roles/common/files/fwo-api-calls/recertification/refreshViewRuleWithOwner.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/recertification/updateRecertReportId.graphql b/roles/common/files/fwo-api-calls/recertification/updateRecertReportId.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/recertification/updateRecertReportId.graphql
rename to roles/common/files/fwo-api-calls/recertification/updateRecertReportId.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/addGeneratedReport.graphql b/roles/common/files/fwo-api-calls/report/addGeneratedReport.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/report/addGeneratedReport.graphql
rename to roles/common/files/fwo-api-calls/report/addGeneratedReport.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/addReportSchedule.graphql b/roles/common/files/fwo-api-calls/report/addReportSchedule.graphql
similarity index 96%
rename from roles/lib/files/FWO.Api.Client/APIcalls/report/addReportSchedule.graphql
rename to roles/common/files/fwo-api-calls/report/addReportSchedule.graphql
index e395316958..d9b28c63ae 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/report/addReportSchedule.graphql
+++ b/roles/common/files/fwo-api-calls/report/addReportSchedule.graphql
@@ -21,7 +21,7 @@ mutation addReportSchedule(
}
) {
returning {
- report_schedule_id
+ newId: report_schedule_id
}
}
}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/addReportScheduleFileFormats.graphql b/roles/common/files/fwo-api-calls/report/addReportScheduleFileFormats.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/report/addReportScheduleFileFormats.graphql
rename to roles/common/files/fwo-api-calls/report/addReportScheduleFileFormats.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/addReportTemplate.graphql b/roles/common/files/fwo-api-calls/report/addReportTemplate.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/report/addReportTemplate.graphql
rename to roles/common/files/fwo-api-calls/report/addReportTemplate.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/addReportTemplatePermission.graphql b/roles/common/files/fwo-api-calls/report/addReportTemplatePermission.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/report/addReportTemplatePermission.graphql
rename to roles/common/files/fwo-api-calls/report/addReportTemplatePermission.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/countReportSchedule.graphql b/roles/common/files/fwo-api-calls/report/countReportSchedule.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/report/countReportSchedule.graphql
rename to roles/common/files/fwo-api-calls/report/countReportSchedule.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/deleteGeneratedReport.graphql b/roles/common/files/fwo-api-calls/report/deleteGeneratedReport.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/report/deleteGeneratedReport.graphql
rename to roles/common/files/fwo-api-calls/report/deleteGeneratedReport.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/deleteReportSchedule.graphql b/roles/common/files/fwo-api-calls/report/deleteReportSchedule.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/report/deleteReportSchedule.graphql
rename to roles/common/files/fwo-api-calls/report/deleteReportSchedule.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/deleteReportTemplate.graphql b/roles/common/files/fwo-api-calls/report/deleteReportTemplate.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/report/deleteReportTemplate.graphql
rename to roles/common/files/fwo-api-calls/report/deleteReportTemplate.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/editReportSchedule.graphql b/roles/common/files/fwo-api-calls/report/editReportSchedule.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/report/editReportSchedule.graphql
rename to roles/common/files/fwo-api-calls/report/editReportSchedule.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/getReportFilteredObjectDetails.graphql b/roles/common/files/fwo-api-calls/report/getAllObjectDetailsInReport.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/report/getReportFilteredObjectDetails.graphql
rename to roles/common/files/fwo-api-calls/report/getAllObjectDetailsInReport.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/getGeneratedReport.graphql b/roles/common/files/fwo-api-calls/report/getGeneratedReport.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/report/getGeneratedReport.graphql
rename to roles/common/files/fwo-api-calls/report/getGeneratedReport.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/getGeneratedReports.graphql b/roles/common/files/fwo-api-calls/report/getGeneratedReports.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/report/getGeneratedReports.graphql
rename to roles/common/files/fwo-api-calls/report/getGeneratedReports.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/getImportsToNotifyForAnyChanges.graphql b/roles/common/files/fwo-api-calls/report/getImportsToNotify.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/report/getImportsToNotifyForAnyChanges.graphql
rename to roles/common/files/fwo-api-calls/report/getImportsToNotify.graphql
diff --git a/roles/common/files/fwo-api-calls/report/getImportsToNotifyForAnyChanges.graphql b/roles/common/files/fwo-api-calls/report/getImportsToNotifyForAnyChanges.graphql
new file mode 100644
index 0000000000..63d0782288
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/report/getImportsToNotifyForAnyChanges.graphql
@@ -0,0 +1,15 @@
+query getImportsToNotifyForAnyChanges {
+ import_control(where: {
+ successful_import: {_eq: true}
+ any_changes_found: {_eq: true}
+ notification_done: {_eq: false}
+ } order_by: {stop_time: asc}) {
+ control_id
+ stop_time
+ mgm_id
+ management{
+ mgm_name
+ }
+ security_relevant_changes_counter
+ }
+}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/getImportsToNotifyForRuleChanges.graphql b/roles/common/files/fwo-api-calls/report/getImportsToNotifyForRuleChanges.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/report/getImportsToNotifyForRuleChanges.graphql
rename to roles/common/files/fwo-api-calls/report/getImportsToNotifyForRuleChanges.graphql
diff --git a/roles/common/files/fwo-api-calls/report/getManagementForLatestNormalizedConfig.graphql b/roles/common/files/fwo-api-calls/report/getManagementForLatestNormalizedConfig.graphql
new file mode 100644
index 0000000000..d9d1eb9f86
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/report/getManagementForLatestNormalizedConfig.graphql
@@ -0,0 +1,169 @@
+query getManagementForLatestNormalizedConfig(
+ $mgm_ids: [Int!]!
+) {
+ management(where: { mgm_id: { _in: $mgm_ids } }) {
+ id: mgm_id
+ uid: mgm_uid
+ name: mgm_name
+ networkObjects: objects(where: { removed: { _is_null: true } }) {
+ ...networkObjectFragment
+ }
+ serviceObjects: services(where: { removed: { _is_null: true } }) {
+ ...serviceObjectFragment
+ }
+ userObjects: usrs(where: { removed: { _is_null: true } }) {
+ ...userObjectFragment
+ }
+ zoneObjects: zones(where: { removed: { _is_null: true } }) {
+ zone_name
+ }
+ rulebases(where: { removed: { _is_null: true } }) {
+ ...rulebaseFragment
+ }
+ devices {
+ ...deviceFragment
+ }
+ }
+}
+
+fragment networkObjectFragment on object {
+ obj_id
+ obj_uid
+ obj_name
+ obj_ip
+ obj_ip_end
+ type: stm_obj_typ {
+ name: obj_typ_name
+ }
+ obj_color: stm_color {
+ color_name
+ }
+ obj_member_refs
+ obj_member_names
+ obj_comment
+}
+
+fragment serviceObjectFragment on service {
+ svc_id
+ svc_uid
+ svc_name
+ svc_port
+ svc_port_end
+ svc_timeout
+ service_type: stm_svc_typ {
+ name: svc_typ_name
+ }
+ stm_color {
+ color_name
+ }
+ ip_proto_id
+ svc_member_refs
+ svc_member_names
+ svc_comment
+ svc_rpcnr
+}
+
+fragment userObjectFragment on usr {
+ user_id
+ user_uid
+ user_name
+ user_lastname
+ user_firstname
+ type: stm_usr_typ {
+ usr_typ_name
+ }
+ user_member_names
+ user_member_refs
+ user_comment
+}
+
+fragment rulebaseFragment on rulebase {
+ id
+ name
+ uid
+ is_global
+ rules(where: { removed: { _is_null: true } } order_by: { rule_num_numeric: asc }) {
+ ...ruleFragment
+ }
+}
+
+fragment ruleFragment on rule {
+ rule_id
+ rule_uid
+ rule_name
+ rule_num
+ rule_num_numeric
+ rule_disabled
+ rule_src_neg
+ rule_src
+ rule_src_refs
+ rule_dst_neg
+ rule_dst
+ rule_dst_refs
+ rule_svc_neg
+ rule_svc
+ rule_svc_refs
+ rule_action
+ rule_track
+ rule_installon
+ rule_time
+ rule_custom_fields
+ rule_implied
+ nat_rule
+ uiuser {
+ uiuser_username
+ }
+ rule {
+ rule_uid
+ }
+ rule_metadatum {
+ rule_last_hit
+ }
+ rule_comment
+ rule_from_zones {
+ zone {
+ zone_name
+ }
+ }
+ rule_to_zones {
+ zone {
+ zone_name
+ }
+ }
+ section_header: rule_head_text
+}
+
+fragment deviceFragment on device {
+ id: dev_id
+ uid: dev_uid
+ name: dev_name
+ #TODO: routing
+ #TODO: interfaces
+ rulebase_links(where: { removed: { _is_null: true } }) {
+ ...ruleBaseLinkFragment
+ }
+ global_rulebase_uid
+ #TODO: EnforcedPolicyUids
+ #TODO: EnforcedNatPolicyUids
+ importDisabled: do_not_import
+ hideInUi: hide_in_gui
+}
+
+fragment ruleBaseLinkFragment on rulebase_link {
+ gw_id
+ rule {
+ rule_uid
+ }
+ rulebaseByFromRulebaseId {
+ uid
+ }
+ rulebase {
+ uid
+ }
+ stm_link_type {
+ name
+ }
+ is_initial
+ is_global
+ is_section
+}
diff --git a/roles/common/files/fwo-api-calls/report/getManagementForNormalizedConfig.graphql b/roles/common/files/fwo-api-calls/report/getManagementForNormalizedConfig.graphql
new file mode 100644
index 0000000000..f54b0e9783
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/report/getManagementForNormalizedConfig.graphql
@@ -0,0 +1,216 @@
+query getManagementForNormalizedConfig(
+ $mgm_id: Int!
+ $import_id: Int!
+) {
+ management(where: { mgm_id: { _eq: $mgm_id } }) {
+ id: mgm_id
+ uid: mgm_uid
+ name: mgm_name
+ networkObjects: objects(
+ where: {
+ obj_create: { _lte: $import_id }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gte: $import_id } }
+ ]
+ }
+ ) {
+ ...networkObjectFragment
+ }
+ serviceObjects: services(
+ where: {
+ svc_create: { _lte: $import_id }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gte: $import_id } }
+ ]
+ }
+ ) {
+ ...serviceObjectFragment
+ }
+ userObjects: usrs(
+ where: {
+ user_create: { _lte: $import_id_end }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gte: $import_id_start } }
+ ]
+ }
+ ) {
+ ...userObjectFragment
+ }
+ zoneObjects: zones(
+ where: {
+ zone_create: { _lte: $import_id }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gte: $import_id } }
+ ]
+ }
+ ) {
+ zone_name
+ }
+ rulebases(
+ where: {
+ created: { _lte: $import_id }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gte: $import_id } }
+ ]
+ }
+ ) {
+ ...rulebaseFragment
+ }
+ devices {
+ ...deviceFragment
+ }
+ }
+}
+
+fragment networkObjectFragment on object {
+ obj_id
+ obj_uid
+ obj_name
+ obj_ip
+ obj_ip_end
+ type: stm_obj_typ {
+ name: obj_typ_name
+ }
+ obj_color: stm_color {
+ color_name
+ }
+ obj_member_refs
+ obj_member_names
+ obj_comment
+}
+
+fragment serviceObjectFragment on service {
+ svc_id
+ svc_uid
+ svc_name
+ svc_port
+ svc_port_end
+ svc_timeout
+ service_type: stm_svc_typ {
+ name: svc_typ_name
+ }
+ stm_color {
+ color_name
+ }
+ ip_proto_id
+ svc_member_refs
+ svc_member_names
+ svc_comment
+ svc_rpcnr
+}
+
+fragment userObjectFragment on usr {
+ user_id
+ user_uid
+ user_name
+ user_lastname
+ user_firstname
+ type: stm_usr_typ {
+ usr_typ_name
+ }
+ user_member_names
+ user_member_refs
+ user_comment
+}
+
+fragment rulebaseFragment on rulebase {
+ id
+ name
+ uid
+ is_global
+ rules(where: {
+ rule_create: { _lte: $import_id }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gte: $import_id } }
+ ]
+ }) {
+ ...ruleFragment
+ }
+}
+
+fragment ruleFragment on rule {
+ rule_id
+ rule_uid
+ rule_name
+ rule_num
+ rule_num_numeric
+ rule_disabled
+ rule_src_neg
+ rule_src
+ rule_src_refs
+ rule_dst_neg
+ rule_dst
+ rule_dst_refs
+ rule_svc_neg
+ rule_svc
+ rule_svc_refs
+ rule_action
+ rule_track
+ rule_installon
+ rule_time
+ rule_custom_fields
+ rule_implied
+ nat_rule
+ uiuser {
+ uiuser_username
+ }
+ rule {
+ rule_uid
+ }
+ rule_metadatum {
+ rule_last_hit
+ }
+ rule_comment
+ rule_from_zones {
+ zone {
+ zone_name
+ }
+ }
+ rule_to_zones {
+ zone {
+ zone_name
+ }
+ }
+ section_header: rule_head_text
+}
+
+fragment deviceFragment on device {
+ id: dev_id
+ uid: dev_uid
+ name: dev_name
+ #TODO: routing
+ #TODO: interfaces
+ rulebase_links(where: { removed: { _is_null: true } }) {
+ ...ruleBaseLinkFragment
+ }
+ global_rulebase_uid
+ #TODO: EnforcedPolicyUids
+ #TODO: EnforcedNatPolicyUids
+ importDisabled: do_not_import
+ hideInUi: hide_in_gui
+}
+
+fragment ruleBaseLinkFragment on rulebase_link {
+ gw_id
+ rule {
+ rule_uid
+ }
+ rulebaseByFromRulebaseId {
+ uid
+ }
+ rulebase {
+ uid
+ }
+ stm_link_type {
+ name
+ }
+ is_initial
+ is_global
+ is_section
+}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/getModelledRulesByManagementComment.graphql b/roles/common/files/fwo-api-calls/report/getModelledRulesByManagementComment.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/report/getModelledRulesByManagementComment.graphql
rename to roles/common/files/fwo-api-calls/report/getModelledRulesByManagementComment.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/getModelledRulesByManagementName.graphql b/roles/common/files/fwo-api-calls/report/getModelledRulesByManagementName.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/report/getModelledRulesByManagementName.graphql
rename to roles/common/files/fwo-api-calls/report/getModelledRulesByManagementName.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/getRelevantImportIdsAtTime.graphql b/roles/common/files/fwo-api-calls/report/getRelevantImportIdsAtTime.graphql
similarity index 67%
rename from roles/lib/files/FWO.Api.Client/APIcalls/report/getRelevantImportIdsAtTime.graphql
rename to roles/common/files/fwo-api-calls/report/getRelevantImportIdsAtTime.graphql
index 5d7745db59..b8ea16daf0 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/report/getRelevantImportIdsAtTime.graphql
+++ b/roles/common/files/fwo-api-calls/report/getRelevantImportIdsAtTime.graphql
@@ -5,14 +5,18 @@ query getRelevantImportIdsAtTime(
) {
management(where: {
hide_in_gui: {_eq: false}
- mgm_id: {_in: $mgmIds}
- stm_dev_typ:{
- dev_typ_is_multi_mgmt:{_eq:false}
- is_pure_routing_device:{_eq:false}
- }
+ _or: [
+ { mgm_id: {_in: $mgmIds}}
+ {_and: {
+ is_super_manager: {_eq: true}
+ managementByMultiDeviceManagerId: { mgm_id: {_in: $mgmIds}}
+ }}
+ ]
} order_by: {mgm_name: asc}) {
- Name: mgm_name
+ name: mgm_name
id: mgm_id
+ is_super_manager
+ multi_device_manager_id
import: import_controls_aggregate(where: {
stop_time: {_lte: $time}
successful_import: {_eq: true}
diff --git a/roles/common/files/fwo-api-calls/report/getRelevantImportIdsInTimeRange.graphql b/roles/common/files/fwo-api-calls/report/getRelevantImportIdsInTimeRange.graphql
new file mode 100644
index 0000000000..017a620315
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/report/getRelevantImportIdsInTimeRange.graphql
@@ -0,0 +1,36 @@
+query getRelevantImportIdsInTimeRange(
+ $start_time: timestamp!
+ $end_time: timestamp!
+ $mgmIds: [Int!]!
+ $ruleChangesFound: Boolean
+) {
+ management(where: {
+ hide_in_gui: {_eq: false}
+ _or: [
+ { mgm_id: {_in: $mgmIds}}
+ {_and: {
+ is_super_manager: {_eq: true}
+ managementByMultiDeviceManagerId: { mgm_id: {_in: $mgmIds}}
+ }}
+ ]
+ } order_by: {mgm_name: asc}) {
+ id: mgm_id
+ managementByMultiDeviceManagerId {
+ id: mgm_id
+ }
+ import_controls(
+ where: {
+ _and: [
+ { stop_time: { _gte: $start_time } }
+ { stop_time: { _lte: $end_time } }
+ ]
+ successful_import: { _eq: true }
+ any_changes_found: { _eq: true }
+ rule_changes_found: { _eq: $ruleChangesFound }
+ }
+ order_by: { stop_time: asc }
+ ) {
+ control_id
+ }
+ }
+}
\ No newline at end of file
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/getReportById.graphql b/roles/common/files/fwo-api-calls/report/getReportById.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/report/getReportById.graphql
rename to roles/common/files/fwo-api-calls/report/getReportById.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/getReportFilteredNetworkObjectDetails.graphql b/roles/common/files/fwo-api-calls/report/getReportFilteredNetworkObjectDetails.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/report/getReportFilteredNetworkObjectDetails.graphql
rename to roles/common/files/fwo-api-calls/report/getReportFilteredNetworkObjectDetails.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/getReportFilteredNetworkServiceDetails.graphql b/roles/common/files/fwo-api-calls/report/getReportFilteredNetworkServiceDetails.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/report/getReportFilteredNetworkServiceDetails.graphql
rename to roles/common/files/fwo-api-calls/report/getReportFilteredNetworkServiceDetails.graphql
diff --git a/roles/common/files/fwo-api-calls/report/getReportFilteredObjectDetails.graphql b/roles/common/files/fwo-api-calls/report/getReportFilteredObjectDetails.graphql
new file mode 100644
index 0000000000..004dda42d7
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/report/getReportFilteredObjectDetails.graphql
@@ -0,0 +1,35 @@
+query getReportFilteredObjectDetails (
+ $mgmIds: [Int!]
+ $ruleIds: _int8
+ $active: Boolean
+ $import_id_start: bigint
+ $import_id_end: bigint
+ $limit: Int
+ $offset: Int
+) {
+ management(where: { mgm_id: { _in: $mgmIds }, stm_dev_typ:{dev_typ_is_multi_mgmt:{_eq:false}} }) {
+ name: mgm_name
+ id: mgm_id
+ reportNetworkObjects: filter_rule_nwobj_resolveds (
+ args: {rule_ids: $ruleIds, import_id: $import_id_start}
+ limit: $limit
+ offset: $offset
+ ) {
+ ...networkObjectDetails
+ }
+ reportServiceObjects: filter_rule_svc_resolveds (
+ args: {rule_ids: $ruleIds, import_id: $import_id_start}
+ limit: $limit
+ offset: $offset
+ ) {
+ ...networkServiceDetails
+ }
+ reportUserObjects: filter_rule_user_resolveds (
+ args: {rule_ids: $ruleIds, import_id: $import_id_start}
+ limit: $limit
+ offset: $offset
+ ) {
+ ...userDetails
+ }
+ }
+}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/getReportFilteredUserDetails.graphql b/roles/common/files/fwo-api-calls/report/getReportFilteredUserDetails.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/report/getReportFilteredUserDetails.graphql
rename to roles/common/files/fwo-api-calls/report/getReportFilteredUserDetails.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/getReportSchedules.graphql b/roles/common/files/fwo-api-calls/report/getReportSchedules.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/report/getReportSchedules.graphql
rename to roles/common/files/fwo-api-calls/report/getReportSchedules.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/getReportTemplates.graphql b/roles/common/files/fwo-api-calls/report/getReportTemplates.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/report/getReportTemplates.graphql
rename to roles/common/files/fwo-api-calls/report/getReportTemplates.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/getReportsOverview.graphql b/roles/common/files/fwo-api-calls/report/getReportsOverview.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/report/getReportsOverview.graphql
rename to roles/common/files/fwo-api-calls/report/getReportsOverview.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/getRuleIdsOfImport.graphql b/roles/common/files/fwo-api-calls/report/getRuleIdsOfImport.graphql
similarity index 67%
rename from roles/lib/files/FWO.Api.Client/APIcalls/report/getRuleIdsOfImport.graphql
rename to roles/common/files/fwo-api-calls/report/getRuleIdsOfImport.graphql
index ad091206a3..25330e6189 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/report/getRuleIdsOfImport.graphql
+++ b/roles/common/files/fwo-api-calls/report/getRuleIdsOfImport.graphql
@@ -5,8 +5,11 @@ query getRuleIdsOfImport(
) {
rule(
where: {
- rule_last_seen: { _gte: $import_id_start }
rule_create: { _lte: $import_id_end }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt: $import_id_start } }
+ ]
dev_id: { _in: $devIds }
}
) {
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/getRuleUidsOfDevice.graphql b/roles/common/files/fwo-api-calls/report/getRuleUidsOfDevice.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/report/getRuleUidsOfDevice.graphql
rename to roles/common/files/fwo-api-calls/report/getRuleUidsOfDevice.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/getRulesByManagement.graphql b/roles/common/files/fwo-api-calls/report/getRulesByManagement.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/report/getRulesByManagement.graphql
rename to roles/common/files/fwo-api-calls/report/getRulesByManagement.graphql
diff --git a/roles/common/files/fwo-api-calls/report/getUsageDataCount.graphql b/roles/common/files/fwo-api-calls/report/getUsageDataCount.graphql
new file mode 100644
index 0000000000..3fdd082b9c
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/report/getUsageDataCount.graphql
@@ -0,0 +1,13 @@
+query getUsageDataCount($devId: Int!) {
+ device(where: {dev_id: {_eq: $devId}}) {
+ rulebase_on_gateways {
+ rulebase {
+ rulesWithHits: rule_metadata_aggregate(where: {rule_last_hit: {_is_null: false}}) {
+ aggregate {
+ count
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/setImportsNotified.graphql b/roles/common/files/fwo-api-calls/report/setImportsNotified.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/report/setImportsNotified.graphql
rename to roles/common/files/fwo-api-calls/report/setImportsNotified.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/statisticsCurrent.graphql b/roles/common/files/fwo-api-calls/report/statisticsCurrent.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/report/statisticsCurrent.graphql
rename to roles/common/files/fwo-api-calls/report/statisticsCurrent.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/statisticsCurrentOverall.graphql b/roles/common/files/fwo-api-calls/report/statisticsCurrentOverall.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/report/statisticsCurrentOverall.graphql
rename to roles/common/files/fwo-api-calls/report/statisticsCurrentOverall.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/subscribeGeneratedReportsChanges.graphql b/roles/common/files/fwo-api-calls/report/subscribeGeneratedReportsChanges.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/report/subscribeGeneratedReportsChanges.graphql
rename to roles/common/files/fwo-api-calls/report/subscribeGeneratedReportsChanges.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/subscribeReportScheduleChanges.graphql b/roles/common/files/fwo-api-calls/report/subscribeReportScheduleChanges.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/report/subscribeReportScheduleChanges.graphql
rename to roles/common/files/fwo-api-calls/report/subscribeReportScheduleChanges.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/updateReportTemplate.graphql b/roles/common/files/fwo-api-calls/report/updateReportTemplate.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/report/updateReportTemplate.graphql
rename to roles/common/files/fwo-api-calls/report/updateReportTemplate.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/addCommentToApproval.graphql b/roles/common/files/fwo-api-calls/request/addCommentToApproval.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/addCommentToApproval.graphql
rename to roles/common/files/fwo-api-calls/request/addCommentToApproval.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/addCommentToImplTask.graphql b/roles/common/files/fwo-api-calls/request/addCommentToImplTask.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/addCommentToImplTask.graphql
rename to roles/common/files/fwo-api-calls/request/addCommentToImplTask.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/addCommentToReqTask.graphql b/roles/common/files/fwo-api-calls/request/addCommentToReqTask.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/addCommentToReqTask.graphql
rename to roles/common/files/fwo-api-calls/request/addCommentToReqTask.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/addCommentToTicket.graphql b/roles/common/files/fwo-api-calls/request/addCommentToTicket.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/addCommentToTicket.graphql
rename to roles/common/files/fwo-api-calls/request/addCommentToTicket.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/addExtState.graphql b/roles/common/files/fwo-api-calls/request/addExtState.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/addExtState.graphql
rename to roles/common/files/fwo-api-calls/request/addExtState.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/addOwnerToReqTask.graphql b/roles/common/files/fwo-api-calls/request/addOwnerToReqTask.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/addOwnerToReqTask.graphql
rename to roles/common/files/fwo-api-calls/request/addOwnerToReqTask.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/addStateAction.graphql b/roles/common/files/fwo-api-calls/request/addStateAction.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/addStateAction.graphql
rename to roles/common/files/fwo-api-calls/request/addStateAction.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/deleteAction.graphql b/roles/common/files/fwo-api-calls/request/deleteAction.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/deleteAction.graphql
rename to roles/common/files/fwo-api-calls/request/deleteAction.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/deleteImplementationElement.graphql b/roles/common/files/fwo-api-calls/request/deleteImplementationElement.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/deleteImplementationElement.graphql
rename to roles/common/files/fwo-api-calls/request/deleteImplementationElement.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/deleteImplementationTask.graphql b/roles/common/files/fwo-api-calls/request/deleteImplementationTask.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/deleteImplementationTask.graphql
rename to roles/common/files/fwo-api-calls/request/deleteImplementationTask.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/deleteRequestElement.graphql b/roles/common/files/fwo-api-calls/request/deleteRequestElement.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/deleteRequestElement.graphql
rename to roles/common/files/fwo-api-calls/request/deleteRequestElement.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/deleteRequestTask.graphql b/roles/common/files/fwo-api-calls/request/deleteRequestTask.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/deleteRequestTask.graphql
rename to roles/common/files/fwo-api-calls/request/deleteRequestTask.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/deleteState.graphql b/roles/common/files/fwo-api-calls/request/deleteState.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/deleteState.graphql
rename to roles/common/files/fwo-api-calls/request/deleteState.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/fragments/commentDetails.graphql b/roles/common/files/fwo-api-calls/request/fragments/commentDetails.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/fragments/commentDetails.graphql
rename to roles/common/files/fwo-api-calls/request/fragments/commentDetails.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/fragments/implTaskDetails.graphql b/roles/common/files/fwo-api-calls/request/fragments/implTaskDetails.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/fragments/implTaskDetails.graphql
rename to roles/common/files/fwo-api-calls/request/fragments/implTaskDetails.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/fragments/reqElementDetails.graphql b/roles/common/files/fwo-api-calls/request/fragments/reqElementDetails.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/fragments/reqElementDetails.graphql
rename to roles/common/files/fwo-api-calls/request/fragments/reqElementDetails.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/fragments/reqTaskDetails.graphql b/roles/common/files/fwo-api-calls/request/fragments/reqTaskDetails.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/fragments/reqTaskDetails.graphql
rename to roles/common/files/fwo-api-calls/request/fragments/reqTaskDetails.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/fragments/reqTaskOverview.graphql b/roles/common/files/fwo-api-calls/request/fragments/reqTaskOverview.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/fragments/reqTaskOverview.graphql
rename to roles/common/files/fwo-api-calls/request/fragments/reqTaskOverview.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/fragments/ticketDetails.graphql b/roles/common/files/fwo-api-calls/request/fragments/ticketDetails.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/fragments/ticketDetails.graphql
rename to roles/common/files/fwo-api-calls/request/fragments/ticketDetails.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/fragments/ticketDetailsReqTaskOverview.graphql b/roles/common/files/fwo-api-calls/request/fragments/ticketDetailsReqTaskOverview.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/fragments/ticketDetailsReqTaskOverview.graphql
rename to roles/common/files/fwo-api-calls/request/fragments/ticketDetailsReqTaskOverview.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/fragments/ticketOverview.graphql b/roles/common/files/fwo-api-calls/request/fragments/ticketOverview.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/fragments/ticketOverview.graphql
rename to roles/common/files/fwo-api-calls/request/fragments/ticketOverview.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/getActions.graphql b/roles/common/files/fwo-api-calls/request/getActions.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/getActions.graphql
rename to roles/common/files/fwo-api-calls/request/getActions.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/getExtStates.graphql b/roles/common/files/fwo-api-calls/request/getExtStates.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/getExtStates.graphql
rename to roles/common/files/fwo-api-calls/request/getExtStates.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/getFullTickets.graphql b/roles/common/files/fwo-api-calls/request/getFullTickets.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/getFullTickets.graphql
rename to roles/common/files/fwo-api-calls/request/getFullTickets.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/getStates.graphql b/roles/common/files/fwo-api-calls/request/getStates.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/getStates.graphql
rename to roles/common/files/fwo-api-calls/request/getStates.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/getTicketById.graphql b/roles/common/files/fwo-api-calls/request/getTicketById.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/getTicketById.graphql
rename to roles/common/files/fwo-api-calls/request/getTicketById.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/getTickets.graphql b/roles/common/files/fwo-api-calls/request/getTickets.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/getTickets.graphql
rename to roles/common/files/fwo-api-calls/request/getTickets.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/newAction.graphql b/roles/common/files/fwo-api-calls/request/newAction.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/newAction.graphql
rename to roles/common/files/fwo-api-calls/request/newAction.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/newApproval.graphql b/roles/common/files/fwo-api-calls/request/newApproval.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/newApproval.graphql
rename to roles/common/files/fwo-api-calls/request/newApproval.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/newComment.graphql b/roles/common/files/fwo-api-calls/request/newComment.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/newComment.graphql
rename to roles/common/files/fwo-api-calls/request/newComment.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/newImplementationElement.graphql b/roles/common/files/fwo-api-calls/request/newImplementationElement.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/newImplementationElement.graphql
rename to roles/common/files/fwo-api-calls/request/newImplementationElement.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/newImplementationTask.graphql b/roles/common/files/fwo-api-calls/request/newImplementationTask.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/newImplementationTask.graphql
rename to roles/common/files/fwo-api-calls/request/newImplementationTask.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/newRequestElement.graphql b/roles/common/files/fwo-api-calls/request/newRequestElement.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/newRequestElement.graphql
rename to roles/common/files/fwo-api-calls/request/newRequestElement.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/newRequestTask.graphql b/roles/common/files/fwo-api-calls/request/newRequestTask.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/newRequestTask.graphql
rename to roles/common/files/fwo-api-calls/request/newRequestTask.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/newTicket.graphql b/roles/common/files/fwo-api-calls/request/newTicket.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/newTicket.graphql
rename to roles/common/files/fwo-api-calls/request/newTicket.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/removeExtState.graphql b/roles/common/files/fwo-api-calls/request/removeExtState.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/removeExtState.graphql
rename to roles/common/files/fwo-api-calls/request/removeExtState.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/removeOwnerFromReqTask.graphql b/roles/common/files/fwo-api-calls/request/removeOwnerFromReqTask.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/removeOwnerFromReqTask.graphql
rename to roles/common/files/fwo-api-calls/request/removeOwnerFromReqTask.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/removeStateAction.graphql b/roles/common/files/fwo-api-calls/request/removeStateAction.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/removeStateAction.graphql
rename to roles/common/files/fwo-api-calls/request/removeStateAction.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/subscribeTaskChanges.graphql b/roles/common/files/fwo-api-calls/request/subscribeTaskChanges.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/subscribeTaskChanges.graphql
rename to roles/common/files/fwo-api-calls/request/subscribeTaskChanges.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/subscribeTicketStateChanges.graphql b/roles/common/files/fwo-api-calls/request/subscribeTicketStateChanges.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/subscribeTicketStateChanges.graphql
rename to roles/common/files/fwo-api-calls/request/subscribeTicketStateChanges.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/updateAction.graphql b/roles/common/files/fwo-api-calls/request/updateAction.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/updateAction.graphql
rename to roles/common/files/fwo-api-calls/request/updateAction.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/updateApproval.graphql b/roles/common/files/fwo-api-calls/request/updateApproval.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/updateApproval.graphql
rename to roles/common/files/fwo-api-calls/request/updateApproval.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/updateImplementationElement.graphql b/roles/common/files/fwo-api-calls/request/updateImplementationElement.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/updateImplementationElement.graphql
rename to roles/common/files/fwo-api-calls/request/updateImplementationElement.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/updateImplementationTask.graphql b/roles/common/files/fwo-api-calls/request/updateImplementationTask.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/updateImplementationTask.graphql
rename to roles/common/files/fwo-api-calls/request/updateImplementationTask.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/updateImplementationTaskState.graphql b/roles/common/files/fwo-api-calls/request/updateImplementationTaskState.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/updateImplementationTaskState.graphql
rename to roles/common/files/fwo-api-calls/request/updateImplementationTaskState.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/updateRequestElement.graphql b/roles/common/files/fwo-api-calls/request/updateRequestElement.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/updateRequestElement.graphql
rename to roles/common/files/fwo-api-calls/request/updateRequestElement.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/updateRequestTask.graphql b/roles/common/files/fwo-api-calls/request/updateRequestTask.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/updateRequestTask.graphql
rename to roles/common/files/fwo-api-calls/request/updateRequestTask.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/updateRequestTaskAdditionalInfo.graphql b/roles/common/files/fwo-api-calls/request/updateRequestTaskAdditionalInfo.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/updateRequestTaskAdditionalInfo.graphql
rename to roles/common/files/fwo-api-calls/request/updateRequestTaskAdditionalInfo.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/updateRequestTaskState.graphql b/roles/common/files/fwo-api-calls/request/updateRequestTaskState.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/updateRequestTaskState.graphql
rename to roles/common/files/fwo-api-calls/request/updateRequestTaskState.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/updateTicket.graphql b/roles/common/files/fwo-api-calls/request/updateTicket.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/updateTicket.graphql
rename to roles/common/files/fwo-api-calls/request/updateTicket.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/updateTicketState.graphql b/roles/common/files/fwo-api-calls/request/updateTicketState.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/updateTicketState.graphql
rename to roles/common/files/fwo-api-calls/request/updateTicketState.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/request/upsertState.graphql b/roles/common/files/fwo-api-calls/request/upsertState.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/request/upsertState.graphql
rename to roles/common/files/fwo-api-calls/request/upsertState.graphql
diff --git a/roles/common/files/fwo-api-calls/rule/countActiveRules.graphql b/roles/common/files/fwo-api-calls/rule/countActiveRules.graphql
new file mode 100644
index 0000000000..213d22622d
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/rule/countActiveRules.graphql
@@ -0,0 +1,7 @@
+query countActiveRules {
+ rule_aggregate(where: { removed: { _is_null: true } }) {
+ aggregate {
+ count
+ }
+ }
+}
diff --git a/roles/common/files/fwo-api-calls/rule/countRules.graphql b/roles/common/files/fwo-api-calls/rule/countRules.graphql
new file mode 100644
index 0000000000..3ac8d11351
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/rule/countRules.graphql
@@ -0,0 +1,5 @@
+ query countRules {
+ rule_aggregate {
+ aggregate { count }
+ }
+ }
\ No newline at end of file
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/rule/fragments/natRuleDetails.graphql b/roles/common/files/fwo-api-calls/rule/fragments/natRuleDetails.graphql
similarity index 72%
rename from roles/lib/files/FWO.Api.Client/APIcalls/rule/fragments/natRuleDetails.graphql
rename to roles/common/files/fwo-api-calls/rule/fragments/natRuleDetails.graphql
index 29c7a1a95c..8528583d77 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/rule/fragments/natRuleDetails.graphql
+++ b/roles/common/files/fwo-api-calls/rule/fragments/natRuleDetails.graphql
@@ -11,8 +11,11 @@ fragment natRuleDetails on rule {
rule_svc_refs
rule_svc_neg
rule_froms(where: {
- rf_last_seen: { _gte: $import_id_start }
rf_create: { _lte: $import_id_end }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt: $import_id_start } }
+ ]
}, order_by: { object: { obj_name: asc } }) {
usr {
...userDetails
@@ -22,8 +25,11 @@ fragment natRuleDetails on rule {
}
}
rule_tos(where: {
- rt_last_seen: { _gte: $import_id_start }
rt_create: { _lte: $import_id_end }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt: $import_id_start } }
+ ]
}, order_by: { object: { obj_name: asc } }) {
usr {
...userDetails
@@ -33,8 +39,11 @@ fragment natRuleDetails on rule {
}
}
rule_services(where: {
- rs_last_seen: { _gte: $import_id_start }
rs_create: { _lte: $import_id_end }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt: $import_id_start } }
+ ]
}, order_by: { service: { svc_name: asc } }) {
service {
...networkServiceDetails
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/rule/fragments/natRuleDetailsForReport.graphql b/roles/common/files/fwo-api-calls/rule/fragments/natRuleDetailsForReport.graphql
similarity index 72%
rename from roles/lib/files/FWO.Api.Client/APIcalls/rule/fragments/natRuleDetailsForReport.graphql
rename to roles/common/files/fwo-api-calls/rule/fragments/natRuleDetailsForReport.graphql
index 7c44e31985..bf4c99cdc6 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/rule/fragments/natRuleDetailsForReport.graphql
+++ b/roles/common/files/fwo-api-calls/rule/fragments/natRuleDetailsForReport.graphql
@@ -11,8 +11,11 @@
rule_svc_refs
rule_svc_neg
rule_froms(where: {
- rf_last_seen: { _gte: $import_id_start }
rf_create: { _lte: $import_id_end }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt: $import_id_start } }
+ ]
}, order_by: { object: { obj_name: asc } }) {
usr {
...userDetails
@@ -22,8 +25,11 @@
}
}
rule_tos(where: {
- rt_last_seen: { _gte: $import_id_start }
rt_create: { _lte: $import_id_end }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt: $import_id_start } }
+ ]
}, order_by: { object: { obj_name: asc } }) {
usr {
...userDetails
@@ -33,8 +39,11 @@
}
}
rule_services(where: {
- rs_last_seen: { _gte: $import_id_start }
rs_create: { _lte: $import_id_end }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt: $import_id_start } }
+ ]
}, order_by: { service: { svc_name: asc } }) {
service {
...networkServiceDetails
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/rule/fragments/natRuleOverview.graphql b/roles/common/files/fwo-api-calls/rule/fragments/natRuleOverview.graphql
similarity index 72%
rename from roles/lib/files/FWO.Api.Client/APIcalls/rule/fragments/natRuleOverview.graphql
rename to roles/common/files/fwo-api-calls/rule/fragments/natRuleOverview.graphql
index 4d2616aa06..248d93a1d4 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/rule/fragments/natRuleOverview.graphql
+++ b/roles/common/files/fwo-api-calls/rule/fragments/natRuleOverview.graphql
@@ -11,8 +11,11 @@ fragment natRuleOverview on rule {
rule_svc_refs
rule_svc_neg
rule_froms(where: {
- rf_last_seen: { _gte: $import_id_start }
rf_create: { _lte: $import_id_end }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt: $import_id_start } }
+ ]
}, order_by: { object: { obj_name: asc } }) {
usr {
...userOverview
@@ -22,8 +25,11 @@ fragment natRuleOverview on rule {
}
}
rule_tos(where: {
- rt_last_seen: { _gte: $import_id_start }
rt_create: { _lte: $import_id_end }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt: $import_id_start } }
+ ]
}, order_by: { object: { obj_name: asc } }) {
usr {
...userOverview
@@ -33,8 +39,11 @@ fragment natRuleOverview on rule {
}
}
rule_services(where: {
- rs_last_seen: { _gte: $import_id_start }
rs_create: { _lte: $import_id_end }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt: $import_id_start } }
+ ]
}, order_by: { service: { svc_name: asc } }) {
service {
...networkServiceOverview
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/rule/fragments/ruleDetails.graphql b/roles/common/files/fwo-api-calls/rule/fragments/ruleDetails.graphql
similarity index 66%
rename from roles/lib/files/FWO.Api.Client/APIcalls/rule/fragments/ruleDetails.graphql
rename to roles/common/files/fwo-api-calls/rule/fragments/ruleDetails.graphql
index d4b4df8692..26c5cb1362 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/rule/fragments/ruleDetails.graphql
+++ b/roles/common/files/fwo-api-calls/rule/fragments/ruleDetails.graphql
@@ -4,14 +4,17 @@ fragment ruleDetails on rule {
mgm_id
dev_id
rule_action
+ rulebase_id
section_header: rule_head_text
rule_comment
rule_track
rule_disabled
- src_zone: zone {
- zone_name
- zone_id
- }
+ rule_from_zones {
+ zone {
+ zone_name
+ zone_id
+ }
+ }
active
rule_create
rule_last_seen
@@ -20,13 +23,18 @@ fragment ruleDetails on rule {
rule_svc_neg
rule_num_numeric
rule_name
+ rule_installon
+ rule_custom_fields
access_rule
nat_rule
xlate_rule
rule_froms(where: {
active: { _eq: $active }
- rf_last_seen: { _gte: $import_id_start }
rf_create: { _lte: $import_id_end }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt: $import_id_start } }
+ ]
}, order_by:{ object: { obj_name: asc } }) {
usr {
...userDetails
@@ -35,14 +43,19 @@ fragment ruleDetails on rule {
...networkObjectDetails
}
}
- dst_zone: zoneByRuleToZone {
- zone_name
- zone_id
+ rule_to_zones {
+ zone {
+ zone_name
+ zone_id
+ }
}
rule_tos(where: {
active: { _eq: $active }
- rt_last_seen: { _gte: $import_id_start }
rt_create: { _lte: $import_id_end }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt: $import_id_start } }
+ ]
}, order_by:{ object: { obj_name: asc } }) {
usr {
...userDetails
@@ -54,8 +67,11 @@ fragment ruleDetails on rule {
rule_svc
rule_services(where: {
active: { _eq: $active }
- rs_last_seen: { _gte: $import_id_start }
rs_create: { _lte: $import_id_end }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt: $import_id_start } }
+ ]
}, order_by:{ service: { svc_name: asc } }) {
service {
...networkServiceDetails
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/rule/fragments/ruleDetailsChangesNew.graphql b/roles/common/files/fwo-api-calls/rule/fragments/ruleDetailsChangesNew.graphql
similarity index 74%
rename from roles/lib/files/FWO.Api.Client/APIcalls/rule/fragments/ruleDetailsChangesNew.graphql
rename to roles/common/files/fwo-api-calls/rule/fragments/ruleDetailsChangesNew.graphql
index 72f60ce36d..56ffcb18a8 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/rule/fragments/ruleDetailsChangesNew.graphql
+++ b/roles/common/files/fwo-api-calls/rule/fragments/ruleDetailsChangesNew.graphql
@@ -7,10 +7,12 @@
rule_comment
rule_track
rule_disabled
- src_zone: zone {
- zone_name
- zone_id
- }
+ rule_from_zones {
+ zone {
+ zone_name
+ zone_id
+ }
+ }
rule_metadatum {
rule_metadata_id
rule_created
@@ -36,8 +38,11 @@
xlate_rule
rule_froms(where: {
active: { _eq: $active }
- rf_last_seen: { _gte: $import_id_new }
rf_create: { _lte: $import_id_new }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt:$import_id_start } }
+ ]
}, order_by: { object: { obj_name: asc } }) {
usr {
...userDetailsChangesNew
@@ -46,14 +51,19 @@
...networkObjectDetailsChangesNew
}
}
- dst_zone: zoneByRuleToZone {
- zone_name
- zone_id
+ rule_to_zones {
+ zone {
+ zone_name
+ zone_id
+ }
}
rule_tos(where: {
active: { _eq: $active }
- rt_last_seen: { _gte: $import_id_new }
rt_create: { _lte: $import_id_new }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt:$import_id_start } }
+ ]
}, order_by: { object: { obj_name: asc } }) {
usr {
...userDetailsChangesNew
@@ -64,8 +74,11 @@
}
rule_services(where: {
active: { _eq: $active }
- rs_last_seen: { _gte: $import_id_new }
rs_create: { _lte: $import_id_new }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt:$import_id_start } }
+ ]
}, order_by: { service: { svc_name: asc } }) {
service {
...networkServiceDetailsChangesNew
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/rule/fragments/ruleDetailsChangesOld.graphql b/roles/common/files/fwo-api-calls/rule/fragments/ruleDetailsChangesOld.graphql
similarity index 74%
rename from roles/lib/files/FWO.Api.Client/APIcalls/rule/fragments/ruleDetailsChangesOld.graphql
rename to roles/common/files/fwo-api-calls/rule/fragments/ruleDetailsChangesOld.graphql
index 805265b48e..3f9f72418b 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/rule/fragments/ruleDetailsChangesOld.graphql
+++ b/roles/common/files/fwo-api-calls/rule/fragments/ruleDetailsChangesOld.graphql
@@ -7,10 +7,12 @@
rule_comment
rule_track
rule_disabled
- src_zone: zone {
- zone_name
- zone_id
- }
+ rule_from_zones {
+ zone {
+ zone_name
+ zone_id
+ }
+ }
rule_metadatum {
rule_metadata_id
rule_created
@@ -36,8 +38,11 @@
xlate_rule
rule_froms(where: {
active: { _eq: $active }
- rf_last_seen: { _gte: $import_id_old }
rf_create: { _lte: $import_id_old }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt:$import_id_start } }
+ ]
}, order_by: { object: { obj_name: asc } }) {
usr {
...userDetailsChangesOld
@@ -46,14 +51,19 @@
...networkObjectDetailsChangesOld
}
}
- dst_zone: zoneByRuleToZone {
- zone_name
- zone_id
+ rule_to_zones {
+ zone {
+ zone_name
+ zone_id
+ }
}
rule_tos(where: {
active: { _eq: $active }
- rt_last_seen: { _gte: $import_id_old }
rt_create: { _lte: $import_id_old }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt:$import_id_start } }
+ ]
}, order_by: { object: { obj_name: asc } }) {
usr {
...userDetailsChangesOld
@@ -64,8 +74,11 @@
}
rule_services(where: {
active: { _eq: $active }
- rs_last_seen: { _gte: $import_id_old }
rs_create: { _lte: $import_id_old }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt:$import_id_start } }
+ ]
}, order_by: { service: { svc_name: asc } }) {
service {
...networkServiceDetailsChangesOld
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/rule/fragments/ruleDetailsForReport.graphql b/roles/common/files/fwo-api-calls/rule/fragments/ruleDetailsForReport.graphql
similarity index 64%
rename from roles/lib/files/FWO.Api.Client/APIcalls/rule/fragments/ruleDetailsForReport.graphql
rename to roles/common/files/fwo-api-calls/rule/fragments/ruleDetailsForReport.graphql
index 2b82197632..206e2ed58c 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/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_from_zones {
+ zone {
+ zone_name
+ zone_id
+ }
+ }
rule_metadatum {
rule_metadata_id
rule_created
@@ -32,12 +34,17 @@
rule_svc_neg
rule_num_numeric
rule_name
+ rule_installon
+ rule_custom_fields
access_rule
nat_rule
xlate_rule
rule_froms(where: {
- rf_last_seen: { _gte: $import_id_start }
rf_create: { _lte: $import_id_end }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt: $import_id_start } }
+ ]
}, order_by: { object: { obj_name: asc } }) {
usr {
...userDetails
@@ -46,13 +53,18 @@
...networkObjectDetails
}
}
- dst_zone: zoneByRuleToZone {
- zone_name
- zone_id
+ rule_to_zones {
+ zone {
+ zone_name
+ zone_id
+ }
}
rule_tos(where: {
- rt_last_seen: { _gte: $import_id_start }
rt_create: { _lte: $import_id_end }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt: $import_id_start } }
+ ]
}, order_by: { object: { obj_name: asc } }) {
usr {
...userDetails
@@ -62,11 +74,20 @@
}
}
rule_services(where: {
- rs_last_seen: { _gte: $import_id_start }
rs_create: { _lte: $import_id_end }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt: $import_id_start } }
+ ]
}, order_by: { service: { svc_name: asc } }) {
service {
...networkServiceDetails
}
}
+ rule_enforced_on_gateways(order_by:{ device: { dev_name: asc } }) {
+ device {
+ id: dev_id
+ name: dev_name
+ }
+ }
}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/rule/fragments/ruleOverview.graphql b/roles/common/files/fwo-api-calls/rule/fragments/ruleOverview.graphql
similarity index 62%
rename from roles/lib/files/FWO.Api.Client/APIcalls/rule/fragments/ruleOverview.graphql
rename to roles/common/files/fwo-api-calls/rule/fragments/ruleOverview.graphql
index ed2822d9a3..08d3e9b5fc 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/rule/fragments/ruleOverview.graphql
+++ b/roles/common/files/fwo-api-calls/rule/fragments/ruleOverview.graphql
@@ -3,14 +3,17 @@ fragment ruleOverview on rule {
rule_uid
dev_id
rule_action
+ rulebase_id
section_header: rule_head_text
rule_comment
rule_track
rule_disabled
- src_zone: zone {
- zone_name
- zone_id
- }
+ rule_from_zones {
+ zone {
+ zone_name
+ zone_id
+ }
+ }
rule_metadatum {
rule_metadata_id
rule_created
@@ -26,14 +29,23 @@ fragment ruleOverview on rule {
rule_src_neg
rule_dst_neg
rule_svc_neg
+ rule_src
+ rule_dst
+ rule_svc
rule_num_numeric
+ rule_num
rule_name
+ rule_custom_fields
+ rule_installon
access_rule
nat_rule
xlate_rule
rule_froms(where: {
- rf_last_seen: { _gte: $import_id_start }
rf_create: { _lte: $import_id_end }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt: $import_id_start } }
+ ]
}, order_by:{ object: { obj_name: asc } }) {
usr {
...userOverview
@@ -42,13 +54,18 @@ fragment ruleOverview on rule {
...networkObjectOverview
}
}
- dst_zone: zoneByRuleToZone {
- zone_name
- zone_id
+ rule_to_zones {
+ zone {
+ zone_name
+ zone_id
+ }
}
rule_tos(where: {
- rt_last_seen: { _gte: $import_id_start }
rt_create: { _lte: $import_id_end }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt: $import_id_start } }
+ ]
}, order_by:{ object: { obj_name: asc } }) {
usr {
...userOverview
@@ -58,11 +75,20 @@ fragment ruleOverview on rule {
}
}
rule_services(where: {
- rs_last_seen: { _gte: $import_id_start }
rs_create: { _lte: $import_id_end }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt: $import_id_start } }
+ ]
}, order_by:{ service: { svc_name: asc } }) {
service {
...networkServiceOverview
}
}
+ rule_enforced_on_gateways(order_by:{ device: { dev_name: asc } }) {
+ device {
+ id: dev_id
+ name: dev_name
+ }
+ }
}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/rule/fragments/ruleOverviewChangesNew.graphql b/roles/common/files/fwo-api-calls/rule/fragments/ruleOverviewChangesNew.graphql
similarity index 73%
rename from roles/lib/files/FWO.Api.Client/APIcalls/rule/fragments/ruleOverviewChangesNew.graphql
rename to roles/common/files/fwo-api-calls/rule/fragments/ruleOverviewChangesNew.graphql
index dce42a691a..0e6c7f10da 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/rule/fragments/ruleOverviewChangesNew.graphql
+++ b/roles/common/files/fwo-api-calls/rule/fragments/ruleOverviewChangesNew.graphql
@@ -7,10 +7,12 @@ fragment ruleOverviewChangesNew on rule {
rule_comment
rule_track
rule_disabled
- src_zone: zone {
- zone_name
- zone_id
- }
+ rule_from_zones {
+ zone {
+ zone_name
+ zone_id
+ }
+ }
rule_metadatum {
rule_metadata_id
rule_created
@@ -33,8 +35,11 @@ fragment ruleOverviewChangesNew on rule {
xlate_rule
rule_froms(where: {
active: { _eq: $active }
- rf_last_seen: { _gte: $import_id_new }
rf_create: { _lte: $import_id_new }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt: $import_id_new } }
+ ]
}, order_by: { object: { obj_name: asc } }) {
usr {
...userOverview
@@ -43,14 +48,19 @@ fragment ruleOverviewChangesNew on rule {
...networkObjectOverview
}
}
- dst_zone: zoneByRuleToZone {
- zone_name
- zone_id
+ rule_to_zones {
+ zone {
+ zone_name
+ zone_id
+ }
}
rule_tos(where: {
active: { _eq: $active }
- rt_last_seen: { _gte: $import_id_new }
rt_create: { _lte: $import_id_new }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt: $import_id_new } }
+ ]
}, order_by: { object: { obj_name: asc } }) {
usr {
...userOverview
@@ -61,8 +71,11 @@ fragment ruleOverviewChangesNew on rule {
}
rule_services(where: {
active: { _eq: $active }
- rs_last_seen: { _gte: $import_id_new }
rs_create: { _lte: $import_id_new }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt: $import_id_new } }
+ ]
}, order_by: { service: { svc_name: asc } }) {
service {
...networkServiceOverview
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/rule/fragments/ruleOverviewChangesOld.graphql b/roles/common/files/fwo-api-calls/rule/fragments/ruleOverviewChangesOld.graphql
similarity index 73%
rename from roles/lib/files/FWO.Api.Client/APIcalls/rule/fragments/ruleOverviewChangesOld.graphql
rename to roles/common/files/fwo-api-calls/rule/fragments/ruleOverviewChangesOld.graphql
index 058bb26be7..4a19c3b1e4 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/rule/fragments/ruleOverviewChangesOld.graphql
+++ b/roles/common/files/fwo-api-calls/rule/fragments/ruleOverviewChangesOld.graphql
@@ -7,10 +7,12 @@ fragment ruleOverviewChangesOld on rule {
rule_comment
rule_track
rule_disabled
- src_zone: zone {
- zone_name
- zone_id
- }
+ rule_from_zones {
+ zone {
+ zone_name
+ zone_id
+ }
+ }
rule_metadatum {
rule_metadata_id
rule_created
@@ -33,8 +35,11 @@ fragment ruleOverviewChangesOld on rule {
xlate_rule
rule_froms(where: {
active: { _eq: $active }
- rf_last_seen: { _gte: $import_id_old }
rf_create: { _lte: $import_id_old }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt: $import_id_old } }
+ ]
}, order_by: { object: { obj_name: asc } }) {
usr {
...userOverview
@@ -43,14 +48,19 @@ fragment ruleOverviewChangesOld on rule {
...networkObjectOverview
}
}
- dst_zone: zoneByRuleToZone {
- zone_name
- zone_id
+ rule_to_zones {
+ zone {
+ zone_name
+ zone_id
+ }
}
rule_tos(where: {
active: { _eq: $active }
- rt_last_seen: { _gte: $import_id_old }
rt_create: { _lte: $import_id_old }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt:$import_id_old } }
+ ]
}, order_by: { object: { obj_name: asc } }) {
usr {
...userOverview
@@ -61,8 +71,11 @@ fragment ruleOverviewChangesOld on rule {
}
rule_services(where: {
active: { _eq: $active }
- rs_last_seen: { _gte: $import_id_old }
rs_create: { _lte: $import_id_old }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt:$import_id_old } }
+ ]
}, order_by: { service: { svc_name: asc } }) {
service {
...networkServiceOverview
diff --git a/roles/common/files/fwo-api-calls/rule/fragments/rulebaseOverview.graphql b/roles/common/files/fwo-api-calls/rule/fragments/rulebaseOverview.graphql
new file mode 100644
index 0000000000..931f395e2f
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/rule/fragments/rulebaseOverview.graphql
@@ -0,0 +1,5 @@
+fragment rulebaseOverview on rulebase {
+ id
+ uid
+ name
+}
diff --git a/roles/common/files/fwo-api-calls/rule/getMapOfUid2Id.graphql b/roles/common/files/fwo-api-calls/rule/getMapOfUid2Id.graphql
new file mode 100644
index 0000000000..63ebaeb9e8
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/rule/getMapOfUid2Id.graphql
@@ -0,0 +1,13 @@
+query getMapOfUid2Id($uids: [String!], $mgmId: Int!) {
+ rule(where: {
+ rule_uid: {_in: $uids},
+ _or: [
+ {mgm_id: {_eq: $mgmId}},
+ {management: {multi_device_manager_id: {_eq: $mgmId}}}
+ ],
+ removed: {_is_null: true}
+ }) {
+ rule_id
+ rule_uid
+ }
+}
diff --git a/roles/common/files/fwo-api-calls/rule/getNatRuleDetails.graphql b/roles/common/files/fwo-api-calls/rule/getNatRuleDetails.graphql
new file mode 100644
index 0000000000..58bc784892
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/rule/getNatRuleDetails.graphql
@@ -0,0 +1,25 @@
+query getNatRuleDetails(
+ $rule_id: [bigint!]
+ $rule_uid: [String!]
+ $ruleSrcName: [String!]
+ $ruleSrcIp: [cidr!]
+ $limit: Int
+ $offset: Int
+) {
+ rule(
+ limit: $limit
+ offset: $offset
+ where: {
+ rule_id: { _in: $rule_id }
+ rule_uid: { _in: $rule_uid }
+ active: { _eq: true }
+ rule_src: { _in: $ruleSrcName }
+ rule_froms: { object: { obj_ip: { _in: $ruleSrcIp } } }
+ nat_rule: { _eq: true }
+ }
+ order_by: { rule_num_numeric: asc }
+ ) {
+ mgm_id: mgm_id
+ ...natRuleDetails
+ }
+}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/rule/getNatRuleOverview.graphql b/roles/common/files/fwo-api-calls/rule/getNatRuleOverview.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/rule/getNatRuleOverview.graphql
rename to roles/common/files/fwo-api-calls/rule/getNatRuleOverview.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/rule/getRuleByUid.graphql b/roles/common/files/fwo-api-calls/rule/getRuleByUid.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/rule/getRuleByUid.graphql
rename to roles/common/files/fwo-api-calls/rule/getRuleByUid.graphql
diff --git a/roles/common/files/fwo-api-calls/rule/getRuleDetails.graphql b/roles/common/files/fwo-api-calls/rule/getRuleDetails.graphql
new file mode 100644
index 0000000000..4c51d53c90
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/rule/getRuleDetails.graphql
@@ -0,0 +1,33 @@
+query getRuleDetails(
+ $rule_id: [bigint!]
+ $rule_uid: [String!]
+ $ruleSrcName: [String!]
+ $ruleSrcIp: [cidr!]
+ $active: Boolean
+ $import_id: bigint
+ $import_id_start: bigint
+ $import_id_end: bigint
+ $limit: Int
+ $offset: Int
+) {
+ rule(
+ limit: $limit
+ offset: $offset
+ where: {
+ rule_id: { _in: $rule_id }
+ rule_uid: { _in: $rule_uid }
+ rule_create: { _lte: $import_id }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt: $import_id_start } }
+ ]
+ rule_src: { _in: $ruleSrcName }
+ rule_froms: { object: { obj_ip: { _in: $ruleSrcIp } } }
+ access_rule: { _eq: true }
+ }
+ order_by: { rule_num_numeric: asc }
+ ) {
+ mgm_id: mgm_id
+ ...ruleDetails
+ }
+}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/rule/getRuleOverview.graphql b/roles/common/files/fwo-api-calls/rule/getRuleOverview.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/rule/getRuleOverview.graphql
rename to roles/common/files/fwo-api-calls/rule/getRuleOverview.graphql
diff --git a/roles/common/files/fwo-api-calls/rule/getRulebaseLinks.graphql b/roles/common/files/fwo-api-calls/rule/getRulebaseLinks.graphql
new file mode 100644
index 0000000000..a105a08fd8
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/rule/getRulebaseLinks.graphql
@@ -0,0 +1,15 @@
+query getRulebaseLinks($gwIds: [Int!]) {
+ rulebase_link (where: {removed:{_is_null:true}, gw_id: {_in: $gwIds } }) {
+ id
+ gw_id
+ from_rule_id
+ from_rulebase_id
+ to_rulebase_id
+ link_type
+ is_initial
+ is_global
+ is_section
+ created
+ removed
+ }
+}
diff --git a/roles/common/files/fwo-api-calls/rule/getRulesByIdWithRefUids.graphql b/roles/common/files/fwo-api-calls/rule/getRulesByIdWithRefUids.graphql
new file mode 100644
index 0000000000..b8de9a0d89
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/rule/getRulesByIdWithRefUids.graphql
@@ -0,0 +1,15 @@
+query getRulesByIdWithRefUids ($ruleIds:[bigint!]!) {
+ rule (where:{rule_id:{_in:$ruleIds}}) {
+ rule_id
+ rule_src_refs
+ rule_dst_refs
+ rule_svc_refs
+ rule_to_zone
+ rule_from_zone
+ rule_src_neg
+ rule_dst_neg
+ rule_svc_neg
+ rulebase_id
+ rule_installon
+ }
+}
diff --git a/roles/common/files/fwo-api-calls/rule/getRulesForSelectedManagements.graphql b/roles/common/files/fwo-api-calls/rule/getRulesForSelectedManagements.graphql
new file mode 100644
index 0000000000..9c876441ad
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/rule/getRulesForSelectedManagements.graphql
@@ -0,0 +1,38 @@
+query getRulesForSelectedManagementsByChunk(
+ $limit: Int
+ $offset: Int
+ $active: Boolean
+ $import_id_start: bigint
+ $import_id_end: bigint
+ $from_date: timestamptz
+ $to_date: timestamptz
+ $mgm_ids: [Int!]
+) {
+ rule(
+ limit: $limit
+ offset: $offset
+ order_by: { rule_uid: asc }
+ where: { removed: { _is_null: true } mgm_id: { _in: $mgm_ids }}
+ ){
+ rule_src
+ rule_dst
+ ...ruleDetails
+ violations: compliance_violations_version_agnostic( where: { removed_date: { _is_null: true } }) {
+ id
+ rule_id
+ rule_uid
+ mgmt_uid
+ found_date
+ removed_date
+ details
+ risk_score
+ policy_id
+ criterion_id
+ criterion: criterion{
+ id
+ name
+ criterion_type
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/roles/common/files/fwo-api-calls/rule/getRulesWithCurrentViolationsByChunk.graphql b/roles/common/files/fwo-api-calls/rule/getRulesWithCurrentViolationsByChunk.graphql
new file mode 100644
index 0000000000..64c2a2b941
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/rule/getRulesWithCurrentViolationsByChunk.graphql
@@ -0,0 +1,28 @@
+query getRulesWithCurrentViolationsByChunk($limit: Int, $offset: Int, $active: Boolean, $import_id_start: bigint, $import_id_end: bigint, $mgm_ids: [Int!]) {
+ rule(limit: $limit, offset: $offset, order_by: { rule_uid: asc }, where: { removed: { _is_null: true } mgm_id: { _in: $mgm_ids }}) {
+ rule_src
+ rule_dst
+ ...ruleDetails
+ violations: compliance_violations_version_agnostic( where: { removed_date: { _is_null: true } }) {
+ id
+ rule_id
+ rule_uid
+ mgmt_uid
+ found_date
+ removed_date
+ details
+ risk_score
+ policy_id
+ criterion_id
+ criterion: criterion{
+ id
+ name
+ criterion_type
+ }
+ }
+ rulebase: rulebase {
+ id
+ name
+ }
+ }
+}
\ No newline at end of file
diff --git a/roles/common/files/fwo-api-calls/rule/getRulesWithViolationsInTimespanByChunk.graphql b/roles/common/files/fwo-api-calls/rule/getRulesWithViolationsInTimespanByChunk.graphql
new file mode 100644
index 0000000000..6624f1db1a
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/rule/getRulesWithViolationsInTimespanByChunk.graphql
@@ -0,0 +1,46 @@
+query getRulesWithViolationsInTimespanByChunk(
+ $limit: Int
+ $offset: Int
+ $active: Boolean
+ $import_id_start: bigint
+ $import_id_end: bigint
+ $from_date: timestamptz
+ $to_date: timestamptz
+ $mgm_ids: [Int!]
+) {
+ rule(
+ limit: $limit
+ offset: $offset
+ order_by: { rule_uid: asc }
+ where: { removed: { _is_null: true } mgm_id: { _in: $mgm_ids }}
+ ) {
+ rule_src
+ rule_dst
+ ...ruleDetails
+
+ violations: compliance_violations_version_agnostic(
+ where: { found_date: { _gte: $from_date, _lt: $to_date } }
+ ) {
+ id
+ rule_id
+ rule_uid
+ mgmt_uid
+ found_date
+ removed_date
+ details
+ risk_score
+ policy_id
+ criterion_id
+ criterion: criterion{
+ id
+ name
+ criterion_type
+ }
+ }
+
+ rulebase: rulebase {
+ id
+ name
+ }
+ }
+}
\ No newline at end of file
diff --git a/roles/common/files/fwo-api-calls/rule/insertRuleRefs.graphql b/roles/common/files/fwo-api-calls/rule/insertRuleRefs.graphql
new file mode 100644
index 0000000000..85cb22b2b3
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/rule/insertRuleRefs.graphql
@@ -0,0 +1,35 @@
+mutation insertRuleRefs(
+ $ruleFroms: [rule_from_insert_input!]!
+ $ruleTos: [rule_to_insert_input!]!
+ $ruleServices: [rule_service_insert_input!]!
+ $ruleNwObjResolveds: [rule_nwobj_resolved_insert_input!]!
+ $ruleSvcResolveds: [rule_svc_resolved_insert_input!]!
+ $ruleUserResolveds: [rule_user_resolved_insert_input!]!
+ $ruleFromZones: [rule_from_zone_insert_input!]!
+ $ruleToZones: [rule_to_zone_insert_input!]!
+) {
+ insert_rule_from(objects: $ruleFroms) {
+ affected_rows
+ }
+ insert_rule_to(objects: $ruleTos) {
+ affected_rows
+ }
+ insert_rule_service(objects: $ruleServices) {
+ affected_rows
+ }
+ insert_rule_nwobj_resolved(objects: $ruleNwObjResolveds) {
+ affected_rows
+ }
+ insert_rule_svc_resolved(objects: $ruleSvcResolveds) {
+ affected_rows
+ }
+ insert_rule_user_resolved(objects: $ruleUserResolveds) {
+ affected_rows
+ }
+ insert_rule_from_zone(objects: $ruleFromZones) {
+ affected_rows
+ }
+ insert_rule_to_zone(objects: $ruleToZones) {
+ affected_rows
+ }
+}
\ No newline at end of file
diff --git a/roles/common/files/fwo-api-calls/rule/insertRulebaseLinks.graphql b/roles/common/files/fwo-api-calls/rule/insertRulebaseLinks.graphql
new file mode 100644
index 0000000000..ce3e6d60ee
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/rule/insertRulebaseLinks.graphql
@@ -0,0 +1,11 @@
+mutation importInsertRulebaseOnGateway($rulebaseLinks: [rulebase_link_insert_input!]!) {
+ insert_rulebase_link(
+ objects: $rulebaseLinks
+ on_conflict: {
+ constraint: unique_rulebase_link
+ update_columns: []
+ }
+ ) {
+ affected_rows
+ }
+}
\ No newline at end of file
diff --git a/roles/common/files/fwo-api-calls/rule/removeRulebaseLinks.graphql b/roles/common/files/fwo-api-calls/rule/removeRulebaseLinks.graphql
new file mode 100644
index 0000000000..d26039a1f6
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/rule/removeRulebaseLinks.graphql
@@ -0,0 +1,18 @@
+mutation removeRulebaseLinks(
+ $importId: bigint!
+ $removedRulebaseLinks: [Int!]!
+) {
+ update_rulebase_link(
+ where: {
+ id: { _in: $removedRulebaseLinks }
+ removed: { _is_null: true }
+ }
+ _set: { removed: $importId }
+ ) {
+ affected_rows
+ returning {
+ gw_id
+ id
+ }
+ }
+}
\ No newline at end of file
diff --git a/roles/common/files/fwo-api-calls/rule/updateChanglogRules.graphql b/roles/common/files/fwo-api-calls/rule/updateChanglogRules.graphql
new file mode 100644
index 0000000000..23932a596a
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/rule/updateChanglogRules.graphql
@@ -0,0 +1,5 @@
+mutation updateChanglogRules($rule_changes: [changelog_rule_insert_input!]!) {
+ insert_changelog_rule(objects: $rule_changes) {
+ 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
new file mode 100644
index 0000000000..5c8664105b
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/rule/updateRuleRefs.graphql
@@ -0,0 +1,79 @@
+mutation updateRuleRefs(
+ $importId: bigint!
+ $ruleFroms: [rule_from_bool_exp!]!
+ $ruleTos: [rule_to_bool_exp!]!
+ $ruleServices: [rule_service_bool_exp!]!
+ $ruleNwObjResolveds: [rule_nwobj_resolved_bool_exp!]
+ $ruleSvcResolveds: [rule_svc_resolved_bool_exp!]!
+ $ruleUserResolveds: [rule_user_resolved_bool_exp!]!
+ $ruleFromZones: [rule_from_zone_bool_exp!]
+ $ruleToZones: [rule_to_zone_bool_exp!]
+) {
+ update_rule_from(where: {
+ _or: $ruleFroms
+ removed: {_is_null: true}
+ }, _set: {
+ removed: $importId
+ active: false
+ }) {
+ affected_rows
+ }
+ update_rule_to(where: {
+ _or: $ruleTos
+ removed: {_is_null: true}
+ }, _set: {
+ removed: $importId
+ active: false
+ }) {
+ affected_rows
+ }
+ update_rule_service(where: {
+ _or: $ruleServices
+ removed: {_is_null: true}
+ }, _set: {
+ removed: $importId
+ active: false
+ }) {
+ affected_rows
+ }
+ update_rule_nwobj_resolved(where: {
+ _or: $ruleNwObjResolveds
+ removed: {_is_null: true}
+ }, _set: {
+ removed: $importId
+ }) {
+ affected_rows
+ }
+ update_rule_svc_resolved(where: {
+ _or: $ruleSvcResolveds
+ removed: {_is_null: true}
+ }, _set: {
+ removed: $importId
+ }) {
+ affected_rows
+ }
+ update_rule_user_resolved(where: {
+ _or: $ruleUserResolveds
+ removed: {_is_null: true}
+ }, _set: {
+ removed: $importId
+ }) {
+ affected_rows
+ }
+ update_rule_from_zone(where: {
+ _or: $ruleFromZones
+ removed: {_is_null: true}
+ }, _set: {
+ removed: $importId
+ }) {
+ affected_rows
+ }
+ update_rule_to_zone(where: {
+ _or: $ruleToZones
+ removed: {_is_null: true}
+ }, _set: {
+ removed: $importId
+ }) {
+ affected_rows
+ }
+}
\ No newline at end of file
diff --git a/roles/common/files/fwo-api-calls/rule_metadata/updateLastHits.graphql b/roles/common/files/fwo-api-calls/rule_metadata/updateLastHits.graphql
new file mode 100644
index 0000000000..6b788c7847
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/rule_metadata/updateLastHits.graphql
@@ -0,0 +1,6 @@
+mutation updateLasthits($hit_info: [rule_metadata_updates!]!) {
+ update_rule_metadata_many(updates: $hit_info) {
+ affected_rows
+ returning { rule_uid }
+ }
+}
diff --git a/roles/common/files/fwo-api-calls/stmTables/getColors.graphql b/roles/common/files/fwo-api-calls/stmTables/getColors.graphql
new file mode 100644
index 0000000000..567c03bd5d
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/stmTables/getColors.graphql
@@ -0,0 +1,7 @@
+
+query getColors {
+ stm_color {
+ color_id
+ color_name
+ }
+}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/stmTables/getIpProtocols.graphql b/roles/common/files/fwo-api-calls/stmTables/getIpProtocols.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/stmTables/getIpProtocols.graphql
rename to roles/common/files/fwo-api-calls/stmTables/getIpProtocols.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/stmTables/getRuleActions.graphql b/roles/common/files/fwo-api-calls/stmTables/getRuleActions.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/stmTables/getRuleActions.graphql
rename to roles/common/files/fwo-api-calls/stmTables/getRuleActions.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/stmTables/getTracking.graphql b/roles/common/files/fwo-api-calls/stmTables/getTracking.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/stmTables/getTracking.graphql
rename to roles/common/files/fwo-api-calls/stmTables/getTracking.graphql
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/user/fragments/userDetails.graphql b/roles/common/files/fwo-api-calls/user/fragments/userDetails.graphql
similarity index 89%
rename from roles/lib/files/FWO.Api.Client/APIcalls/user/fragments/userDetails.graphql
rename to roles/common/files/fwo-api-calls/user/fragments/userDetails.graphql
index d61d08f140..06f83fb9d9 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/user/fragments/userDetails.graphql
+++ b/roles/common/files/fwo-api-calls/user/fragments/userDetails.graphql
@@ -28,8 +28,11 @@ fragment userDetails on usr {
}
usergrp_flats(where: {
active: { _eq: $active }
- import_last_seen: { _gte: $import_id_start }
import_created: { _lte: $import_id_end }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt:$import_id_start } }
+ ]
} order_by: { usergrp_flat_member_id: asc }) {
flat_id: usergrp_flat_id
byFlatId: usrByUsergrpFlatMemberId {
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/user/fragments/userDetailsChangesNew.graphql b/roles/common/files/fwo-api-calls/user/fragments/userDetailsChangesNew.graphql
similarity index 69%
rename from roles/lib/files/FWO.Api.Client/APIcalls/user/fragments/userDetailsChangesNew.graphql
rename to roles/common/files/fwo-api-calls/user/fragments/userDetailsChangesNew.graphql
index f17785194b..5897fe1704 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/user/fragments/userDetailsChangesNew.graphql
+++ b/roles/common/files/fwo-api-calls/user/fragments/userDetailsChangesNew.graphql
@@ -16,7 +16,14 @@ fragment userDetailsChangesNew on usr {
}
user_member_names
user_member_refs
- usergrps(order_by: { usergrp_member_id: asc }) {
+ usergrps(where: {
+ active: { _eq: $active }
+ import_created: { _lte: $import_id_new }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt: $import_id_new } }
+ ]
+ } order_by: { usergrp_member_id: asc }) {
id: usergrp_id
byId: usrByUsergrpMemberId {
user_id
@@ -28,8 +35,11 @@ fragment userDetailsChangesNew on usr {
}
usergrp_flats(where: {
active: { _eq: $active }
- import_last_seen: { _gte: $import_id_new }
import_created: { _lte: $import_id_new }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt: $import_id_new } }
+ ]
} order_by: { usergrp_flat_member_id: asc }) {
flat_id: usergrp_flat_id
byFlatId: usrByUsergrpFlatMemberId {
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/user/fragments/userDetailsChangesOld.graphql b/roles/common/files/fwo-api-calls/user/fragments/userDetailsChangesOld.graphql
similarity index 69%
rename from roles/lib/files/FWO.Api.Client/APIcalls/user/fragments/userDetailsChangesOld.graphql
rename to roles/common/files/fwo-api-calls/user/fragments/userDetailsChangesOld.graphql
index 2e7e4c34d7..3cbac357cd 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/user/fragments/userDetailsChangesOld.graphql
+++ b/roles/common/files/fwo-api-calls/user/fragments/userDetailsChangesOld.graphql
@@ -16,7 +16,14 @@ fragment userDetailsChangesOld on usr {
}
user_member_names
user_member_refs
- usergrps(order_by: { usergrp_member_id: asc }) {
+ usergrps(where: {
+ active: { _eq: $active }
+ import_created: { _lte: $import_id_old }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt: $import_id_old } }
+ ]
+ } order_by: { usergrp_member_id: asc }) {
id: usergrp_id
byId: usrByUsergrpMemberId {
user_id
@@ -28,8 +35,11 @@ fragment userDetailsChangesOld on usr {
}
usergrp_flats(where: {
active: { _eq: $active }
- import_last_seen: { _gte: $import_id_old }
import_created: { _lte: $import_id_old }
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gt: $import_id_old } }
+ ]
} order_by: { usergrp_flat_member_id: asc }) {
flat_id: usergrp_flat_id
byFlatId: usrByUsergrpFlatMemberId {
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/user/fragments/userOverview.graphql b/roles/common/files/fwo-api-calls/user/fragments/userOverview.graphql
similarity index 100%
rename from roles/lib/files/FWO.Api.Client/APIcalls/user/fragments/userOverview.graphql
rename to roles/common/files/fwo-api-calls/user/fragments/userOverview.graphql
diff --git a/roles/common/files/fwo-api-calls/user/getMapOfUid2Id.graphql b/roles/common/files/fwo-api-calls/user/getMapOfUid2Id.graphql
new file mode 100644
index 0000000000..29b394d835
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/user/getMapOfUid2Id.graphql
@@ -0,0 +1,13 @@
+query getMapOfUid2Id($uids: [String!], $mgmId: Int!) {
+ usr(where: {
+ user_uid: {_in: $uids},
+ _or: [
+ {mgm_id: {_eq: $mgmId}},
+ {management: {multi_device_manager_id: {_eq: $mgmId}}}
+ ],
+ removed: {_is_null: true}
+ }) {
+ user_id
+ user_uid
+ }
+}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/user/getUserDetails.graphql b/roles/common/files/fwo-api-calls/user/getUserDetails.graphql
similarity index 84%
rename from roles/lib/files/FWO.Api.Client/APIcalls/user/getUserDetails.graphql
rename to roles/common/files/fwo-api-calls/user/getUserDetails.graphql
index 2b1e82fc5a..acdda0f284 100644
--- a/roles/lib/files/FWO.Api.Client/APIcalls/user/getUserDetails.graphql
+++ b/roles/common/files/fwo-api-calls/user/getUserDetails.graphql
@@ -14,8 +14,11 @@ query getUserDetails(
limit: $limit
offset: $offset
where: {
+ _or: [
+ { removed: { _is_null: true } }
+ { removed: { _gte: $import_id_start } }
+ ]
active: { _eq: $active }
- user_last_seen: { _gte: $import_id_start }
user_create: { _lte: $import_id_end }
user_name: { _in: $user_name }
}
diff --git a/roles/common/files/fwo-api-calls/zone/getMapOfName2Id.graphql b/roles/common/files/fwo-api-calls/zone/getMapOfName2Id.graphql
new file mode 100644
index 0000000000..bc7f268a6b
--- /dev/null
+++ b/roles/common/files/fwo-api-calls/zone/getMapOfName2Id.graphql
@@ -0,0 +1,13 @@
+query getMapOfName2Id($names: [String!], $mgmId: Int!) {
+ zone(where: {
+ zone_name: {_in: $names},
+ _or: [
+ {mgm_id: {_eq: $mgmId}},
+ {management: {multi_device_manager_id: {_eq: $mgmId}}}
+ ],
+ removed: {_is_null: true}
+ }) {
+ zone_id
+ zone_name
+ }
+}
diff --git a/roles/common/tasks/global-apache2-config.yml b/roles/common/tasks/global-apache2-config.yml
index 52b23f2894..844b7207e2 100644
--- a/roles/common/tasks/global-apache2-config.yml
+++ b/roles/common/tasks/global-apache2-config.yml
@@ -1,16 +1,13 @@
- name: install apache and make all global configs (non-server dependent)
block:
- - name: Install apache2
+ - name: Install apache2 and wsgi module
package:
- name: "{{ webserver_package_name }}"
- state: present
- update_cache: yes
-
- - name: Install wsgi module
- package:
- name: "{{ wsgi_package_name }}"
+ name: "{{ item }}"
state: present
update_cache: yes
+ loop:
+ - "{{ webserver_package_name }}"
+ - "{{ wsgi_package_name }}"
- name: edit conf file for global security settings - ServerTokens
lineinfile:
@@ -44,4 +41,4 @@
name: "{{ webserver_package_name }}"
state: restarted
- become: true
\ No newline at end of file
+ become: true
diff --git a/roles/common/tasks/install-api-calls.yml b/roles/common/tasks/install-api-calls.yml
new file mode 100644
index 0000000000..8bf6576ac5
--- /dev/null
+++ b/roles/common/tasks/install-api-calls.yml
@@ -0,0 +1,8 @@
+
+# this coppies all graphql queries and mutations to the server
+# for use with UI, MW and importer
+- name: install the fwo api calls
+ synchronize:
+ src: "fwo-api-calls"
+ dest: "{{ fworch_home }}"
+ become: true
diff --git a/roles/common/tasks/last_commit_id_file_creator.yml b/roles/common/tasks/last_commit_id_file_creator.yml
new file mode 100644
index 0000000000..22cee7131b
--- /dev/null
+++ b/roles/common/tasks/last_commit_id_file_creator.yml
@@ -0,0 +1,23 @@
+- block:
+ - name: remove local copy of remote config file
+ file:
+ state: absent
+ name: "temp_remote_config_file.json"
+
+ - name: get last commit id
+ command: git rev-parse HEAD
+ register: final_commit_id
+ failed_when: false
+ changed_when: false
+
+ # also handle case where we are not inside a git repo
+ - name: determine commit id content
+ set_fact:
+ final_commit_id_value: "{{ final_commit_id.stdout if final_commit_id.rc == 0 else 'unknown' }}"
+
+ - name: write commit id to last_commit_id.txt file
+ copy:
+ content: "{{ final_commit_id_value }}"
+ dest: "{{ last_commit_id_file_path }}"
+
+ become: true
diff --git a/roles/common/tasks/main.yml b/roles/common/tasks/main.yml
index ad8a6b31b7..c1cc7ea128 100644
--- a/roles/common/tasks/main.yml
+++ b/roles/common/tasks/main.yml
@@ -4,282 +4,287 @@
# only when not uninstalling ...
- block:
- # check installation mode
- - name: assert ansible version gt 2.12
- fail:
- msg: Ansible 2.13 or above is required
- when: ansible_version.full is version('2.13', '<')
-
- - name: check for existing main config file {{ fworch_conf_file }}
- stat:
- path: "{{ fworch_conf_file }}"
- register: already_installed
-
- - set_fact:
- already_installed: "{{ already_installed.stat.exists }}"
-
- - set_fact:
- wsgi_package_name: "{{ wsgi_package_name }}-py3"
- when: |
- (ansible_facts['distribution_release']|lower == debian_testing_release_name)
- or
- (ansible_facts['distribution']|lower == 'debian' and ansible_facts['distribution_major_version']|int is version('10', '>'))
- or
- (ansible_facts['distribution']|lower == 'ubuntu' and ansible_facts['distribution_major_version']|int is version('20', '>'))
-
- - debug:
- msg: "installation_mode={{ installation_mode }}, already_installed={{ already_installed }}"
-
- - name: fail if unknown installation_mode is set
- fail:
- msg: "Found undefined installation_mode: {{ installation_mode }}, aborting."
- when: installation_mode != "new" and installation_mode != "uninstall" and installation_mode != "upgrade"
-
- - name: fail if already installed and installation_mode is new
- fail:
- msg: "Found existing installation but running with installation_mode set to {{ installation_mode }}. If you want to upgrade, use 'installation_mode=upgrade'."
- when: already_installed|bool and installation_mode == "new"
-
- - name: fail if not already installed and installation_mode is upgrade
- fail:
- msg: "Could not find existing installation but running with installation_mode set to {{ installation_mode }}. Try running with installation_mode=new"
- when: not already_installed and installation_mode == "upgrade"
-
- # when upgrading check if we are not attempting a downgrade
- - block:
- - name: fetch remote config file
- fetch:
- src: "{{ fworch_conf_file }}"
- dest: "temp_remote_config_file.json"
- flat: true
-
- - name: read config file of currently installed product
- include_vars:
- file: "temp_remote_config_file.json"
- name: config
-
- - name: get version of currently installed product
- set_fact:
- old_version: "{{ config.product_version }}"
-
- - name: abort in case of downgrade
- fail:
- msg: "your are attempting to downgrade the product from {{ old_version }} to {{ product_version }}, which is not supported"
- when: old_version is version(product_version, '>')
-
- when: already_installed and installation_mode == "upgrade"
-
- - name: create group {{ fworch_group }}
- group:
- name: "{{ fworch_user }}"
- gid: "{{ user_id }}"
- state: present
- become: true
-
- - name: add user {{ fworch_user }}
- user:
- name: "{{ fworch_user }}"
- comment: "{{ product_name }} User"
- uid: "{{ user_id }}"
- home: "{{ fworch_home }}"
- shell: /bin/bash
- group: "{{ fworch_group }}"
- generate_ssh_key: true
- ssh_key_bits: 4096
- ssh_key_file: .ssh/id_rsa
- become: true
-
- - name: global apache config
- include_tasks: global-apache2-config.yml
- # vars:
- # apache2_required_modules: "{{ apache2_required_modules | default([]) }}"
- # webserver_package_name: "{{ webserver_package_name | default('apache2') }}"
- # http_conf_dir: "{{ http_conf_dir | default('/etc/apache2/sites-available') }}"
- # product_name: "{{ product_name }}"
- # product_version: "{{ product_version }}"
- # api_web_port: "{{ api_web_port | default(8080) }}"
- # server_admin: "{{ server_admin | default('webmaster@localhost') }}"
- # apache_global_settings_file: "{{ apache_global_settings_file | default('/etc/apache2/conf-available/security.conf') }}"
- when: "inventory_hostname in groups['frontends'] or inventory_hostname in groups['middlewareserver'] or inventory_hostname in groups['apiserver']"
-
- - name: replace fwo web sites with maintenance site
- include_tasks: maintenance-site.yml
- when: "installation_mode == 'upgrade' and inventory_hostname in groups['frontends']"
-
- - name: stop importer service before making any changes
- systemd:
- name: "{{ item }}"
- state: stopped
- become: true
- when: "inventory_hostname in groups['importers'] and installation_mode == 'upgrade'"
- loop:
- - "{{ product_name }}-importer-legacy"
- - "{{ product_name }}-importer-api"
-
- - name: stop importer service before making any changes
- systemd:
- name: "{{ item }}"
- state: stopped
- become: true
- ignore_errors: true # might not have been installed yet in case of early fail installs
- when: "inventory_hostname in groups['importers'] and installation_mode == 'uninstall'"
- loop:
- - "{{ product_name }}-importer-legacy"
- - "{{ product_name }}-importer-api"
-
- - name: update operating system package cache .deb based
- apt:
- update_cache: true
- when: ansible_facts['distribution'] == "Ubuntu" or ansible_facts['distribution'] == "Debian"
-
- - name: check for existing upgradable packages
- command: apt list --upgradable
- register: upgradable_packages
- when: ansible_facts['distribution'] == "Ubuntu" or ansible_facts['distribution'] == "Debian"
-
- - block:
- - debug:
- msg: "current number of upgradable packages: {{ upgradable_packages.stdout_lines|length-1 }}"
-
- - block:
- - name: disable apache2 maintenance web site
- command: "a2dissite {{ product_name }}-maintenance"
- ignore_errors: true
-
- - name: enable {{ product_name }} web site
- command: "a2ensite {{ product_name }}-ui"
- ignore_errors: true
-
- - name: restart apache without maintenance site
- service:
- name: "{{ webserver_package_name }}"
- state: restarted
- when: installation_mode == "upgrade"
-
- - name: assert there are no upgradable packages. upgrades must be run interactively outside the FWORCH installer
- fail:
- msg:
- - There are upgradable OS packages available, please run OS upgrade before running FWORCH installer.
- - Use "-e force_install=true" to overwrite this check and install anyway at your own risk.
- when: |
- not force_install|bool and
- (ansible_facts['distribution'] == "Ubuntu" or ansible_facts['distribution'] == "Debian") and
- upgradable_packages.stdout_lines|length > 1
-
- - name: update operating system packages .rpm based (untested)
- yum:
- upgrade: dist
- when: ansible_facts['distribution'] == "Red Hat" or ansible_facts['distribution'] == "CentOS"
-
- - name: install packages rsync, acl (for non-root user in ansible)
- package:
- name: "{{ item }}"
- state: present
- loop:
- - rsync
- - acl
-
- - name: create var lock directory
- file:
- path: "{{ fworch_log_lock_dir }}"
- state: directory
- owner: "{{ fworch_user }}"
- group: "{{ fworch_group }}"
- mode: "0775"
-
- - name: install rsyslog
- import_tasks: install_syslog.yml
- when: "install_syslog | bool"
-
- - name: create base {{ fworch_home }} directory
- file:
- path: "{{ fworch_home }}"
- state: directory
- owner: "{{ fworch_user }}"
- group: "{{ fworch_group }}"
- mode: "0775"
-
- - name: copy fworch scripts directory
- copy:
- src: scripts
- dest: "{{ fworch_home }}"
- mode: "0755"
- owner: "{{ fworch_user}}"
- group: "{{ fworch_user}}"
- become: true
-
- - name: add proxy setting to fworch users .profile and .bashrc
- blockinfile:
- path: "{{ fworch_home }}/{{ item }}"
- create: true
- mode: "0644"
- owner: "{{ fworch_user }}"
- group: "{{ fworch_group }}"
- block: |
- http_proxy={{ http_proxy }}
- https_proxy={{ http_proxy }}
- loop:
- - .bashrc
- - .profile
-
- - name: create etc dir
- file:
- path: "{{ fworch_home }}/etc"
- state: directory
- owner: "{{ fworch_user }}"
- group: "{{ fworch_group }}"
- mode: "0755"
-
- - name: create dir for passwords
- file:
- path: "{{ fworch_home }}/etc/secrets"
- state: directory
- owner: "{{ fworch_user }}"
- group: "{{ fworch_user }}"
- # group: "{{ postgres_group }}" # group does not exist yet during install, created in role database
- mode: "0750"
- when: "installation_mode == 'new'"
-
- - name: generate main key
- set_fact:
- main_key: "{{ randomly_generated_pwd }}" # 32 bytes
- main_key_file: "{{ fworch_secrets_dir }}/main_key"
- when: testkeys is not defined or testkeys|bool is false
-
- - name: set static main key for test purposes only
- set_fact:
- main_key: "{{ api_hasura_admin_test_password }}..{{ api_hasura_admin_test_password }}.." # to have 32 bytes
- main_key_file: "{{ fworch_secrets_dir }}/main_key"
- when: testkeys is defined and testkeys|bool
-
- - name: Check if main key file exists
- stat:
- path: "{{ main_key_file }}"
- register: stat_result
-
- - name: write main key to secrets directory
- copy:
- content: "{{ main_key }}\n"
- dest: "{{ main_key_file }}"
- mode: "0640"
- owner: "{{ fworch_user }}"
- group: "{{ fworch_group }}"
- become: true
- when: not stat_result.stat.exists
-
- - name: copy iso.conf to target for legacy importer support only
- template:
- src: iso.conf.j2
- dest: "{{ fworch_home }}/etc/iso.conf"
- owner: "{{ fworch_user }}"
- group: "{{ fworch_group }}"
-
- - name: include fworch.yaml config file creator
- import_tasks: conf_file_creator.yml
-
- - name: include upgrade script
- import_tasks: run-upgrades.yml
- when: "installation_mode == 'upgrade'"
+ # check installation mode
+ - name: assert ansible version gt 2.12
+ fail:
+ msg: Ansible 2.13 or above is required
+ when: ansible_version.full is version('2.13', '<')
+
+ - name: check for existing main config file {{ fworch_conf_file }}
+ stat:
+ path: "{{ fworch_conf_file }}"
+ register: already_installed
+
+ - set_fact:
+ already_installed: "{{ already_installed.stat.exists }}"
+
+ - set_fact:
+ wsgi_package_name: "{{ wsgi_package_name }}-py3"
+ when: |
+ (ansible_facts['distribution_release']|lower == debian_testing_release_name)
+ or
+ (ansible_facts['distribution']|lower == 'debian' and ansible_facts['distribution_major_version']|int is version('10', '>'))
+ or
+ (ansible_facts['distribution']|lower == 'ubuntu' and ansible_facts['distribution_major_version']|int is version('20', '>'))
+
+ - debug:
+ msg: "installation_mode={{ installation_mode }}, already_installed={{ already_installed }}"
+
+ - name: fail if unknown installation_mode is set
+ fail:
+ msg: "Found undefined installation_mode: {{ installation_mode }}, aborting."
+ when: installation_mode != "new" and installation_mode != "uninstall" and installation_mode != "upgrade"
+
+ - name: fail if already installed and installation_mode is new
+ fail:
+ msg: "Found existing installation but running with installation_mode set to {{ installation_mode }}. If you want to upgrade, use 'installation_mode=upgrade'."
+ when: already_installed|bool and installation_mode == "new"
+
+ - name: fail if not already installed and installation_mode is upgrade
+ fail:
+ msg: "Could not find existing installation but running with installation_mode set to {{ installation_mode }}. Try running with installation_mode=new"
+ when: not already_installed and installation_mode == "upgrade"
+
+ # when upgrading check if we are not attempting a downgrade
+ - block:
+ - name: fetch remote config file
+ fetch:
+ src: "{{ fworch_conf_file }}"
+ dest: "temp_remote_config_file.json"
+ flat: true
+
+ - name: read config file of currently installed product
+ include_vars:
+ file: "temp_remote_config_file.json"
+ name: config
+
+ - name: get version of currently installed product
+ set_fact:
+ old_version: "{{ config.product_version }}"
+
+ when: already_installed and installation_mode == "upgrade"
+
+ - name: create group {{ fworch_group }}
+ group:
+ name: "{{ fworch_user }}"
+ gid: "{{ user_id }}"
+ state: present
+ become: true
+
+ - name: add user {{ fworch_user }}
+ user:
+ name: "{{ fworch_user }}"
+ comment: "{{ product_name }} User"
+ uid: "{{ user_id }}"
+ home: "{{ fworch_home }}"
+ shell: /bin/bash
+ group: "{{ fworch_group }}"
+ generate_ssh_key: true
+ ssh_key_bits: 4096
+ ssh_key_file: .ssh/id_rsa
+ become: true
+
+ - name: global apache config
+ include_tasks: global-apache2-config.yml
+ # vars:
+ # apache2_required_modules: "{{ apache2_required_modules | default([]) }}"
+ # webserver_package_name: "{{ webserver_package_name | default('apache2') }}"
+ # http_conf_dir: "{{ http_conf_dir | default('/etc/apache2/sites-available') }}"
+ # product_name: "{{ product_name }}"
+ # product_version: "{{ product_version }}"
+ # api_web_port: "{{ api_web_port | default(8080) }}"
+ # server_admin: "{{ server_admin | default('webmaster@localhost') }}"
+ # apache_global_settings_file: "{{ apache_global_settings_file | default('/etc/apache2/conf-available/security.conf') }}"
+ when: "inventory_hostname in groups['frontends'] or inventory_hostname in groups['middlewareserver'] or inventory_hostname in groups['apiserver']"
+
+ - name: replace fwo web sites with maintenance site
+ include_tasks: maintenance-site.yml
+ when: "installation_mode == 'upgrade' and inventory_hostname in groups['frontends']"
+
+ - name: stop importer service before making any changes
+ systemd:
+ name: "{{ item }}"
+ state: stopped
+ become: true
+ when: "inventory_hostname in groups['importers'] and installation_mode == 'upgrade'"
+ loop:
+ - "{{ product_name }}-importer-legacy"
+ - "{{ product_name }}-importer-api"
+
+ - name: stop importer service before making any changes
+ systemd:
+ name: "{{ item }}"
+ state: stopped
+ become: true
+ ignore_errors: true # might not have been installed yet in case of early fail installs
+ when: "inventory_hostname in groups['importers'] and installation_mode == 'uninstall'"
+ loop:
+ - "{{ product_name }}-importer-legacy"
+ - "{{ product_name }}-importer-api"
+
+ - name: update operating system package cache .deb based
+ apt:
+ update_cache: true
+ when: ansible_facts['distribution'] == "Ubuntu" or ansible_facts['distribution'] == "Debian"
+
+ - name: check for existing upgradable packages
+ command: apt list --upgradable
+ register: upgradable_packages
+ when: ansible_facts['distribution'] == "Ubuntu" or ansible_facts['distribution'] == "Debian"
+
+ - block:
+ - debug:
+ msg: "current number of upgradable packages: {{ upgradable_packages.stdout_lines|length-1 }}"
+
+ - block:
+ - name: disable apache2 maintenance web site
+ command: "a2dissite {{ product_name }}-maintenance"
+ ignore_errors: true
+
+ - name: enable {{ product_name }} web site
+ command: "a2ensite {{ product_name }}-ui"
+ ignore_errors: true
+
+ - name: restart apache without maintenance site
+ service:
+ name: "{{ webserver_package_name }}"
+ state: restarted
+ when: installation_mode == "upgrade"
+
+ - name: assert there are no upgradable packages. upgrades must be run interactively outside the FWORCH installer
+ fail:
+ msg:
+ - There are upgradable OS packages available, please run OS upgrade before running FWORCH installer.
+ - Use "-e force_install=true" to overwrite this check and install anyway at your own risk.
+ when: |
+ not force_install|bool and
+ (ansible_facts['distribution'] == "Ubuntu" or ansible_facts['distribution'] == "Debian") and
+ upgradable_packages.stdout_lines|length > 1
+
+ - name: update operating system packages .rpm based (untested)
+ yum:
+ upgrade: dist
+ when: ansible_facts['distribution'] == "Red Hat" or ansible_facts['distribution'] == "CentOS"
+
+ - name: install packages rsync, acl (for non-root user in ansible)
+ package:
+ name: "{{ item }}"
+ state: present
+ loop:
+ - rsync
+ - acl
+
+ - name: create var lock directory
+ file:
+ path: "{{ fworch_log_lock_dir }}"
+ state: directory
+ owner: "{{ fworch_user }}"
+ group: "{{ fworch_group }}"
+ mode: "0775"
+
+ - name: install rsyslog
+ import_tasks: install_syslog.yml
+ when: "install_syslog | bool"
+
+ - name: create base {{ fworch_home }} directory
+ file:
+ path: "{{ fworch_home }}"
+ state: directory
+ owner: "{{ fworch_user }}"
+ group: "{{ fworch_group }}"
+ mode: "0775"
+
+ - name: copy fworch scripts directory
+ copy:
+ src: scripts
+ dest: "{{ fworch_home }}"
+ mode: "0755"
+ owner: "{{ fworch_user}}"
+ group: "{{ fworch_user}}"
+ become: true
+
+ - name: add proxy setting to fworch users .profile and .bashrc
+ blockinfile:
+ path: "{{ fworch_home }}/{{ item }}"
+ create: true
+ mode: "0644"
+ owner: "{{ fworch_user }}"
+ group: "{{ fworch_group }}"
+ block: |
+ http_proxy={{ http_proxy }}
+ https_proxy={{ http_proxy }}
+ loop:
+ - .bashrc
+ - .profile
+
+ - name: create etc dir
+ file:
+ path: "{{ fworch_home }}/etc"
+ state: directory
+ owner: "{{ fworch_user }}"
+ group: "{{ fworch_group }}"
+ mode: "0755"
+
+ - name: create dir for passwords
+ file:
+ path: "{{ fworch_home }}/etc/secrets"
+ state: directory
+ owner: "{{ fworch_user }}"
+ group: "{{ fworch_user }}"
+ # group: "{{ postgres_group }}" # group does not exist yet during install, created in role database
+ mode: "0750"
+ when: "installation_mode == 'new'"
+
+ - name: generate main key
+ set_fact:
+ main_key: "{{ randomly_generated_pwd }}" # 32 bytes
+ main_key_file: "{{ fworch_secrets_dir }}/main_key"
+ when: testkeys is not defined or testkeys|bool is false
+
+ - name: set static main key for test purposes only
+ set_fact:
+ main_key: "{{ api_hasura_admin_test_password }}..{{ api_hasura_admin_test_password }}.." # to have 32 bytes
+ main_key_file: "{{ fworch_secrets_dir }}/main_key"
+ when: testkeys is defined and testkeys|bool
+
+ - name: Check if main key file exists
+ stat:
+ path: "{{ main_key_file }}"
+ register: stat_result
+
+ - name: write main key to secrets directory
+ copy:
+ content: "{{ main_key }}\n"
+ dest: "{{ main_key_file }}"
+ mode: "0640"
+ owner: "{{ fworch_user }}"
+ group: "{{ fworch_group }}"
+ become: true
+ when: not stat_result.stat.exists
+
+ - name: include fwo api call installer
+ import_tasks: install-api-calls.yml
+
+ - name: include upgrade script
+ import_tasks: run-upgrades.yml
+ when: "installation_mode == 'upgrade'"
+
+ - name: copy iso.conf to target for legacy importer support only
+ template:
+ src: iso.conf.j2
+ dest: "{{ fworch_home }}/etc/iso.conf"
+ owner: "{{ fworch_user }}"
+ group: "{{ fworch_group }}"
+
+ - name: include fworch.yaml config file creator
+ import_tasks: conf_file_creator.yml
+
+ - name: include last_commit_id.txt file creator
+ import_tasks: last_commit_id_file_creator.yml
+
+ - name: include upgrade script
+ import_tasks: run-upgrades.yml
+ when: "installation_mode == 'upgrade'"
become: true
when: "installation_mode != 'uninstall'"
diff --git a/roles/database/files/sql/creation/fworch-create-constraints.sql b/roles/database/files/sql/creation/fworch-create-constraints.sql
index dd66884566..ef0e473dcf 100755
--- a/roles/database/files/sql/creation/fworch-create-constraints.sql
+++ b/roles/database/files/sql/creation/fworch-create-constraints.sql
@@ -17,15 +17,31 @@ Alter Table "import_control" add Constraint "control_id_stop_time_unique" UNIQUE
Alter Table "object" add Constraint "obj_altkey" UNIQUE ("mgm_id","zone_id","obj_uid","obj_create");
ALTER TABLE object ADD CONSTRAINT object_obj_ip_is_host CHECK (is_single_ip(obj_ip));
ALTER TABLE object ADD CONSTRAINT object_obj_ip_end_is_host CHECK (is_single_ip(obj_ip_end));
-ALTER TABLE object ADD CONSTRAINT object_obj_ip_not_null CHECK (obj_ip IS NOT NULL OR obj_typ_id=2);
-ALTER TABLE object ADD CONSTRAINT object_obj_ip_end_not_null CHECK (obj_ip_end IS NOT NULL OR obj_typ_id=2);
+-- magic numbers here: 1 = host object, 3 = network object, 4 = range object
+ALTER TABLE "object" ADD CONSTRAINT object_obj_ip_not_null CHECK (NOT (obj_ip IS NULL AND obj_typ_id IN (1, 3, 4)));
+ALTER TABLE "object" ADD CONSTRAINT object_obj_ip_end_not_null CHECK (NOT (obj_ip_end IS NULL AND obj_typ_id IN (1, 3, 4)));
ALTER TABLE owner ADD CONSTRAINT owner_name_unique_in_tenant UNIQUE ("name","tenant_id");
ALTER TABLE owner_network ADD CONSTRAINT port_in_valid_range CHECK (port > 0 and port <= 65535);
+ALTER TABLE owner_network ADD CONSTRAINT owner_network_ip_is_host CHECK (is_single_ip(ip));
+ALTER TABLE owner_network ADD CONSTRAINT owner_network_ip_end_is_host CHECK (is_single_ip(ip_end));
ALTER TABLE owner_network ADD CONSTRAINT owner_network_ip_unique UNIQUE (owner_id, ip, ip_end, import_source);
ALTER TABLE request.reqelement ADD CONSTRAINT port_in_valid_range CHECK (port > 0 and port <= 65535);
ALTER TABLE request.implelement ADD CONSTRAINT port_in_valid_range CHECK (port > 0 and port <= 65535);
-Alter Table "rule" add Constraint "rule_altkey" UNIQUE ("dev_id","rule_uid","rule_create",xlate_rule);
-Alter Table "rule_metadata" add Constraint "rule_metadata_alt_key" UNIQUE ("rule_uid","dev_id");
+-- Alter Table "rule" add Constraint "rule_altkey" UNIQUE ("dev_id","rule_uid","rule_create",xlate_rule);
+Alter Table "rule" ADD Constraint "rule_unique_mgm_id_rule_uid_rule_create_xlate_rule" UNIQUE ("mgm_id", "rule_uid","rule_create","xlate_rule");
+-- Alter Table "rule_metadata" add Constraint "rule_metadata_alt_key" UNIQUE ("rule_uid","dev_id");
+-- Alter Table "rule_metadata" add Constraint "rule_metadata_alt_key" UNIQUE ("rule_uid","dev_id","rulebase_id");
+ALTER TABLE rule_metadata ADD Constraint "rule_metadata_rule_uid_unique" unique ("rule_uid");
+ALTER TABLE rule_metadata ADD CONSTRAINT rule_metadata_mgm_id_rule_uid_unique UNIQUE (mgm_id, rule_uid);
+Alter table "rulebase" add CONSTRAINT unique_rulebase_mgm_id_uid UNIQUE ("mgm_id", "uid");
+Alter table "rulebase_link" add CONSTRAINT unique_rulebase_link
+ UNIQUE (
+ "gw_id",
+ "from_rulebase_id",
+ "from_rule_id",
+ "to_rulebase_id",
+ "created"
+ );
Alter Table "service" add Constraint "svc_altkey" UNIQUE ("mgm_id","svc_uid","svc_create");
Alter Table "stm_dev_typ" add Constraint "Alter_Key1" UNIQUE ("dev_typ_name","dev_typ_version");
Alter Table "usr" add Constraint "usr_altkey" UNIQUE ("mgm_id","user_name","user_create");
@@ -35,15 +51,18 @@ create unique index if not exists only_one_future_recert_per_owner_per_rule on r
where recert_date IS NULL;
--- compliance
+
CREATE EXTENSION IF NOT EXISTS btree_gist;
ALTER TABLE compliance.ip_range ADD CONSTRAINT "exclude_overlapping_ip_ranges"
EXCLUDE USING gist (
network_zone_id WITH =,
numrange(ip_range_start - '0.0.0.0'::inet, ip_range_end - '0.0.0.0'::inet, '[]') WITH &&
-);
+)
+WHERE (removed IS NULL);
+
+--- report template
+
+ALTER TABLE report_template
+ADD CONSTRAINT unique_report_template_name UNIQUE (report_template_name);
-ALTER TABLE owner_network ADD CONSTRAINT owner_network_ip_is_host CHECK (is_single_ip(ip));
-ALTER TABLE owner_network ADD CONSTRAINT owner_network_ip_end_is_host CHECK (is_single_ip(ip_end));
--- Alter Table modelling.service add Constraint "modelling_service_unique_name" UNIQUE ("name", "app_id");
--- Alter Table modelling.service add Constraint "modelling_service_unique_name_ext_app_id" UNIQUE ("name", "ext_app_id");
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 071d8cb8e2..11722b4f8e 100755
--- a/roles/database/files/sql/creation/fworch-create-foreign-keys.sql
+++ b/roles/database/files/sql/creation/fworch-create-foreign-keys.sql
@@ -14,7 +14,6 @@ Alter table "changelog_object" add foreign key ("new_obj_id") references "objec
Alter table "changelog_object" add foreign key ("old_obj_id") references "object" ("obj_id") on update restrict on delete cascade;
Alter table "changelog_rule" add foreign key ("change_type_id") references "stm_change_type" ("change_type_id") on update restrict on delete cascade;
Alter table "changelog_rule" add foreign key ("control_id") references "import_control" ("control_id") on update restrict on delete cascade;
-Alter table "changelog_rule" add foreign key ("dev_id") references "device" ("dev_id") on update restrict on delete cascade;
Alter table "changelog_rule" add foreign key ("doku_admin") references "uiuser" ("uiuser_id") on update restrict on delete cascade;
Alter table "changelog_rule" add foreign key ("import_admin") references "uiuser" ("uiuser_id") on update restrict on delete cascade;
Alter table "changelog_rule" add foreign key ("mgm_id") references "management" ("mgm_id") on update restrict on delete cascade;
@@ -53,7 +52,7 @@ Alter table "import_user" add foreign key ("control_id") references "import_con
Alter table "import_zone" add foreign key ("control_id") references "import_control" ("control_id") on update restrict on delete cascade;
Alter table "ldap_connection" add foreign key ("tenant_id") references "tenant" ("tenant_id") on update restrict on delete cascade;
Alter table "management" add foreign key ("dev_typ_id") references "stm_dev_typ" ("dev_typ_id") on update restrict on delete cascade;
-ALTER TABLE "management" ADD CONSTRAINT management_multi_device_manager_id_fkey FOREIGN KEY ("multi_device_manager_id") REFERENCES "management" ("mgm_id") ON UPDATE RESTRICT; --ON DELETE CASCADE;
+ALTER TABLE "management" ADD CONSTRAINT management_multi_device_manager_id_fkey FOREIGN KEY ("multi_device_manager_id") REFERENCES "management" ("mgm_id") ON UPDATE RESTRICT;
ALTER TABLE "management" ADD CONSTRAINT management_import_credential_id_foreign_key FOREIGN KEY (import_credential_id) REFERENCES import_credential(id) ON UPDATE RESTRICT ON DELETE CASCADE;
Alter table "object" add foreign key ("last_change_admin") references "uiuser" ("uiuser_id") on update restrict on delete cascade;
Alter table "object" add foreign key ("mgm_id") references "management" ("mgm_id") on update restrict on delete cascade;
@@ -83,7 +82,6 @@ Alter table "report_template" add foreign key ("report_template_owner") referenc
Alter table "report_template_viewable_by_user" add foreign key ("report_template_id") references "report_template" ("report_template_id") on update restrict on delete cascade;
Alter table "report_template_viewable_by_user" add foreign key ("uiuser_id") references "uiuser" ("uiuser_id") on update restrict on delete cascade;
Alter table "rule" add foreign key ("action_id") references "stm_action" ("action_id") on update restrict on delete cascade;
-Alter table "rule" add foreign key ("dev_id") references "device" ("dev_id") on update restrict on delete cascade;
Alter table "rule" add foreign key ("last_change_admin") references "uiuser" ("uiuser_id") on update restrict on delete cascade;
Alter table "rule" add foreign key ("mgm_id") references "management" ("mgm_id") on update restrict on delete cascade;
Alter table "rule" add foreign key ("rule_create") references "import_control" ("control_id") on update restrict on delete cascade;
@@ -97,8 +95,29 @@ ALTER TABLE "rule"
ADD CONSTRAINT rule_rule_parent_rule_id_fkey FOREIGN KEY ("parent_rule_id") REFERENCES "rule" ("rule_id") ON UPDATE RESTRICT ON DELETE CASCADE;
ALTER TABLE "rule"
ADD CONSTRAINT rule_parent_rule_type_id_fkey FOREIGN KEY ("parent_rule_type") REFERENCES "parent_rule_type" ("id") ON UPDATE RESTRICT ON DELETE CASCADE;
-Alter table "rule" add constraint "rule_metadata_dev_id_rule_uid_f_key"
- foreign key ("dev_id", "rule_uid") references "rule_metadata" ("dev_id", "rule_uid") on update restrict on delete cascade;
+-- Alter table "rule" add constraint "rule_metadata_rule_uid_f_key"
+-- foreign key ("rule_uid") references "rule_metadata" ("rule_uid") on update restrict on delete cascade;
+Alter table "rule" add constraint "fk_rule_rulebase_id" foreign key ("rulebase_id") references "rulebase" ("id") on update restrict on delete cascade;
+-- Alter table "rule" add constraint "fk_rule_rule_metadata_rule_uid"
+-- foreign key ("rule_uid") references "rule_metadata" ("rule_uid") on update restrict on delete cascade;
+Alter table "rule" add constraint "rule_rule_metadata_rule_uid_f_key"
+ foreign key ("rule_uid") references "rule_metadata" ("rule_uid") on update restrict on delete cascade;
+
+Alter table "rulebase" add CONSTRAINT fk_rulebase_mgm_id foreign key ("mgm_id") references "management" ("mgm_id") on update restrict on delete cascade;
+Alter table "rulebase_link" add constraint "fk_rulebase_link_from_rulebase_id" foreign key ("from_rulebase_id") references "rulebase" ("id") on update restrict on delete cascade;
+Alter table "rulebase_link" add constraint "fk_rulebase_link_to_rulebase_id" foreign key ("to_rulebase_id") references "rulebase" ("id") on update restrict on delete cascade;
+Alter table "rulebase_link" add constraint "fk_rulebase_link_from_rule_id" foreign key ("from_rule_id") references "rule" ("rule_id") on update restrict on delete cascade;
+Alter table "rulebase_link" add constraint "fk_rulebase_link_link_type" foreign key ("link_type") references "stm_link_type" ("id") on update restrict on delete cascade;
+Alter table "rulebase_link" add constraint "fk_rulebase_link_gw_id" foreign key ("gw_id") references "device" ("dev_id") on update restrict on delete cascade;
+Alter table "rulebase_link" add CONSTRAINT fk_rulebase_link_created_import_control_control_id
+ foreign key ("created") references "import_control" ("control_id") on update restrict on delete cascade;
+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_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;
@@ -106,12 +125,19 @@ Alter table "rule_from" add foreign key ("rf_last_seen") references "import_con
Alter table "rule_from" add foreign key ("rule_id") references "rule" ("rule_id") on update restrict on delete cascade;
Alter table "rule_from" add foreign key ("user_id") references "usr" ("user_id") on update restrict on delete cascade;
-Alter table "rule_metadata" add constraint "rule_metadata_device_dev_id_f_key"
- foreign key ("dev_id") references "device" ("dev_id") on update restrict on delete cascade;
+-- Alter table "rule_metadata" add constraint "rule_metadata_device_dev_id_f_key"
+-- foreign key ("dev_id") references "device" ("dev_id") on update restrict on delete cascade;
Alter table "rule_metadata" add constraint "rule_metadata_rule_last_certifier_uiuser_uiuser_id_f_key"
foreign key ("rule_last_certifier") references "uiuser" ("uiuser_id") on update restrict on delete cascade;
Alter table "rule_metadata" add constraint "rule_metadata_rule_owner_uiuser_uiuser_id_f_key"
foreign key ("rule_owner") references "uiuser" ("uiuser_id") on update restrict on delete cascade;
+ALTER TABLE rule_metadata ADD CONSTRAINT rule_metadata_mgm_id_management_id_fk FOREIGN KEY (mgm_id) REFERENCES management(mgm_id)
+ON update restrict on delete cascade;
+
+Alter table "rule_enforced_on_gateway" add CONSTRAINT fk_rule_enforced_on_gateway_rule_rule_id foreign key ("rule_id") references "rule" ("rule_id") on update restrict on delete cascade;
+Alter table "rule_enforced_on_gateway" add CONSTRAINT fk_rule_enforced_on_gateway_device_dev_id foreign key ("dev_id") references "device" ("dev_id") on update restrict on delete cascade;
+Alter table "rule_enforced_on_gateway" add CONSTRAINT fk_rule_enforced_on_gateway_created_import_control_control_id foreign key ("created") references "import_control" ("control_id") on update restrict on delete cascade;
+Alter table "rule_enforced_on_gateway" add CONSTRAINT fk_rule_enforced_on_gateway_removed_import_control_control_id foreign key ("removed") references "import_control" ("control_id") on update restrict on delete cascade;
Alter table "rule_nwobj_resolved" add foreign key ("obj_id") references "object" ("obj_id") on update restrict on delete cascade;
Alter table "rule_nwobj_resolved" add foreign key ("rule_id") references "rule" ("rule_id") on update restrict on delete cascade;
@@ -124,6 +150,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_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;
Alter table "rule_svc_resolved" add foreign key ("mgm_id") references "management" ("mgm_id") on update restrict on delete cascade;
@@ -289,13 +320,29 @@ ALTER TABLE owner_recertification ADD CONSTRAINT owner_recertification_report_fo
--- compliance.ip_range ---
ALTER TABLE compliance.ip_range ADD CONSTRAINT compliance_ip_range_network_zone_foreign_key FOREIGN KEY (network_zone_id) REFERENCES compliance.network_zone(id) ON UPDATE RESTRICT ON DELETE CASCADE;
+ALTER TABLE compliance.ip_range ADD CONSTRAINT compliance_criterion_ip_range_foreign_key FOREIGN KEY (criterion_id) REFERENCES compliance.criterion(id) ON UPDATE RESTRICT ON DELETE CASCADE;
--- compliance.network_zone ---
ALTER TABLE compliance.network_zone ADD CONSTRAINT compliance_super_zone_foreign_key FOREIGN KEY (super_network_zone_id) REFERENCES compliance.network_zone(id) ON UPDATE RESTRICT ON DELETE CASCADE;
+ALTER TABLE compliance.network_zone ADD CONSTRAINT compliance_criterion_network_zone_foreign_key FOREIGN KEY (criterion_id) REFERENCES compliance.criterion(id) ON UPDATE RESTRICT ON DELETE CASCADE;
--- compliance.network_zone_communication ---
ALTER TABLE compliance.network_zone_communication ADD CONSTRAINT compliance_from_network_zone_communication_foreign_key FOREIGN KEY (from_network_zone_id) REFERENCES compliance.network_zone(id) ON UPDATE RESTRICT ON DELETE CASCADE;
ALTER TABLE compliance.network_zone_communication ADD CONSTRAINT compliance_to_network_zone_communication_foreign_key FOREIGN KEY (to_network_zone_id) REFERENCES compliance.network_zone(id) ON UPDATE RESTRICT ON DELETE CASCADE;
+ALTER TABLE compliance.network_zone_communication ADD CONSTRAINT compliance_criterion_network_zone_communication_foreign_key FOREIGN KEY (criterion_id) REFERENCES compliance.criterion(id) ON UPDATE RESTRICT ON DELETE CASCADE;
+
+--- compliance.policy_criterion ---
+ALTER TABLE compliance.policy_criterion ADD CONSTRAINT compliance_policy_policy_criterion_foreign_key FOREIGN KEY (policy_id) REFERENCES compliance.policy(id) ON UPDATE RESTRICT ON DELETE CASCADE;
+ALTER TABLE compliance.policy_criterion ADD CONSTRAINT compliance_criterion_policy_criterion_foreign_key FOREIGN KEY (criterion_id) REFERENCES compliance.criterion(id) ON UPDATE RESTRICT ON DELETE CASCADE;
+
+--- compliance.violation ---
+ALTER TABLE compliance.violation ADD CONSTRAINT compliance_policy_violation_foreign_key FOREIGN KEY (policy_id) REFERENCES compliance.policy(id) ON UPDATE RESTRICT ON DELETE CASCADE;
+ALTER TABLE compliance.violation ADD CONSTRAINT compliance_criterion_violation_foreign_key FOREIGN KEY (criterion_id) REFERENCES compliance.criterion(id) ON UPDATE RESTRICT ON DELETE CASCADE;
+ALTER TABLE compliance.violation ADD CONSTRAINT compliance_rule_violation_foreign_key FOREIGN KEY (rule_id) REFERENCES public.rule(rule_id) ON UPDATE RESTRICT ON DELETE CASCADE;
+
+-- ALTER TABLE compliance.assessability_issue ADD CONSTRAINT compliance_assessability_issue_type_foreign_key FOREIGN KEY (type_id) REFERENCES compliance.assessability_issue_type(type_id) ON UPDATE RESTRICT ON DELETE CASCADE;
+-- ALTER TABLE compliance.assessability_issue ADD CONSTRAINT compliance_assessability_issue_violation_foreign_key FOREIGN KEY (violation_id) REFERENCES compliance.violation(id) ON UPDATE RESTRICT ON DELETE CASCADE;
+
-- modelling
ALTER TABLE modelling.nwgroup ADD CONSTRAINT modelling_nwgroup_owner_foreign_key FOREIGN KEY (app_id) REFERENCES owner(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 2b212f4ba5..276cdd09d1 100755
--- a/roles/database/files/sql/creation/fworch-create-tables.sql
+++ b/roles/database/files/sql/creation/fworch-create-tables.sql
@@ -33,6 +33,7 @@ Create table "device" -- contains an entry for each firewall gateway
"dev_id" SERIAL,
"mgm_id" Integer NOT NULL,
"dev_name" Varchar,
+ "dev_uid" Varchar,
"local_rulebase_name" Varchar,
"local_rulebase_uid" Varchar,
"global_rulebase_name" Varchar,
@@ -76,10 +77,15 @@ Create table "management" -- contains an entry for each firewall management syst
"importer_hostname" Varchar,
"debug_level" Integer,
"multi_device_manager_id" integer, -- if this manager belongs to another multi_device_manager, then this id points to it
+ "is_super_manager" BOOLEAN DEFAULT FALSE,
"ext_mgm_data" Varchar,
+ "mgm_uid" Varchar NOT NULL DEFAULT '',
+ "rulebase_name" Varchar NOT NULL DEFAULT '',
+ "rulebase_uid" Varchar NOT NULL DEFAULT '',
primary key ("mgm_id")
);
+
create table import_credential
(
id SERIAL PRIMARY KEY,
@@ -122,6 +128,7 @@ Create table "object"
"obj_sys_readcom" Varchar,
"obj_sys_writecom" Varchar,
"active" Boolean NOT NULL Default TRUE,
+ "removed" BIGINT,
"obj_create" BIGINT NOT NULL,
"obj_last_seen" BIGINT NOT NULL,
primary key ("obj_id")
@@ -132,6 +139,7 @@ Create table "objgrp"
"objgrp_id" BIGINT NOT NULL,
"objgrp_member_id" BIGINT NOT NULL,
"import_created" BIGINT NOT NULL,
+ "removed" BIGINT,
"import_last_seen" BIGINT NOT NULL,
"active" Boolean NOT NULL Default TRUE,
"negated" Boolean NOT NULL Default FALSE,
@@ -147,6 +155,7 @@ Create table "rule"
"parent_rule_id" BIGINT,
"parent_rule_type" smallint,
"active" Boolean NOT NULL Default TRUE,
+ "removed" BIGINT,
"rule_num" Integer NOT NULL,
"rule_num_numeric" NUMERIC(16, 8),
"rule_ruleid" Varchar,
@@ -179,15 +188,17 @@ Create table "rule"
"access_rule" BOOLEAN Default TRUE,
"nat_rule" BOOLEAN Default FALSE,
"xlate_rule" BIGINT,
- primary key ("rule_id")
+ "is_global" BOOLEAN DEFAULT FALSE NOT NULL,
+ "rulebase_id" Integer NOT NULL,
+ primary key ("rule_id")
);
-- rule_metadata contains rule related data that does not change when the rule itself is changed
Create table "rule_metadata"
(
"rule_metadata_id" BIGSERIAL,
- "dev_id" Integer NOT NULL,
"rule_uid" Text NOT NULL,
+ "mgm_id" Integer NOT NULL,
"rule_created" Timestamp NOT NULL Default now(),
"rule_last_modified" Timestamp NOT NULL Default now(),
"rule_first_hit" Timestamp,
@@ -202,7 +213,7 @@ Create table "rule_metadata"
"last_change_admin" Integer,
"rule_decert_date" Timestamp,
"rule_recertification_comment" Varchar,
- primary key ("rule_metadata_id")
+ primary key ("rule_metadata_id")
);
-- adding direct link tables rule_[svc|nwobj|user]_resolved to make report object export easier
@@ -245,6 +256,7 @@ Create table "rule_from"
"user_id" BIGINT,
"active" Boolean NOT NULL Default TRUE,
"negated" Boolean NOT NULL Default FALSE,
+ "removed" BIGINT,
"rf_create" BIGINT NOT NULL,
"rf_last_seen" BIGINT NOT NULL
);
@@ -257,6 +269,7 @@ Create table "rule_service"
"rs_create" BIGINT NOT NULL,
"rs_last_seen" BIGINT NOT NULL,
"negated" Boolean NOT NULL Default FALSE,
+ "removed" BIGINT,
primary key ("rule_id","svc_id")
);
@@ -270,6 +283,7 @@ Create table "rule_to"
"rt_create" BIGINT NOT NULL,
"rt_last_seen" BIGINT NOT NULL,
"active" Boolean NOT NULL Default TRUE,
+ "removed" BIGINT,
"negated" Boolean NOT NULL Default FALSE
);
@@ -306,6 +320,7 @@ Create table "service"
"svc_sync_delay_start" Integer,
"active" Boolean NOT NULL Default TRUE,
"last_change_admin" Integer,
+ "removed" BIGINT,
"svc_create" BIGINT NOT NULL,
"svc_last_seen" BIGINT NOT NULL,
primary key ("svc_id")
@@ -317,6 +332,7 @@ Create table "svcgrp"
"svcgrp_member_id" BIGINT NOT NULL,
"import_created" BIGINT NOT NULL,
"import_last_seen" BIGINT NOT NULL,
+ "removed" BIGINT,
"active" Boolean NOT NULL Default TRUE,
"negated" Boolean NOT NULL Default FALSE,
primary key ("svcgrp_id","svcgrp_member_id")
@@ -327,12 +343,33 @@ Create table "zone"
"zone_id" SERIAL,
"zone_create" BIGINT NOT NULL,
"zone_last_seen" BIGINT NOT NULL,
+ "removed" BIGINT,
"mgm_id" Integer NOT NULL,
"zone_name" Varchar NOT NULL,
"active" Boolean NOT NULL Default TRUE,
primary key ("zone_id")
);
+--crosstabulation rule zone for source
+Create table "rule_from_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_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,
@@ -351,6 +388,7 @@ Create table "usr"
"time_restrict" Text,
"user_create" BIGINT NOT NULL,
"user_last_seen" BIGINT NOT NULL,
+ "removed" BIGINT,
"user_comment" Text,
"user_uid" Text,
"user_firstname" Varchar,
@@ -365,6 +403,7 @@ Create table "usergrp"
"usergrp_member_id" BIGINT,
"import_created" BIGINT NOT NULL,
"import_last_seen" BIGINT NOT NULL,
+ "removed" BIGINT,
"active" Boolean NOT NULL Default TRUE,
primary key ("usergrp_id","usergrp_member_id")
);
@@ -376,6 +415,7 @@ Create table "usergrp_flat"
"usergrp_flat_member_id" BIGINT NOT NULL,
"import_created" BIGINT NOT NULL,
"import_last_seen" BIGINT NOT NULL,
+ "removed" BIGINT,
primary key ("usergrp_flat_id","usergrp_flat_member_id")
);
@@ -386,16 +426,18 @@ Create table "objgrp_flat"
"active" Boolean NOT NULL Default TRUE,
"import_created" BIGINT NOT NULL,
"import_last_seen" BIGINT NOT NULL,
+ "removed" BIGINT,
"negated" Boolean NOT NULL Default FALSE
);
Create table "svcgrp_flat"
(
- "svcgrp_flat_id" Integer NOT NULL,
- "svcgrp_flat_member_id" Integer NOT NULL,
- "import_created" Integer NOT NULL,
- "import_last_seen" Integer NOT NULL,
+ "svcgrp_flat_id" BIGINT NOT NULL,
+ "svcgrp_flat_member_id" BIGINT NOT NULL,
+ "import_created" BIGINT NOT NULL,
+ "import_last_seen" BIGINT NOT NULL,
"active" Boolean NOT NULL Default TRUE,
+ "removed" BIGINT,
"negated" Boolean NOT NULL Default FALSE
);
@@ -501,15 +543,22 @@ Create table "tenant_network"
Create table "parent_rule_type"
(
- "id" smallserial NOT NULL,
+ "id" smallint NOT NULL,
"name" Varchar NOT NULL,
primary key ("id")
);
+Create table IF NOT EXISTS "stm_link_type"
+(
+ "id" Integer primary key,
+ "name" Varchar NOT NULL
+);
+
Create table "stm_action"
(
- "action_id" SERIAL,
+ "action_id" Integer,
"action_name" Varchar NOT NULL,
+ "allowed" BOOLEAN NOT NULL DEFAULT TRUE,
primary key ("action_id")
);
@@ -524,7 +573,7 @@ Create table "stm_color"
Create table "stm_dev_typ"
(
- "dev_typ_id" SERIAL,
+ "dev_typ_id" Integer,
"dev_typ_manufacturer" Varchar,
"dev_typ_name" Varchar NOT NULL,
"dev_typ_version" Varchar NOT NULL,
@@ -542,7 +591,7 @@ Create table "stm_dev_typ"
Create table "stm_obj_typ"
(
- "obj_typ_id" SERIAL,
+ "obj_typ_id" Integer,
"obj_typ_name" Varchar NOT NULL,
"obj_typ_comment" Text,
primary key ("obj_typ_id")
@@ -550,7 +599,7 @@ Create table "stm_obj_typ"
Create table "stm_track"
(
- "track_id" SERIAL,
+ "track_id" Integer,
"track_name" Varchar NOT NULL,
primary key ("track_id")
);
@@ -600,6 +649,7 @@ Create table "import_control"
"import_errors" Varchar,
"notification_done" Boolean NOT NULL Default FALSE,
"security_relevant_changes_counter" INTEGER NOT NULL Default 0,
+ "is_full_import" BOOLEAN DEFAULT FALSE,
primary key ("control_id")
);
@@ -607,7 +657,6 @@ Create table "import_control"
CREATE table "import_config" (
"import_id" bigint NOT NULL,
"mgm_id" integer NOT NULL,
- "chunk_number" integer,
"config" jsonb NOT NULL,
"start_import_flag" Boolean NOT NULL Default FALSE,
"debug_mode" Boolean Default FALSE
@@ -622,6 +671,13 @@ CREATE TABLE "import_full_config" (
PRIMARY KEY ("import_id")
);
+CREATE TABLE IF NOT EXISTS "latest_config" (
+ "mgm_id" integer NOT NULL,
+ "import_id" bigint NOT NULL,
+ "config" jsonb NOT NULL,
+ PRIMARY KEY ("mgm_id")
+);
+
-- temporary import tables -------------------------------------
Create table "import_service"
@@ -912,7 +968,7 @@ Create table "changelog_rule"
"documented" Boolean NOT NULL Default FALSE,
"docu_time" Timestamp,
"mgm_id" Integer NOT NULL,
- "dev_id" Integer NOT NULL,
+ "dev_id" Integer,
"change_type_id" Integer NOT NULL Default 3,
"security_relevant" Boolean NOT NULL Default TRUE,
"change_request_info" Varchar,
@@ -923,7 +979,7 @@ Create table "changelog_rule"
Create table "stm_change_type"
(
- "change_type_id" SERIAL,
+ "change_type_id" Integer,
"change_type_name" Varchar,
primary key ("change_type_id")
);
@@ -1127,6 +1183,40 @@ create table recertification
owner_recert_id bigint
);
+Create Table IF NOT EXISTS "rule_enforced_on_gateway"
+(
+ "rule_id" Integer NOT NULL,
+ "dev_id" Integer, -- NULL if rule is available for all gateways of its management
+ "created" BIGINT,
+ "removed" BIGINT
+);
+
+Create table IF NOT EXISTS "rulebase"
+(
+ "id" SERIAL primary key,
+ "name" Varchar NOT NULL,
+ "uid" Varchar NOT NULL,
+ "mgm_id" Integer NOT NULL,
+ "is_global" BOOLEAN DEFAULT FALSE NOT NULL,
+ "created" BIGINT,
+ "removed" BIGINT
+);
+
+Create table IF NOT EXISTS "rulebase_link"
+(
+ "id" SERIAL primary key,
+ "gw_id" Integer,
+ "from_rulebase_id" Integer, -- either from_rulebase_id or from_rule_id must be SET or the is_initial flag
+ "from_rule_id" BIGINT,
+ "to_rulebase_id" Integer NOT NULL,
+ "link_type" Integer,
+ "is_initial" BOOLEAN DEFAULT FALSE,
+ "is_global" BOOLEAN DEFAULT FALSE,
+ "is_section" BOOLEAN DEFAULT TRUE,
+ "created" BIGINT,
+ "removed" BIGINT
+);
+
create table owner_recertification
(
id BIGSERIAL PRIMARY KEY,
@@ -1384,13 +1474,22 @@ create table compliance.network_zone
name VARCHAR NOT NULL,
description VARCHAR NOT NULL,
super_network_zone_id bigint,
- owner_id bigint
+ owner_id bigint,
+ removed timestamp with time zone,
+ created timestamp with time zone default now(),
+ criterion_id INT,
+ id_string TEXT,
+ is_auto_calculated_internet_zone BOOLEAN DEFAULT FALSE,
+ is_auto_calculated_undefined_internal_zone BOOLEAN DEFAULT FALSE
);
create table compliance.network_zone_communication
(
+ criterion_id INT,
from_network_zone_id bigint NOT NULL,
- to_network_zone_id bigint NOT NULL
+ to_network_zone_id bigint NOT NULL,
+ removed timestamp with time zone,
+ created timestamp with time zone default now()
);
create table compliance.ip_range
@@ -1398,9 +1497,68 @@ create table compliance.ip_range
network_zone_id bigint NOT NULL,
ip_range_start inet NOT NULL,
ip_range_end inet NOT NULL,
- PRIMARY KEY(network_zone_id, ip_range_start, ip_range_end)
+ PRIMARY KEY(network_zone_id, ip_range_start, ip_range_end, created),
+ removed timestamp with time zone,
+ created timestamp with time zone default now(),
+ criterion_id INT,
+ name TEXT
+);
+
+create table compliance.policy
+(
+ id SERIAL PRIMARY KEY,
+ name TEXT,
+ created_date timestamp default now(),
+ disabled bool
+);
+
+create table compliance.policy_criterion
+(
+ policy_id INT NOT NULL,
+ criterion_id INT NOT NULL,
+ removed timestamp with time zone,
+ created timestamp with time zone default now()
);
+create table compliance.criterion
+(
+ id SERIAL PRIMARY KEY,
+ name TEXT,
+ comment TEXT,
+ criterion_type TEXT,
+ content TEXT,
+ removed timestamp with time zone,
+ created timestamp with time zone default now(),
+ import_source TEXT
+);
+
+create table compliance.violation
+(
+ id BIGSERIAL PRIMARY KEY,
+ rule_id bigint NOT NULL,
+ rule_uid TEXT,
+ mgmt_uid TEXT,
+ found_date timestamp with time zone default now(),
+ removed_date timestamp with time zone,
+ details TEXT,
+ risk_score real,
+ policy_id INT NOT NULL,
+ criterion_id INT NOT NULL
+);
+
+-- create table compliance.assessability_issue
+-- (
+-- violation_id BIGINT NOT NULL,
+-- type_id INT NOT NULL,
+-- PRIMARY KEY(violation_id, type_id)
+-- );
+
+-- create table compliance.assessability_issue_type
+-- (
+-- type_id INT PRIMARY KEY,
+-- type_name VARCHAR(50) NOT NULL
+-- );
+
--- Network modelling ---
create schema modelling;
diff --git a/roles/database/files/sql/creation/fworch-create-triggers.sql b/roles/database/files/sql/creation/fworch-create-triggers.sql
index ac64c4f777..177b3540b7 100644
--- a/roles/database/files/sql/creation/fworch-create-triggers.sql
+++ b/roles/database/files/sql/creation/fworch-create-triggers.sql
@@ -72,21 +72,27 @@ CREATE OR REPLACE FUNCTION import_config_from_json ()
AS $BODY$
DECLARE
i_mgm_id INTEGER;
+ i_count INTEGER;
BEGIN
-- networking
- IF NEW.chunk_number=0 THEN -- delete all networking data only when starting import, not for each chunk
- SELECT INTO i_mgm_id mgm_id FROM import_control WHERE control_id=NEW.import_id;
- -- before importing, delete all old interfaces and routes belonging to the current management:
- DELETE FROM gw_route WHERE routing_device IN
- (SELECT dev_id FROM device LEFT JOIN management ON (device.mgm_id=management.mgm_id) WHERE management.mgm_id=i_mgm_id);
+ SELECT INTO i_mgm_id mgm_id FROM import_control WHERE control_id=NEW.import_id;
+ -- before importing, delete all old interfaces and routes belonging to the current management:
+
+ -- now re-insert the currently found interfaces:
+ SELECT INTO i_count COUNT(*) FROM jsonb_populate_recordset(NULL::gw_interface, NEW.config -> 'interfaces');
+ IF COUNT>0 THEN
DELETE FROM gw_interface WHERE routing_device IN
(SELECT dev_id FROM device LEFT JOIN management ON (device.mgm_id=management.mgm_id) WHERE management.mgm_id=i_mgm_id);
+ INSERT INTO gw_interface SELECT * FROM jsonb_populate_recordset(NULL::gw_interface, NEW.config -> 'interfaces');
END IF;
- -- now re-insert the currently found interfaces:
- INSERT INTO gw_interface SELECT * FROM jsonb_populate_recordset(NULL::gw_interface, NEW.config -> 'interfaces');
- -- now re-insert the currently found routes:
- INSERT INTO gw_route SELECT * FROM jsonb_populate_recordset(NULL::gw_route, NEW.config -> 'routing');
+ SELECT INTO i_count COUNT(*) FROM jsonb_populate_recordset(NULL::gw_route, NEW.config -> 'routing');
+ IF COUNT>0 THEN
+ DELETE FROM gw_route WHERE routing_device IN
+ (SELECT dev_id FROM device LEFT JOIN management ON (device.mgm_id=management.mgm_id) WHERE management.mgm_id=i_mgm_id);
+ -- now re-insert the currently found routes:
+ INSERT INTO gw_route SELECT * FROM jsonb_populate_recordset(NULL::gw_route, NEW.config -> 'routing');
+ END IF;
-- firewall objects and rules
diff --git a/roles/database/files/sql/creation/fworch-fill-stm.sql b/roles/database/files/sql/creation/fworch-fill-stm.sql
index 67bc259220..6c121ac3f4 100644
--- a/roles/database/files/sql/creation/fworch-fill-stm.sql
+++ b/roles/database/files/sql/creation/fworch-fill-stm.sql
@@ -139,13 +139,44 @@ insert into config (config_key, config_value, config_user) VALUES ('manageOwnerL
insert into config (config_key, config_value, config_user) VALUES ('modModelledMarker', 'FWOC', 0);
insert into config (config_key, config_value, config_user) VALUES ('modModelledMarkerLocation', 'rulename', 0);
insert into config (config_key, config_value, config_user) VALUES ('ruleRecognitionOption', '{"nwRegardIp":true,"nwRegardName":false,"nwRegardGroupName":false,"nwResolveGroup":false,"svcRegardPortAndProt":true,"svcRegardName":false,"svcRegardGroupName":false,"svcResolveGroup":true,"svcSplitPortRanges":false}', 0);
-insert into config (config_key, config_value, config_user) VALUES ('availableReportTypes', '[1,2,3,4,5,6,7,8,9,10,21,22]', 0);
+insert into config (config_key, config_value, config_user) VALUES ('availableReportTypes', '[1,2,3,4,5,6,7,8,9,10,21,22,31,32]', 0);
insert into config (config_key, config_value, config_user) VALUES ('varianceAnalysisSleepTime', '0', 0);
insert into config (config_key, config_value, config_user) VALUES ('varianceAnalysisStartAt', '00:00:00', 0);
insert into config (config_key, config_value, config_user) VALUES ('varianceAnalysisSync', 'false', 0);
insert into config (config_key, config_value, config_user) VALUES ('varianceAnalysisRefresh', 'false', 0);
insert into config (config_key, config_value, config_user) VALUES ('resolveNetworkAreas', 'False', 0);
+insert into config (config_key, config_value, config_user) VALUES ('complianceCheckSleepTime', '0', 0);
+insert into config (config_key, config_value, config_user) VALUES ('complianceCheckStartAt', '00:00:00', 0);
+insert into config (config_key, config_value, config_user) VALUES ('complianceCheckPolicy', '0', 0);
+insert into config (config_key, config_value, config_user) VALUES ('complianceCheckMaxPrintedViolations', '0', 0);
+insert into config (config_key, config_value, config_user) VALUES ('complianceCheckSortMatrixByID', 'false', 0);
insert into config (config_key, config_value, config_user) VALUES ('availableModules', '[1,2,3,4,5,6]', 0);
+insert into config (config_key, config_value, config_user) VALUES ('debugConfig', '{"debugLevel":8, "extendedLogComplianceCheck":true, "extendedLogReportGeneration":true, "extendedLogScheduler":true}', 0);
+insert into config (config_key, config_value, config_user) VALUES ('reportSchedulerConfig', '', 0);
+insert into config (config_key, config_value, config_user) VALUES ('autoCalculateInternetZone', 'true', 0);
+insert into config (config_key, config_value, config_user) VALUES ('autoCalculateUndefinedInternalZone', 'true', 0);
+insert into config (config_key, config_value, config_user) VALUES ('internalZoneRange_10_0_0_0_8', 'true', 0);
+insert into config (config_key, config_value, config_user) VALUES ('internalZoneRange_172_16_0_0_12', 'true', 0);
+insert into config (config_key, config_value, config_user) VALUES ('internalZoneRange_192_168_0_0_16', 'true', 0);
+insert into config (config_key, config_value, config_user) VALUES ('internalZoneRange_0_0_0_0_8', 'true', 0);
+insert into config (config_key, config_value, config_user) VALUES ('internalZoneRange_127_0_0_0_8', 'true', 0);
+insert into config (config_key, config_value, config_user) VALUES ('internalZoneRange_169_254_0_0_16', 'true', 0);
+insert into config (config_key, config_value, config_user) VALUES ('internalZoneRange_224_0_0_0_4', 'true', 0);
+insert into config (config_key, config_value, config_user) VALUES ('internalZoneRange_240_0_0_0_4', 'true', 0);
+insert into config (config_key, config_value, config_user) VALUES ('internalZoneRange_255_255_255_255_32', 'true', 0);
+insert into config (config_key, config_value, config_user) VALUES ('internalZoneRange_192_0_2_0_24', 'true', 0);
+insert into config (config_key, config_value, config_user) VALUES ('internalZoneRange_198_51_100_0_24', 'true', 0);
+insert into config (config_key, config_value, config_user) VALUES ('internalZoneRange_203_0_113_0_24', 'true', 0);
+insert into config (config_key, config_value, config_user) VALUES ('internalZoneRange_100_64_0_0_10', 'true', 0);
+insert into config (config_key, config_value, config_user) VALUES ('internalZoneRange_192_0_0_0_24', 'true', 0);
+insert into config (config_key, config_value, config_user) VALUES ('internalZoneRange_192_88_99_0_24', 'true', 0);
+insert into config (config_key, config_value, config_user) VALUES ('internalZoneRange_198_18_0_0_15', 'true', 0);
+insert into config (config_key, config_value, config_user) VALUES ('autoCalculatedZonesAtTheEnd', 'true', 0);
+insert into config (config_key, config_value, config_user) VALUES ('treatDynamicAndDomainObjectsAsInternet', 'true', 0);
+insert into config (config_key, config_value, config_user) VALUES ('showShortColumnsInComplianceReports', 'true', 0);
+insert into config (config_key, config_value, config_user) VALUES ('importedMatrixReadOnly', 'true', 0);
+insert into config (config_key, config_value, config_user) VALUES ('complianceCheckElementsPerFetch', '500', 0);
+insert into config (config_key, config_value, config_user) VALUES ('complianceCheckAvailableProcessors', '4', 0);
INSERT INTO "report_format" ("report_format_name") VALUES ('json');
INSERT INTO "report_format" ("report_format_name") VALUES ('pdf');
@@ -266,6 +297,44 @@ INSERT INTO "report_template" ("report_filter","report_template_name","report_te
"recertOwnerList": [],
"recertShowAnyMatch": true,
"recertificationDisplayPeriod": 30}}');
+INSERT INTO "report_template" ("report_filter","report_template_name","report_template_comment","report_template_owner", "report_parameters")
+ VALUES ('action=accept',
+ 'Compliance: Unresolved violations','T0108', 0,
+ '{"report_type":31,"device_filter":{"management":[]},
+ "time_filter": {
+ "is_shortcut": true,
+ "shortcut": "now",
+ "report_time": "2022-01-01T00:00:00.0000000+01:00",
+ "timerange_type": "SHORTCUT",
+ "shortcut_range": "this year",
+ "offset": 0,
+ "interval": "DAYS",
+ "start_time": "2022-01-01T00:00:00.0000000+01:00",
+ "end_time": "2022-01-01T00:00:00.0000000+01:00",
+ "open_start": false,
+ "open_end": false},
+ "compliance_filter": {
+ "diff_reference_in_days": 0,
+ "show_non_impact_rules": true}}');
+INSERT INTO "report_template" ("report_filter","report_template_name","report_template_comment","report_template_owner", "report_parameters")
+ VALUES ('action=accept',
+ 'Compliance: Diffs','T0109', 0,
+ '{"report_type":32,"device_filter":{"management":[]},
+ "time_filter": {
+ "is_shortcut": true,
+ "shortcut": "now",
+ "report_time": "2022-01-01T00:00:00.0000000+01:00",
+ "timerange_type": "SHORTCUT",
+ "shortcut_range": "this year",
+ "offset": 0,
+ "interval": "DAYS",
+ "start_time": "2022-01-01T00:00:00.0000000+01:00",
+ "end_time": "2022-01-01T00:00:00.0000000+01:00",
+ "open_start": false,
+ "open_end": false},
+ "compliance_filter": {
+ "diff_reference_in_days": 7,
+ "show_non_impact_rules": false}}');
insert into parent_rule_type (id, name) VALUES (1, 'section'); -- do not restart numbering
insert into parent_rule_type (id, name) VALUES (2, 'guarded-layer'); -- restart numbering, rule restrictions are ANDed to all rules below it, layer is not entered if guard does not apply
@@ -302,14 +371,15 @@ insert into stm_obj_typ (obj_typ_id,obj_typ_name) VALUES (17,'voip_sip');
insert into stm_obj_typ (obj_typ_id,obj_typ_name) VALUES (18,'simple-gateway');
insert into stm_obj_typ (obj_typ_id,obj_typ_name) VALUES (19,'external-gateway');
insert into stm_obj_typ (obj_typ_id,obj_typ_name) VALUES (20,'voip'); -- general voip object replacing old specific ones and including CpmiVoipSipDomain
+insert into stm_obj_typ (obj_typ_id,obj_typ_name) VALUES (21,'access-role');
insert into stm_action (action_id,action_name) VALUES (1,'accept'); -- cp, fortinet
-insert into stm_action (action_id,action_name) VALUES (2,'drop'); -- cp
-insert into stm_action (action_id,action_name) VALUES (3,'deny'); -- netscreen, fortinet
+insert into stm_action (action_id,action_name, allowed) VALUES (2,'drop', FALSE); -- cp
+insert into stm_action (action_id,action_name, allowed) VALUES (3,'deny', FALSE); -- netscreen, fortinet
insert into stm_action (action_id,action_name) VALUES (4,'access'); -- netscreen
insert into stm_action (action_id,action_name) VALUES (5,'client encrypt'); -- cp
insert into stm_action (action_id,action_name) VALUES (6,'client auth'); -- cp
-insert into stm_action (action_id,action_name) VALUES (7,'reject'); -- cp
+insert into stm_action (action_id,action_name, allowed) VALUES (7,'reject', FALSE); -- cp
insert into stm_action (action_id,action_name) VALUES (8,'encrypt'); -- cp
insert into stm_action (action_id,action_name) VALUES (9,'user auth'); -- cp
insert into stm_action (action_id,action_name) VALUES (10,'session auth'); -- cp
@@ -358,23 +428,22 @@ insert into stm_track (track_id,track_name) VALUES (17,'count alarm');
insert into stm_track (track_id,track_name) VALUES (18,'all');
insert into stm_track (track_id,track_name) VALUES (19,'all start');
insert into stm_track (track_id,track_name) VALUES (20,'utm');
-insert into stm_track (track_id,track_name) VALUES (22,'utm start');
-
--- new mixed:
-insert into stm_track (track_id,track_name) VALUES (21,'network log'); -- check point R8x:
-insert into stm_track (track_id,track_name) VALUES (23,'detailed log') ON CONFLICT DO NOTHING; -- check point R8x:
-insert into stm_track (track_id,track_name) VALUES (24,'extended log') ON CONFLICT DO NOTHING; -- check point R8x:
+-- mixed (continuous):
+insert into stm_track (track_id,track_name) VALUES (21,'network log'); -- check point R8x
+insert into stm_track (track_id,track_name) VALUES (22,'utm start'); -- fortinet
+insert into stm_track (track_id,track_name) VALUES (23,'detailed log'); -- check point R8x
+insert into stm_track (track_id,track_name) VALUES (24,'extended log'); -- check point R8x
-insert into stm_dev_typ (dev_typ_id,dev_typ_name,dev_typ_version,dev_typ_manufacturer,dev_typ_predef_svc,dev_typ_is_mgmt,is_pure_routing_device)
- VALUES (2,'Netscreen','5.x-6.x','Netscreen', '', true,false);
-insert into stm_dev_typ (dev_typ_id,dev_typ_name,dev_typ_version,dev_typ_manufacturer,dev_typ_predef_svc,dev_typ_is_mgmt,is_pure_routing_device)
- VALUES (4,'FortiGateStandalone','5ff','Fortinet','', true,false);
+-- insert into stm_dev_typ (dev_typ_id,dev_typ_name,dev_typ_version,dev_typ_manufacturer,dev_typ_predef_svc,dev_typ_is_mgmt,is_pure_routing_device)
+-- VALUES (2,'Netscreen','5.x-6.x','Netscreen', '', true,false);
+-- insert into stm_dev_typ (dev_typ_id,dev_typ_name,dev_typ_version,dev_typ_manufacturer,dev_typ_predef_svc,dev_typ_is_mgmt,is_pure_routing_device)
+-- VALUES (4,'FortiGateStandalone','5ff','Fortinet','', true,false);
insert into stm_dev_typ (dev_typ_id,dev_typ_name,dev_typ_version,dev_typ_manufacturer,dev_typ_predef_svc,dev_typ_is_mgmt,is_pure_routing_device)
VALUES (5,'Barracuda Firewall Control Center','Vx','phion','',true,false);
insert into stm_dev_typ (dev_typ_id,dev_typ_name,dev_typ_version,dev_typ_manufacturer,dev_typ_predef_svc,dev_typ_is_mgmt,is_pure_routing_device)
VALUES (6,'phion netfence','3.x','phion','', false,false);
-insert into stm_dev_typ (dev_typ_id,dev_typ_name,dev_typ_version,dev_typ_manufacturer,dev_typ_predef_svc,dev_typ_is_mgmt,is_pure_routing_device)
- VALUES (7,'Check Point','R5x-R7x','Check Point','', true,false);
+-- insert into stm_dev_typ (dev_typ_id,dev_typ_name,dev_typ_version,dev_typ_manufacturer,dev_typ_predef_svc,dev_typ_is_mgmt,is_pure_routing_device)
+-- VALUES (7,'Check Point','R5x-R7x','Check Point','', true,false);
insert into stm_dev_typ (dev_typ_id,dev_typ_name,dev_typ_version,dev_typ_manufacturer,dev_typ_predef_svc,dev_typ_is_mgmt,is_pure_routing_device)
VALUES (8,'JUNOS','10-21','Juniper','any;0;0;65535;;junos-predefined-service;simple;', true,false);
insert into stm_dev_typ (dev_typ_id,dev_typ_name,dev_typ_version,dev_typ_manufacturer,dev_typ_predef_svc,dev_typ_is_mgmt,is_pure_routing_device)
@@ -415,176 +484,10 @@ insert into stm_dev_typ (dev_typ_id,dev_typ_name,dev_typ_version,dev_typ_manufac
VALUES (26,'NSX','REST','VMWare','',false,true,false) ON CONFLICT DO NOTHING;
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 (27,'NSX DFW Gateway','REST','VMWare','',false,false,false) ON CONFLICT DO NOTHING;
-
-
-update stm_dev_typ set dev_typ_predef_svc=
-'ANY;0;0;65535;1;other;simple
-AOL;6;5190;5194;30;remote;simple
-APPLE-ICHAT-SNATMAP;17;5678;5678;1;other;simple
-BGP;6;179;179;30;other;simple
-CHARGEN;17;19;19;1;other;simple
-DHCP-Relay;17;67;67;1;info seeking;simple
-DISCARD;17;9;9;1;other;simple
-DNS;17;53;53;1;info seeking;simple
-ECHO;17;7;7;1;other;simple
-FINGER;6;79;79;30;info seeking;simple
-FTP;6;21;21;30;remote;simple
-FTP-Get;6;21;21;30;remote;simple
-FTP-Put;6;21;21;30;remote;simple
-GNUTELLA;17;6346;6347;1;remote;simple
-GOPHER;6;70;70;30;info seeking;simple
-GRE;47;0;65535;60;remote;simple
-GTP;6;3386;3386;30;remote;simple
-H.323;6;1720;1720;30;remote;simple
-HTTP;6;80;80;5;info seeking;simple
-HTTP-EXT;6;8000;8001;5;info seeking;simple
-HTTPS;6;443;443;30;security;simple
-ICMP Address Mask;1;0;65535;1;other;simple
-ICMP Dest Unreachable;1;0;65535;1;other;simple
-ICMP Fragment Needed;1;0;65535;1;other;simple
-ICMP Fragment Reassembly;1;0;65535;1;other;simple
-ICMP Host Unreachable;1;0;65535;1;other;simple
-ICMP Parameter Problem;1;0;65535;1;other;simple
-ICMP Port Unreachable;1;0;65535;1;other;simple
-ICMP Protocol Unreach;1;0;65535;1;other;simple
-ICMP Redirect;1;0;65535;1;other;simple
-ICMP Redirect Host;1;0;65535;1;other;simple
-ICMP Redirect TOS & Host;1;0;65535;1;other;simple
-ICMP Redirect TOS & Net;1;0;65535;1;other;simple
-ICMP Source Quench;1;0;65535;1;other;simple
-ICMP Source Route Fail;1;0;65535;1;other;simple
-ICMP Time Exceeded;1;0;65535;1;other;simple
-ICMP-ANY;1;0;65535;1;other;simple
-ICMP-INFO;1;0;65535;1;other;simple
-ICMP-TIMESTAMP;1;0;65535;1;other;simple
-ICMP6 Dst Unreach addr;58;0;65535;30;other;simple
-ICMP6 Dst Unreach admin;58;0;65535;30;other;simple
-ICMP6 Dst Unreach beyond;58;0;65535;30;other;simple
-ICMP6 Dst Unreach port;58;0;65535;30;other;simple
-ICMP6 Dst Unreach route;58;0;65535;30;other;simple
-ICMP6 Echo Reply;58;0;65535;30;other;simple
-ICMP6 Echo Request;58;0;65535;30;other;simple
-ICMP6 HAAD Reply;58;0;65535;30;other;simple
-ICMP6 HAAD Request;58;0;65535;30;other;simple
-ICMP6 MP Advertisement;58;0;65535;30;other;simple
-ICMP6 MP Solicitation;58;0;65535;30;other;simple
-ICMP6 Packet Too Big;58;0;65535;30;other;simple
-ICMP6 Param Prob header;58;0;65535;30;other;simple
-ICMP6 Param Prob nexthdr;58;0;65535;30;other;simple
-ICMP6 Param Prob option;58;0;65535;30;other;simple
-ICMP6 Time Exceed reasse;58;0;65535;30;other;simple
-ICMP6 Time Exceed transi;58;0;65535;30;other;simple
-ICMP6-ANY;58;0;65535;30;other;simple
-IDENT;6;113;113;30;other;simple
-IKE;17;500;500;1;security;simple
-IKE-NAT;17;500;500;3;security;simple
-IMAP;6;143;143;30;email;simple
-Internet Locator Service;6;389;389;30;info seeking;simple
-IRC;6;6660;6669;30;remote;simple
-L2TP;17;1701;1701;1;remote;simple
-LDAP;6;389;389;30;info seeking;simple
-LPR;6;515;515;30;other;simple
-MAIL;6;25;25;30;email;simple
-MGCP-CA;17;2727;2727;120;other;simple
-MGCP-UA;17;2427;2427;120;other;simple
-MS-AD-BR;;;;1;other;rpc
-MS-AD-DRSUAPI;;;;1;other;rpc
-MS-AD-DSROLE;;;;1;other;rpc
-MS-AD-DSSETUP;;;;1;other;rpc
-MS-DTC;;;;1;other;rpc
-MS-EXCHANGE-DATABASE;;;;30;other;rpc
-MS-EXCHANGE-DIRECTORY;;;;30;other;rpc
-MS-EXCHANGE-INFO-STORE;;;;30;other;rpc
-MS-EXCHANGE-MTA;;;;30;other;rpc
-MS-EXCHANGE-STORE;;;;30;other;rpc
-MS-EXCHANGE-SYSATD;;;;30;other;rpc
-MS-FRS;;;;1;other;rpc
-MS-IIS-COM;;;;30;other;rpc
-MS-IIS-IMAP4;;;;1;other;rpc
-MS-IIS-INETINFO;;;;1;other;rpc
-MS-IIS-NNTP;;;;1;other;rpc
-MS-IIS-POP3;;;;1;other;rpc
-MS-IIS-SMTP;;;;1;other;rpc
-MS-ISMSERV;;;;1;other;rpc
-MS-MESSENGER;;;;30;other;rpc
-MS-MQQM;;;;1;other;rpc
-MS-NETLOGON;;;;1;other;rpc
-MS-RPC-ANY;;;;1;other;rpc
-MS-RPC-EPM;17;135;135;30;remote;simple
-MS-SCHEDULER;;;;1;other;rpc
-MS-SQL;6;1433;1433;30;other;simple
-MS-WIN-DNS;;;;1;other;rpc
-MS-WINS;;;;1;other;rpc
-MS-WMIC;;;;30;other;rpc
-MSN;6;1863;1863;30;remote;simple
-NBDS;17;138;138;1;remote;simple
-NBNAME;17;137;137;1;remote;simple
-NetMeeting;6;1720;1720;30;remote;simple
-NFS;17;111;111;40;remote;simple
-NNTP;6;119;119;30;info seeking;simple
-NS Global;6;15397;15397;30;remote;simple
-NS Global PRO;6;15397;15397;30;remote;simple
-NSM;17;69;69;1;other;simple
-NTP;17;123;123;1;other;simple
-OSPF;89;0;65535;1;other;simple
-PC-Anywhere;17;5632;5632;1;remote;simple
-PING;1;0;65535;1;other;simple
-PINGv6;58;0;65535;30;other;simple
-POP3;6;110;110;30;email;simple
-PPTP;6;1723;1723;30;security;simple
-RADIUS;17;1812;1813;1;other;simple
-Real Media;6;7070;7070;30;info seeking;simple
-REXEC;6;512;512;30;remote;simple
-RIP;17;520;520;1;other;simple
-RLOGIN;6;513;513;30;remote;simple
-RSH;6;514;514;30;remote;simple
-RTSP;6;554;554;30;info seeking;simple
-SCCP;6;2000;2000;30;other;simple
-SCTP-ANY;132;0;65535;1;other;simple
-SIP;17;5060;5060;1;other;simple
-SMB;6;139;139;30;remote;simple
-SMTP;6;25;25;30;email;simple
-SNMP;17;161;161;1;other;simple
-SQL Monitor;17;1434;1434;1;other;simple
-SQL*Net V1;6;1525;1525;30;other;simple
-SQL*Net V2;6;1521;1521;30;other;simple
-SSH;6;22;22;30;security;simple
-SUN-RPC;;;;1;other;rpc
-SUN-RPC-ANY;;;;1;other;rpc
-SUN-RPC-MOUNTD;;;;30;other;rpc
-SUN-RPC-NFS;;;;40;other;rpc
-SUN-RPC-NLOCKMGR;;;;1;other;rpc
-SUN-RPC-PORTMAPPER;17;111;111;40;remote;simple
-SUN-RPC-RQUOTAD;;;;30;other;rpc
-SUN-RPC-RSTATD;;;;30;other;rpc
-SUN-RPC-RUSERD;;;;30;other;rpc
-SUN-RPC-SADMIND;;;;30;other;rpc
-SUN-RPC-SPRAYD;;;;30;other;rpc
-SUN-RPC-STATUS;;;;30;other;rpc
-SUN-RPC-WALLD;;;;30;other;rpc
-SUN-RPC-YPBIND;;;;30;other;rpc
-SYSLOG;17;514;514;1;other;simple
-TALK;17;517;518;1;other;simple
-TCP-ANY;6;0;65535;30;other;simple
-TELNET;6;23;23;30;remote;simple
-TFTP;17;69;69;1;remote;simple
-TRACEROUTE;1;0;65535;1;other;simple
-UDP-ANY;17;0;65535;1;other;simple
-UUCP;17;540;540;1;remote;simple
-VDO Live;6;7000;7010;30;info seeking;simple
-VNC;6;5800;5800;30;other;simple
-WAIS;6;210;210;30;info seeking;simple
-WHOIS;6;43;43;30;info seeking;simple
-WINFRAME;6;1494;1494;30;remote;simple
-X-WINDOWS;6;6000;6063;30;remote;simple
-YMSG;6;5050;5050;30;remote;simple
-APPLE-ICHAT;;;;;Apple iChat Services Group;group;AOL|APPLE-ICHAT-SNATMAP|DNS|HTTP|HTTPS|SIP
-MGCP;;;;;Media Gateway Control Protoc;group;MGCP-CA|MGCP-UA
-MS-AD;;;;;Microsoft Active Directory;group;MS-AD-BR|MS-AD-DRSUAPI|MS-AD-DSROLE|MS-AD-DSSETUP
-MS-EXCHANGE;;;;;Microsoft Exchange;group;MS-EXCHANGE-DIRECTORY|MS-EXCHANGE-INFO-STORE|MS-EXCHANGE-MTA|MS-EXCHANGE-STORE|MS-EXCHANGE-SYSATD
-MS-IIS;;;;;Microsoft IIS Server;group;MS-IIS-COM|MS-IIS-IMAP4|MS-IIS-INETINFO|MS-IIS-NNTP|MS-IIS-POP3|MS-IIS-SMTP
-VOIP;;;;;VOIP Service Group;group;H.323|MGCP-CA|MGCP-UA|SCCP|SIP'
-where dev_typ_id=2;
+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 (28,'Cisco Asa','9','Cisco','',false,true,false);
+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);
-- SET statement_timeout = 0;
-- SET client_encoding = 'UTF8';
@@ -635,3 +538,13 @@ insert into request.state (id,name) VALUES (620,'Discarded');
INSERT INTO owner (id, name, dn, group_dn, is_default, recert_interval, app_id_external)
VALUES (0, 'super-owner', 'uid=admin,ou=tenant0,ou=operator,ou=user,dc=fworch,dc=internal', 'group-dn-for-super-owner', true, 365, 'NONE')
ON CONFLICT DO NOTHING;
+
+insert into stm_link_type (id, name) VALUES (2, 'ordered');
+insert into stm_link_type (id, name) VALUES (3, 'inline');
+insert into stm_link_type (id, name) VALUES (4, 'concatenated');
+insert into stm_link_type (id, name) VALUES (5, 'domain');
+
+-- insert into compliance.assessability_issue_type (type_id, type_name) VALUES (1, 'empty group');
+-- insert into compliance.assessability_issue_type (type_id, type_name) VALUES (2, 'broadcast address');
+-- insert into compliance.assessability_issue_type (type_id, type_name) VALUES (3, 'DHCP IP undefined address');
+-- insert into compliance.assessability_issue_type (type_id, type_name) VALUES (4, 'dynamic internet address');
diff --git a/roles/database/files/sql/creation/fworch-views-materialized.sql b/roles/database/files/sql/creation/fworch-views-materialized.sql
index d37c354af3..3cfacac9f4 100644
--- a/roles/database/files/sql/creation/fworch-views-materialized.sql
+++ b/roles/database/files/sql/creation/fworch-views-materialized.sql
@@ -36,7 +36,7 @@ CREATE OR REPLACE VIEW v_rule_with_rule_owner AS
SELECT r.rule_id, ow.id as owner_id, ow.name as owner_name, 'rule' AS matches,
ow.recert_interval, met.rule_last_certified, met.rule_last_certifier
FROM v_active_access_allow_rules r
- LEFT JOIN rule_metadata met ON (r.rule_uid=met.rule_uid AND r.dev_id=met.dev_id)
+ LEFT JOIN rule_metadata met ON (r.rule_uid=met.rule_uid)
LEFT JOIN rule_owner ro ON (ro.rule_metadata_id=met.rule_metadata_id)
LEFT JOIN owner ow ON (ro.owner_id=ow.id)
WHERE NOT ow.id IS NULL
@@ -89,7 +89,7 @@ CREATE OR REPLACE VIEW v_rule_with_src_owner AS
LEFT JOIN object o ON (of.objgrp_flat_member_id=o.obj_id)
LEFT JOIN owner_network onw ON (onw.ip_end >= o.obj_ip AND onw.ip <= o.obj_ip_end)
LEFT JOIN owner ow ON (onw.owner_id=ow.id)
- LEFT JOIN rule_metadata met ON (r.rule_uid=met.rule_uid AND r.dev_id=met.dev_id)
+ LEFT JOIN rule_metadata met ON (r.rule_uid=met.rule_uid)
WHERE r.rule_id NOT IN (SELECT distinct rwo.rule_id FROM v_rule_with_rule_owner rwo) AND
CASE
when (select mode from v_rule_ownership_mode) = 'exclusive' then (NOT o.obj_ip IS NULL) AND o.obj_ip NOT IN (select * from v_excluded_src_ips)
@@ -121,7 +121,7 @@ CREATE OR REPLACE VIEW v_rule_with_dst_owner AS
LEFT JOIN object o ON (of.objgrp_flat_member_id=o.obj_id)
LEFT JOIN owner_network onw ON (onw.ip_end >= o.obj_ip AND onw.ip <= o.obj_ip_end)
LEFT JOIN owner ow ON (onw.owner_id=ow.id)
- LEFT JOIN rule_metadata met ON (r.rule_uid=met.rule_uid AND r.dev_id=met.dev_id)
+ LEFT JOIN rule_metadata met ON (r.rule_uid=met.rule_uid)
WHERE r.rule_id NOT IN (SELECT distinct rwo.rule_id FROM v_rule_with_rule_owner rwo) AND
CASE
when (select mode from v_rule_ownership_mode) = 'exclusive' then (NOT o.obj_ip IS NULL) AND o.obj_ip NOT IN (select * from v_excluded_dst_ips)
@@ -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-api-funcs.sql b/roles/database/files/sql/idempotent/fworch-api-funcs.sql
index ae728f8093..9a482bdcd4 100644
--- a/roles/database/files/sql/idempotent/fworch-api-funcs.sql
+++ b/roles/database/files/sql/idempotent/fworch-api-funcs.sql
@@ -582,6 +582,7 @@ AS $function$
END;
$function$;
+
CREATE OR REPLACE FUNCTION get_rules_for_owner(device_row device, ownerid integer)
RETURNS SETOF rule AS $$
BEGIN
@@ -604,3 +605,47 @@ RETURNS SETOF rule AS $$
ORDER BY rule_name;
END;
$$ LANGUAGE 'plpgsql' STABLE;
+
+
+CREATE OR REPLACE FUNCTION public.get_rulebase_for_owner(rulebase_row rulebase, ownerid integer)
+RETURNS SETOF rule
+LANGUAGE plpgsql
+STABLE
+AS $function$
+BEGIN
+ RETURN QUERY
+ SELECT *
+ FROM (
+ WITH src_rules AS (
+ SELECT r.*, rf_o.obj_ip, rf_o.obj_ip_end, rf.negated
+ FROM rule r
+ LEFT JOIN rule_from rf ON r.rule_id = rf.rule_id
+ LEFT JOIN objgrp_flat rf_of ON rf.obj_id = rf_of.objgrp_flat_id
+ LEFT JOIN object rf_o ON rf_of.objgrp_flat_member_id = rf_o.obj_id
+ WHERE r.rulebase_id = rulebase_row.id
+ AND owner_id = ownerid
+ AND rule_head_text IS NULL
+ ),
+ dst_rules AS (
+ SELECT r.*, rt_o.obj_ip, rt_o.obj_ip_end, rt.negated
+ FROM rule r
+ LEFT JOIN rule_to rt ON r.rule_id = rt.rule_id
+ LEFT JOIN objgrp_flat rt_of ON rt.obj_id = rt_of.objgrp_flat_id
+ LEFT JOIN object rt_o ON rt_of.objgrp_flat_member_id = rt_o.obj_id
+ WHERE r.rulebase_id = rulebase_row.id
+ AND owner_id = ownerid
+ AND rule_head_text IS NULL
+ )
+ SELECT s.*
+ FROM src_rules s
+ LEFT JOIN owner_network ON ip_ranges_overlap(s.obj_ip, s.obj_ip_end, ip, ip_end, s.negated != s.rule_src_neg)
+ UNION
+ SELECT d.*
+ FROM dst_rules d
+ LEFT JOIN owner_network ON ip_ranges_overlap(d.obj_ip, d.obj_ip_end, ip, ip_end, d.negated != d.rule_dst_neg)
+ ) AS combined
+ ORDER BY rule_name ASC;
+END;
+$function$;
+
+
diff --git a/roles/database/files/sql/idempotent/fworch-obj-import.sql b/roles/database/files/sql/idempotent/fworch-obj-import.sql
index 4c06ee1fa9..72eafc7d4c 100644
--- a/roles/database/files/sql/idempotent/fworch-obj-import.sql
+++ b/roles/database/files/sql/idempotent/fworch-obj-import.sql
@@ -106,13 +106,13 @@ DECLARE
i_admin_id INTEGER; -- id des admins der die letzte Aenderung gemacht hat
existing_obj RECORD; -- der ev. bestehende Datensatz aus object
b_insert BOOLEAN; -- soll eingefuegt werden oder nicht?
- b_change BOOLEAN; -- hat sich etwas geändert?
- b_change_security_relevant BOOLEAN; -- hat sich etwas sicherheitsrelevantes geändert?
+ b_change BOOLEAN; -- hat sich etwas geaendert?
+ b_change_security_relevant BOOLEAN; -- hat sich etwas sicherheitsrelevantes geaendert?
v_change_id VARCHAR; -- type of change
b_is_documented BOOLEAN;
t_outtext TEXT;
i_change_type INTEGER;
- i_new_obj_id BIGINT; -- id des neu eingefügten object
+ i_new_obj_id BIGINT; -- id des neu eingefuegten object
v_comment VARCHAR;
ip_range RECORD;
BEGIN
diff --git a/roles/database/files/sql/idempotent/fworch-rule-recert.sql b/roles/database/files/sql/idempotent/fworch-rule-recert.sql
index c6dc84d2fe..96fd02a2e7 100644
--- a/roles/database/files/sql/idempotent/fworch-rule-recert.sql
+++ b/roles/database/files/sql/idempotent/fworch-rule-recert.sql
@@ -7,6 +7,33 @@
-- (once per statement, not per row)
+-- fundamental function to check owner <--> rule mapping using the existing view
+-- "view_rule_with_owner"
+CREATE OR REPLACE FUNCTION recert_owner_responsible_for_rule (i_owner_id INTEGER, i_rule_id BIGINT) RETURNS BOOLEAN AS $$
+DECLARE
+ i_id BIGINT;
+BEGIN
+ -- check if this is the super owner:
+ SELECT INTO i_id id FROM owner WHERE id=i_owner_id AND is_default;
+ IF FOUND THEN -- this is the super owner
+ SELECT INTO i_id rule_id FROM view_rule_with_owner WHERE owner_id IS NULL AND rule_id=i_rule_id;
+ IF FOUND THEN
+ RAISE DEBUG '%', 'rule found for super owner ' || i_rule_id;
+ RETURN TRUE;
+ ELSE
+ RETURN FALSE;
+ END IF;
+ ELSE -- standard owner
+ SELECT INTO i_id rule_id FROM view_rule_with_owner WHERE owner_id=i_owner_id AND rule_id=i_rule_id;
+ IF FOUND THEN
+ RETURN TRUE;
+ ELSE
+ RETURN FALSE;
+ END IF;
+ END IF;
+END;
+$$ LANGUAGE plpgsql;
+
-- This function returns a table of future recert entries
-- but does not write them into the recertification table
@@ -45,7 +72,7 @@ BEGIN
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 AND R.mgm_id = M.mgm_id)
LEFT JOIN owner O ON (
CASE WHEN b_super_owner THEN O.is_default ELSE V.owner_id = O.id END
)
@@ -64,6 +91,133 @@ BEGIN
END;
$$ LANGUAGE plpgsql STABLE;
+-- this function deletes existing (future) open recert entries and inserts the new ones into the recertificaiton table
+-- the new recert date will only replace an existing one, if it is closer (smaller)
+CREATE OR REPLACE FUNCTION recert_refresh_one_owner_one_mgm
+ (i_owner_id INTEGER, i_mgm_id INTEGER, t_requested_next_recert_date TIMESTAMP) RETURNS VOID AS $$
+DECLARE
+ r_rule RECORD;
+ i_recert_entry_id BIGINT;
+ b_super_owner BOOLEAN := FALSE;
+ t_rule_created TIMESTAMP;
+ t_current_next_recert_date TIMESTAMP;
+ t_next_recert_date_by_interval TIMESTAMP;
+ t_rule_last_recertified TIMESTAMP;
+ t_next_recert_date TIMESTAMP;
+ i_recert_inverval INTEGER;
+ b_never_recertified BOOLEAN := FALSE;
+ b_no_current_next_recert_date BOOLEAN := FALSE;
+ b_super_owner_exists BOOLEAN := FALSE;
+ i_previous_import BIGINT;
+ i_current_import_id BIGINT;
+ i_super_owner_id INT;
+ i_current_owner_id_tmp INT;
+BEGIN
+ IF i_owner_id IS NULL OR i_mgm_id IS NULL THEN
+ IF i_owner_id IS NULL THEN
+ RAISE WARNING 'found undefined owner_id in recert_refresh_one_owner_one_mgm';
+ ELSE -- mgm_id NULL
+ RAISE WARNING 'found undefined mgm_id in recert_refresh_one_owner_one_mgm';
+ END IF;
+ ELSE
+ -- get id of previous import:
+ SELECT INTO i_current_import_id control_id FROM import_control WHERE mgm_id=i_mgm_id AND stop_time IS NULL;
+ SELECT INTO i_previous_import * FROM get_previous_import_id_for_mgmt(i_mgm_id,i_current_import_id);
+ IF NOT FOUND OR i_previous_import IS NULL THEN
+ i_previous_import := -1; -- prevent match for previous import
+ END IF;
+
+ SELECT INTO i_super_owner_id id FROM owner WHERE is_default;
+ IF FOUND THEN
+ b_super_owner_exists := TRUE;
+ END IF;
+
+ SELECT INTO i_current_owner_id_tmp id FROM owner WHERE id=i_owner_id AND is_default;
+ IF FOUND THEN
+ b_super_owner := TRUE;
+ END IF;
+
+ SELECT INTO i_recert_inverval recert_interval FROM owner WHERE id=i_owner_id;
+
+ FOR r_rule IN
+ SELECT rule_uid, rule_id FROM rule WHERE mgm_id=i_mgm_id AND (active OR NOT active AND rule_last_seen=i_previous_import)
+ LOOP
+
+ IF recert_owner_responsible_for_rule (i_owner_id, r_rule.rule_id) THEN
+
+ -- collects dates
+ SELECT INTO t_current_next_recert_date next_recert_date FROM recertification
+ WHERE owner_id=i_owner_id AND rule_id=r_rule.rule_id AND recert_date IS NULL;
+
+ IF NOT FOUND THEN
+ b_no_current_next_recert_date := TRUE;
+ END IF;
+
+ SELECT INTO t_rule_last_recertified MAX(recert_date)
+ FROM recertification
+ WHERE rule_id=r_rule.rule_id AND NOT recert_date IS NULL;
+
+ IF NOT FOUND OR t_rule_last_recertified IS NULL THEN -- no prior recertification, use initial rule import date
+ 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.mgm_id = rule_metadata.mgm_id)
+ WHERE rule_id=r_rule.rule_id;
+ END IF;
+
+ IF t_requested_next_recert_date IS NULL THEN
+ -- if the currenct next recert date is before the intended fixed input date, ignore it
+ IF b_never_recertified THEN
+ t_next_recert_date := t_rule_created + make_interval (days => i_recert_inverval);
+ ELSE
+ t_next_recert_date := t_rule_last_recertified + make_interval (days => i_recert_inverval);
+ END IF;
+ ELSE
+ t_next_recert_date := t_requested_next_recert_date;
+ END IF;
+
+ -- do not set next recert date later than actually calculated date
+ IF NOT b_no_current_next_recert_date THEN
+ IF t_next_recert_date>t_current_next_recert_date THEN
+ t_next_recert_date := t_current_next_recert_date;
+ END IF;
+ END IF;
+
+ -- delete old recert entry:
+ DELETE FROM recertification WHERE owner_id=i_owner_id AND rule_id=r_rule.rule_id AND recert_date IS NULL;
+
+ -- add new recert entry:
+ IF b_super_owner THEN -- special case for super owner (convert NULL to ID)
+ INSERT INTO recertification (rule_metadata_id, next_recert_date, rule_id, ip_match, owner_id)
+ SELECT rule_metadata_id,
+ t_next_recert_date AS next_recert_date,
+ rule_id,
+ matches as ip_match,
+ 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.mgm_id = rule_metadata.mgm_id)
+ 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)
+ SELECT rule_metadata_id,
+ t_next_recert_date AS next_recert_date,
+ rule_id,
+ matches as ip_match,
+ 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.mgm_id = rule_metadata.mgm_id)
+ WHERE view_rule_with_owner.rule_id=r_rule.rule_id AND view_rule_with_owner.owner_id=i_owner_id;
+ END IF;
+ ELSE
+ -- delete old outdated recert entry if owner is not responsible any more
+ DELETE FROM recertification WHERE owner_id=i_owner_id AND rule_id=r_rule.rule_id AND recert_date IS NULL;
+ END IF;
+ END LOOP;
+ END IF;
+END;
+$$ LANGUAGE plpgsql;
-- function used during import of a single management config
@@ -124,11 +278,11 @@ BEGIN
UNION
SELECT C.recert_date + make_interval (days => o.recert_interval) AS value
) AS temp_table)),
- NULL::bigint AS owner_recert_id
+ 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 AND R.mgm_id = M.mgm_id)
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)
@@ -152,11 +306,11 @@ BEGIN
UNION
SELECT C.recert_date + make_interval (days => o.recert_interval) AS value
) AS temp_table)),
- NULL::bigint AS owner_recert_id
+ 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 AND R.mgm_id = M.mgm_id)
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 85976d5896..0426e3cfef 100644
--- a/roles/database/files/sql/idempotent/fworch-texts.sql
+++ b/roles/database/files/sql/idempotent/fworch-texts.sql
@@ -161,6 +161,10 @@ INSERT INTO txt VALUES ('RecertificationEvent', 'German', 'Zert: Zertifikat');
INSERT INTO txt VALUES ('RecertificationEvent', 'English', 'Cert: Certificate');
INSERT INTO txt VALUES ('RecertEventReport', 'German', 'Zert: Zert-Regel-Details');
INSERT INTO txt VALUES ('RecertEventReport', 'English', 'Cert: Cert Rule Details');
+INSERT INTO txt VALUES ('ComplianceReport', 'German', 'Compliance Report');
+INSERT INTO txt VALUES ('ComplianceReport', 'English', 'Compliance Report');
+INSERT INTO txt VALUES ('ComplianceDiffReport', 'German', 'Compliance Diff Report');
+INSERT INTO txt VALUES ('ComplianceDiffReport', 'English', 'Compliance Diff Report');
INSERT INTO txt VALUES ('mixed', 'German', 'Gemischt');
INSERT INTO txt VALUES ('mixed', 'English', 'Mixed');
INSERT INTO txt VALUES ('exclusive', 'German', 'Exklusiv');
@@ -443,7 +447,7 @@ Die folgenden Hauptmenüpunkte stehen (je nach Rollenzugehörigkeit) zur
Rezertifizierung : Workflow zur Bereinigung des Regelwerks um nicht mehr benötigte Regeln
Modellierung : Erlaubt die verteilte Modellierung von Kommunikationsverbindungen (Soll-Zustand)
Netzanalyse : Pfadanalyse - welche Firewalls liegen zwischen zwei IP-Adressen?
- Compliance : Definition von Zonenmatrix und Zugriffs-Compliance
+ Compliance : Definition von Zonenmatrix und Zugriffs-Compliance
Monitoring : Alarmierung, Log-Files, Import-Status, ...
Hilfeseiten : Benutzerhandbuch
Einstellungen : Alle Einstellungen wie z.B. Sprache der Benutzeroberfläche oder
@@ -461,7 +465,7 @@ The following top-level menu items are available (depending on role memberships)
Recertification : Rulebase sanitization by continuously cleaning up existing rules
Modelling : Allows for distributed modelling of network connections (target state)
Network Analysis : Path analysis - which firewall is crossed when routing between two IP addresses?
- Compliance : Defining zone matrix and access compliance
+ Compliance : Defining zone matrix and access compliance
Monitoring : Alarms, log files, import status, ...
Help : Manual pages
Settings : All settings like e.g. language of the user interface or
@@ -558,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');
@@ -702,6 +708,8 @@ INSERT INTO txt VALUES ('objects', 'German', 'Objekte');
INSERT INTO txt VALUES ('objects', 'English', 'Objects');
INSERT INTO txt VALUES ('report_duration', 'German', 'Report-Generierung in');
INSERT INTO txt VALUES ('report_duration', 'English', 'Report generation took');
+INSERT INTO txt VALUES ('report_elements', 'German', 'Ausgewertete Elemente');
+INSERT INTO txt VALUES ('report_elements', 'English', 'Evaluated elements');
INSERT INTO txt VALUES ('seconds', 'German', 'Sekunden');
INSERT INTO txt VALUES ('seconds', 'English', 'seconds');
INSERT INTO txt VALUES ('minutes', 'German', 'Minuten');
@@ -1270,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');
@@ -1785,14 +1795,13 @@ INSERT INTO txt VALUES ('modelled_destination', 'English', 'Modelled Destinatio
INSERT INTO txt VALUES ('last_requested', 'German', 'Letzte Beantragung');
INSERT INTO txt VALUES ('last_requested', 'English', 'Last Requested');
-
-- compliance
INSERT INTO txt VALUES ('compliance', 'German', 'Compliance');
INSERT INTO txt VALUES ('compliance', 'English', 'Compliance');
INSERT INTO txt VALUES ('network_zones', 'German', 'Netzwerkzonen');
INSERT INTO txt VALUES ('network_zones', 'English', 'Network zones');
-INSERT INTO txt VALUES ('matrix', 'German', 'Matrix');
-INSERT INTO txt VALUES ('matrix', 'English', 'Matrix');
+INSERT INTO txt VALUES ('matrix', 'German', 'Zonen-Matrix');
+INSERT INTO txt VALUES ('matrix', 'English', 'Zones Matrix');
INSERT INTO txt VALUES ('checks', 'German', 'Überprüfung');
INSERT INTO txt VALUES ('checks', 'English', 'Checks');
INSERT INTO txt VALUES ('check', 'German', 'Überprüfen');
@@ -1845,6 +1854,34 @@ INSERT INTO txt VALUES ('file_upload_failed', 'German', 'Datei hochladen ist
INSERT INTO txt VALUES ('file_upload_failed', 'English', 'File upload failed.');
INSERT INTO txt VALUES ('wrong_input_data', 'German', 'Falsche Eingabedaten');
INSERT INTO txt VALUES ('wrong_input_data', 'English', 'Wrong input data');
+INSERT INTO txt VALUES ('add_matrix', 'German', 'Matrix hinzufügen');
+INSERT INTO txt VALUES ('add_matrix', 'English', 'Add Matrix');
+INSERT INTO txt VALUES ('delete_matrix', 'German', 'Matrix löschen');
+INSERT INTO txt VALUES ('delete_matrix', 'English', 'Delete Matrix');
+INSERT INTO txt VALUES ('policy', 'German', 'Policy');
+INSERT INTO txt VALUES ('policy', 'English', 'Policy');
+INSERT INTO txt VALUES ('policies', 'German', 'Policies');
+INSERT INTO txt VALUES ('policies', 'English', 'Policies');
+INSERT INTO txt VALUES ('edit_policy', 'German', 'Policy bearbeiten');
+INSERT INTO txt VALUES ('edit_policy', 'English', 'Edit Policy');
+INSERT INTO txt VALUES ('delete_policy', 'German', 'Policy löschen');
+INSERT INTO txt VALUES ('delete_policy', 'English', 'Delete Policy');
+INSERT INTO txt VALUES ('fix_criteria', 'German', 'Feste Kriterien');
+INSERT INTO txt VALUES ('fix_criteria', 'English', 'Fixed Criteria');
+INSERT INTO txt VALUES ('edit_fix_crit', 'German', 'Festes Kriteriium bearbeiten');
+INSERT INTO txt VALUES ('edit_fix_crit', 'English', 'Edit Fixed Criterion');
+INSERT INTO txt VALUES ('criteria', 'German', 'Kriterien');
+INSERT INTO txt VALUES ('criteria', 'English', 'Criteria');
+INSERT INTO txt VALUES ('show_non_impact_rules','German', 'Zeige Regeln ohne Auswirkungen');
+INSERT INTO txt VALUES ('show_non_impact_rules','English', 'Show non-impact rules');
+INSERT INTO txt VALUES ('assess_ip_null', 'German', 'Netzwerkobjekte in Quelle oder Ziel ohne IP-Adresse');
+INSERT INTO txt VALUES ('assess_ip_null', 'English', 'Network objects in source or destination without IP');
+INSERT INTO txt VALUES ('assess_all_ips', 'German', 'Netzwerkobjekte in Quelle oder Ziel mit 0.0.0.0/0 oder ::/0');
+INSERT INTO txt VALUES ('assess_all_ips', 'English', 'Network objects in source or destination with 0.0.0.0/0 or ::/0');
+INSERT INTO txt VALUES ('assess_host_address', 'German', 'Netzwerkobjekte in Quelle oder Ziel mit 0.0.0.0/32');
+INSERT INTO txt VALUES ('assess_host_address', 'English', 'Network objects in source or destination with 0.0.0.0/32');
+INSERT INTO txt VALUES ('assess_broadcast', 'German', 'Netzwerkobjekte in Quelle oder Ziel mit 255.255.255.255/32');
+INSERT INTO txt VALUES ('assess_broadcast', 'English', 'Network objects in source or destination with 255.255.255.255/32');
-- settings
INSERT INTO txt VALUES ('devices', 'German', 'Geräte');
@@ -1981,6 +2018,10 @@ INSERT INTO txt VALUES ('last_incomplete', 'German', 'Letzter Unvollendete
INSERT INTO txt VALUES ('last_incomplete', 'English', 'Last Incomplete');
INSERT INTO txt VALUES ('rollback', 'German', 'Zurücksetzen');
INSERT INTO txt VALUES ('rollback', 'English', 'Rollback');
+INSERT INTO txt VALUES ('full_rollback', 'German', 'Komplett zurücksetzen');
+INSERT INTO txt VALUES ('full_rollback', 'English', 'Full rollback');
+INSERT INTO txt VALUES ('not_imported_yet', 'German', 'Kein Import für dieses Management vorhanden');
+INSERT INTO txt VALUES ('not_imported_yet', 'English', 'No existing Import for this management');
INSERT INTO txt VALUES ('last_success', 'German', 'Letzter Erfolg');
INSERT INTO txt VALUES ('last_success', 'English', 'Last Success');
INSERT INTO txt VALUES ('last_import', 'German', 'Letzter Import');
@@ -2605,6 +2646,51 @@ INSERT INTO txt VALUES ('varianceAnalysisStartAt','German', 'Soll-Ist-Abgleich-S
INSERT INTO txt VALUES ('varianceAnalysisStartAt','English','Variance Analysis start at');
INSERT INTO txt VALUES ('resolveNetworkAreas', 'German', 'Netzbereiche auflösen');
INSERT INTO txt VALUES ('resolveNetworkAreas', 'English', 'Resolve Network Areas');
+INSERT INTO txt VALUES ('complianceCheckSleepTime','German','Compliance-Check-Intervall (in Minuten)');
+INSERT INTO txt VALUES ('complianceCheckSleepTime','English','Compliance Check sleep time (in minutes)');
+INSERT INTO txt VALUES ('complianceCheckStartAt','German', 'Compliance-Check-Start');
+INSERT INTO txt VALUES ('complianceCheckStartAt','English',' Compliance Check start at');
+INSERT INTO txt VALUES ('complianceCheckMailRecipients','German','Empfänger-Email-Adressen für Benachrichtigungen');
+INSERT INTO txt VALUES ('complianceCheckMailRecipients','English','Recipient email addresses for notifications');
+INSERT INTO txt VALUES ('complianceCheckMailSubject','German','Titel der Benachrichtigung');
+INSERT INTO txt VALUES ('complianceCheckMailSubject','English','Subject of notification emails');
+INSERT INTO txt VALUES ('complianceCheckMailBody','German', 'Text der Benachrichtigung');
+INSERT INTO txt VALUES ('complianceCheckMailBody','English','Body of notification emails');
+INSERT INTO txt VALUES ('complianceMatrixAllowNetworkZones','German', 'Netzwerkzonenverschachtelung erlauben');
+INSERT INTO txt VALUES ('complianceMatrixAllowNetworkZones','English','Nested Network Zones allowed');
+INSERT INTO txt VALUES ('complianceCheckInternetZoneObject','German', 'Internetzone');
+INSERT INTO txt VALUES ('complianceCheckInternetZoneObject','English','Internet zone');
+INSERT INTO txt VALUES ('complianceCheckMaxPrintedViolations','German', 'Maximale Anzahl gedruckter Verstöße pro Regel');
+INSERT INTO txt VALUES ('complianceCheckMaxPrintedViolations','English','Maximum number of printed violations per rule');
+INSERT INTO txt VALUES ('complianceCheckSortMatrixByID', 'German', 'Matrixsortierung nach Zonen-ID');
+INSERT INTO txt VALUES ('complianceCheckSortMatrixByID', 'English', 'Matrix sorting by zone ID');
+INSERT INTO txt VALUES ('complianceCheckRelevantManagements','German', 'Relevante Managements');
+INSERT INTO txt VALUES ('complianceCheckRelevantManagements','English','Relevant managements');
+INSERT INTO txt VALUES ('complianceCheckAutoCalcInternetZone','German', 'Internetzone automatisch berechnen');
+INSERT INTO txt VALUES ('complianceCheckAutoCalcInternetZone','English','Auto-calculate internet zone');
+INSERT INTO txt VALUES ('complianceCheckAutoCalcUndefinedInternalZone','German', 'Undefiniert-intern Zone automatisch berechnen');
+INSERT INTO txt VALUES ('complianceCheckAutoCalcUndefinedInternalZone','English','Auto-calculate undefined-internal zone');
+INSERT INTO txt VALUES ('complianceCheckExcludeFromInternetZone','German', 'Von Internetzonenberechnung ausschließen');
+INSERT INTO txt VALUES ('complianceCheckExcludeFromInternetZone','English','Exclude from internet zone calculation');
+INSERT INTO txt VALUES ('complianceCheckPrivateAdressSpace','German', 'Privater Adressbereich');
+INSERT INTO txt VALUES ('complianceCheckPrivateAdressSpace','English','Private address space');
+INSERT INTO txt VALUES ('complianceCheckLoopbackLocal','German', 'Loopback / local');
+INSERT INTO txt VALUES ('complianceCheckLoopbackLocal','English','Loopback / lokal');
+INSERT INTO txt VALUES ('complianceCheckMulticastBroadcast','German', 'Multicast / Broadcast');
+INSERT INTO txt VALUES ('complianceCheckMulticastBroadcast','English','Multicast / broadcast');
+INSERT INTO txt VALUES ('complianceCheckDocumentationSamples','German', 'Dokumentation / Beispiele');
+INSERT INTO txt VALUES ('complianceCheckDocumentationSamples','English','Documentation / samples');
+INSERT INTO txt VALUES ('complianceCheckDiv','German', 'Div (Benchmarking, Spezialzweck usw.)');
+INSERT INTO txt VALUES ('complianceCheckDiv','English','Div (benchmarking, special purpose, etc)');
+INSERT INTO txt VALUES ('complianceCheckAutoCalculatedZonesAtTheEnd','German', 'Auto-kalkulierte Zonen nach anderen Zonen anordnen');
+INSERT INTO txt VALUES ('complianceCheckAutoCalculatedZonesAtTheEnd','English','Arrange auto-calculated zones according to other zones');
+INSERT INTO txt VALUES ('complianceCheckTreatDynamicAndDomainObjectsAsInternet','German', 'Behandel dynamische und Domain-Objekte als Internet');
+INSERT INTO txt VALUES ('complianceCheckTreatDynamicAndDomainObjectsAsInternet','English','Treat dynamic and domain objects as internet');
+INSERT INTO txt VALUES ('complianceCheckShowShortColumsInComplianceReports','German', 'Spalten mit Kurzform für Sources, Destinations und Services anzeigen');
+INSERT INTO txt VALUES ('complianceCheckShowShortColumsInComplianceReports','English','Show columns with short forms for sources, destinations and services');
+INSERT INTO txt VALUES ('importedMatrixReadOnly','German', 'Importierte Matrizen schreibgeschützt');
+INSERT INTO txt VALUES ('importedMatrixReadOnly','English','Imported matrices read-only');
+
INSERT INTO txt VALUES ('availableModules', 'German', 'Verfügbare Module');
INSERT INTO txt VALUES ('availableModules', 'English', 'Available Modules');
INSERT INTO txt VALUES ('notification', 'German', 'Benachrichtigung');
@@ -2633,6 +2719,8 @@ INSERT INTO txt VALUES ('suspected_cause', 'German', 'Vermutliche Ursache'
INSERT INTO txt VALUES ('suspected_cause', 'English', 'Suspected Cause');
INSERT INTO txt VALUES ('device', 'German', 'Gerät');
INSERT INTO txt VALUES ('device', 'English', 'Device');
+INSERT INTO txt VALUES ('enforcing_devices', 'German', 'Filter-Devices');
+INSERT INTO txt VALUES ('enforcing_devices', 'English', 'Enforcing Devices');
INSERT INTO txt VALUES ('object_type', 'German', 'Objekt-Typ');
INSERT INTO txt VALUES ('object_type', 'English', 'Object Type');
INSERT INTO txt VALUES ('object_name', 'German', 'Objektname');
@@ -2871,6 +2959,8 @@ INSERT INTO txt VALUES ('naming_convention', 'German', 'Namenskonvention');
INSERT INTO txt VALUES ('naming_convention', 'English', 'Naming Convention');
INSERT INTO txt VALUES ('import_app_server', 'German', 'App Server importieren');
INSERT INTO txt VALUES ('import_app_server', 'English', 'Import app servers');
+INSERT INTO txt VALUES ('import_matrix', 'German', 'Matrix-Import');
+INSERT INTO txt VALUES ('import_matrix', 'English', 'Matrix Import');
-- user messages
INSERT INTO txt VALUES ('U0001', 'German', 'Eingabetext wurde um nicht erlaubte Zeichen gekürzt');
@@ -2919,6 +3009,16 @@ INSERT INTO txt VALUES ('U4007', 'German', 'Weitere rezertifizierte Apps');
INSERT INTO txt VALUES ('U4007', 'English', 'Further recertified apps');
INSERT INTO txt VALUES ('U4008', 'German', 'Weitere Apps');
INSERT INTO txt VALUES ('U4008', 'English', 'Further apps');
+INSERT INTO txt VALUES ('U4501', 'German', 'Sind sie sicher, dass sie folgende Policy löschen wollen: ');
+INSERT INTO txt VALUES ('U4501', 'English', 'Are you sure you want to delete policy: ');
+INSERT INTO txt VALUES ('U4502', 'German', 'Sind sie sicher, dass sie folgendes Fixes Kriterium löschen wollen: ');
+INSERT INTO txt VALUES ('U4502', 'English', 'Are you sure you want to delete fix criterion: ');
+INSERT INTO txt VALUES ('U4503', 'German', 'Sind sie sicher, dass sie folgende Matrix löschen wollen: ');
+INSERT INTO txt VALUES ('U4503', 'English', 'Are you sure you want to delete matrix: ');
+INSERT INTO txt VALUES ('U4504', 'German', 'Die Matrix kann nicht gelöscht werden, da sie in mindestens einer aktiven Policy verwendet wird.');
+INSERT INTO txt VALUES ('U4504', 'English', 'Matrix cannot be deleted as it is used in at least one active Policy.');
+INSERT INTO txt VALUES ('U4505', 'German', 'Die Matrix kann nicht gelöscht werden, da sie von einer anderen Quelle importiert wurde.');
+INSERT INTO txt VALUES ('U4505', 'English', 'Matrix cannot be deleted as it is imported from another source.');
INSERT INTO txt VALUES ('U5001', 'German', 'Setup und Verwaltung des Firewall Orchestrator. Bitte eine Einstellung in der linken Randleiste auswählen.');
INSERT INTO txt VALUES ('U5001', 'English', 'Setup and administration of Firewall Orchestrator. Please choose a setting in the left sidebar.');
@@ -3045,6 +3145,10 @@ INSERT INTO txt VALUES ('U5322', 'German', 'Verwaltung der Voreinstellungen f&u
INSERT INTO txt VALUES ('U5322', 'English', 'Administration of default settings for network modelling');
INSERT INTO txt VALUES ('U5323', 'German', 'Verwaltung der Voreinstellungen für das Reporting');
INSERT INTO txt VALUES ('U5323', 'English', 'Administration of default settings for reporting');
+INSERT INTO txt VALUES ('U5324', 'German', 'Verwaltung der Compliance-Voreinstellungen für alle Nutzer');
+INSERT INTO txt VALUES ('U5324', 'English', 'Administration of compliance settings for all users');
+INSERT INTO txt VALUES ('U5325', 'German', 'Mehrere Service-Uids mit Komma trennen');
+INSERT INTO txt VALUES ('U5325', 'English', 'Multiple service uids can be separated by using commas');
INSERT INTO txt VALUES ('U5401', 'German', 'Passwort geändert.');
INSERT INTO txt VALUES ('U5401', 'English', 'Password changed.');
@@ -3245,6 +3349,8 @@ INSERT INTO txt VALUES ('E5108', 'German', 'Email-Adresse muss "@"-Zeichen enth
INSERT INTO txt VALUES ('E5108', 'English', 'Email address must contain "@"-sign.');
INSERT INTO txt VALUES ('E5109', 'German', 'Bitte keine Leerzeichen im Namen verwenden.');
INSERT INTO txt VALUES ('E5109', 'English', 'Please do not use spaces in the name.');
+INSERT INTO txt VALUES ('E5110', 'German', 'UID für Check Point Managements muss gesetzt sein.');
+INSERT INTO txt VALUES ('E5110', 'English', 'UID for Check Point managements has to be set.');
INSERT INTO txt VALUES ('E5111', 'German', 'Es gibt bereits ein Gateway mit derselben Konfiguration und Import aktiviert');
INSERT INTO txt VALUES ('E5111', 'English', 'There is already a gateway in the same configuration with import enabled');
INSERT INTO txt VALUES ('E5112', 'German', 'Gateway konnte nicht angelegt werden');
@@ -3376,6 +3482,10 @@ INSERT INTO txt VALUES ('E5292', 'German', 'Dn oder Gruppe muss gefüllt se
INSERT INTO txt VALUES ('E5292', 'English', 'Dn or group has to be filled');
INSERT INTO txt VALUES ('E5293', 'German', 'Lifecycle Status konnte nicht gespeichert werden');
INSERT INTO txt VALUES ('E5293', 'English', 'Owner Lifecycle State could not be saved');
+INSERT INTO txt VALUES ('E5294', 'German', 'Fehler beim Einloggen in');
+INSERT INTO txt VALUES ('E5294', 'English', 'error while logging in to');
+INSERT INTO txt VALUES ('E5295', 'German', 'Authentifizierungsfehler');
+INSERT INTO txt VALUES ('E5295', 'English', 'Authentication Error');
INSERT INTO txt VALUES ('E5301', 'German', 'Konfiguration konnte nicht gelesen oder verarbeitet werden.');
INSERT INTO txt VALUES ('E5301', 'English', 'Error reading or processing Config.');
@@ -3526,6 +3636,10 @@ INSERT INTO txt VALUES ('E9302', 'English', 'HTML is invalid!');
INSERT INTO txt VALUES ('E9400', 'German', 'Leere Datei hochgeladen/keine Änderungen wurden vorgenommen');
INSERT INTO txt VALUES ('E9400', 'English', 'Empty file provided/no changes where applied');
+INSERT INTO txt VALUES ('E9401', 'German', 'Matrix erfolgreich importiert');
+INSERT INTO txt VALUES ('E9401', 'English', 'Matrix imported successfully');
+INSERT INTO txt VALUES ('E9402', 'German', 'Fehlr beim Import der Matrix');
+INSERT INTO txt VALUES ('E9402', 'English', 'Error importing matrix');
-- errors from Api
INSERT INTO txt VALUES ('A0001', 'German', 'Ungültige Anmeldedaten. Nutzername darf nicht leer sein');
@@ -3586,6 +3700,10 @@ INSERT INTO txt VALUES ('T0106', 'German', 'Aktuell aktive unbenutzte Regeln al
INSERT INTO txt VALUES ('T0106', 'English', 'Currently active unused rules of all gateways');
INSERT INTO txt VALUES ('T0107', 'German', 'Aktuell aktive Regeln, die zur Rezertifizierung anstehen');
INSERT INTO txt VALUES ('T0107', 'English', 'Currently active rules with upcoming recertification');
+INSERT INTO txt VALUES ('T0108', 'German', 'Alle nicht gelösten Compliance-Verletzungen');
+INSERT INTO txt VALUES ('T0108', 'English', 'All unresolved compliance violations');
+INSERT INTO txt VALUES ('T0109', 'German', 'Veränderungen der Compliance-Verletzungen in Zeitraum x');
+INSERT INTO txt VALUES ('T0109', 'English', 'Changes in compliance violations in period x');
-- Contextual Info (Tooltips)
INSERT INTO txt VALUES ('C1000', 'German', 'Zeige alle Eigentüer inklusive der rezertifizierten.');
@@ -4599,6 +4717,8 @@ INSERT INTO txt VALUES ('H5103', 'English', 'For firewall gateways without a sep
');
INSERT INTO txt VALUES ('H5104', 'German', 'Wenn Beispieldaten (definiert durch die Endung "_demo" vom Namen) existieren, wird eine Schaltfläche angezeigt, um diese und alle verknüpften Gateways zu löschen.');
INSERT INTO txt VALUES ('H5104', 'English', 'If there are sample data (defined by the ending "_demo" of the name), a button is displayed to delete them and all related gateways .');
+INSERT INTO txt VALUES ('H5110', 'German', 'Uid: Eindeutige ID des Managements.');
+INSERT INTO txt VALUES ('H5110', 'English', 'Uid: Unique id of the mangement.');
INSERT INTO txt VALUES ('H5111', 'German', 'Name*: Name des Managements.
Für die meisten Firewalls ist dies ein willkürlicher Name. Ausnahmen sind direkt verbundene Gateways von Fortigate, Netscreen und Juniper.
Hier muss der Name des Firewallgateways eingetragen werden.
@@ -4726,6 +4846,8 @@ INSERT INTO txt VALUES ('H5141', 'English', 'Admins can create and administrate
The clone button helps defining new gateways by copying the data from existing ones.
Before saving at least one of the parameters Device Type, Management or Rulebase has to be different from the existing gateways if the Import Disabled flag is not set.
');
+INSERT INTO txt VALUES ('H5150', 'German', 'UID*: Eindeutige ID des Gateways.');
+INSERT INTO txt VALUES ('H5150', 'English', 'UID*: Unique ID of the Gateway.');
INSERT INTO txt VALUES ('H5151', 'German', 'Name*: Name des Gateways. Für Legacy Fortinet (ssh) muss dies der reale Name des Firewallgateways sein wie in der Config definiert.');
INSERT INTO txt VALUES ('H5151', 'English', 'Name*: Name of the Gateway. For legacy Fortinet (ssh) this must be the real name of the firewall gateway as defined in the config.');
INSERT INTO txt VALUES ('H5152', 'German', 'Kommentar: Optionaler Kommentar zu diesem Gateway.');
@@ -5770,6 +5892,95 @@ INSERT INTO txt VALUES ('H5704', 'German', 'In der Tabelle der Suchergebnisse k
INSERT INTO txt VALUES ('H5704', 'English', 'In the table of search results new texts can be defined per key, existing texts can be marked for deletion by setting the "Delete" flag.
If only a text is removed, the system text will be overwritten by an empty text! All changes get effective only by pressing the "Save" button.
');
+INSERT INTO txt VALUES ('H5801', 'German', 'Compliance-Check-Intervall (in Minuten): legt das Intervall fest, in dem der Compliance-Check durchgeführt werden soll.');
+INSERT INTO txt VALUES ('H5801', 'English', 'Compliance Check sleep time (in minutes): defines the interval in which the compliance check should be performed.');
+INSERT INTO txt VALUES ('H5802', 'German', 'Compliance-Check-Start: legt eine Bezugszeit fest, ab dem die Intervalle für den Compliance-Check gerechnet werden.');
+INSERT INTO txt VALUES ('H5802', 'English', 'Compliance Check start at: defines a referential time from which the Compliance Check intervals are calculated.');
+INSERT INTO txt VALUES ('H5803', 'German', 'Hier werden alle Einstellungen rund um den Compliance-Check verwaltet.');
+INSERT INTO txt VALUES ('H5803', 'English', 'Here all settings around the Compliance Check are administrated.');
+INSERT INTO txt VALUES ('H5804', 'German', 'Empfänger-Email-Adressen für Benachrichtigungen: Komma-separierte Liste von Email-Adressen, die bei Compliance-Verstössen benachrichtigt werden. Default-Wert = "leer".');
+INSERT INTO txt VALUES ('H5804', 'English', 'Recipient email addresses for change notifications: A comma-separated list of email addresses, which will be informed in the case of compliance issues. Default value = "empty".');
+INSERT INTO txt VALUES ('H5805', 'German', 'Titel der Benachrichtigung: Betreffzeile der Benachrichtigungs-Email. Default-Wert = "leer".');
+INSERT INTO txt VALUES ('H5805', 'English', 'Subject of notification emails: Subject line for notification emails. Default value = "empty".');
+INSERT INTO txt VALUES ('H5806', 'German', 'Text der Benachrichtigung: Email-Text für die Benachrichtigung. Der Email wird ein Compliance-Report angehängt. Default-Wert = "leer".');
+INSERT INTO txt VALUES ('H5806', 'English', 'Body of notification emails: Email text for the notification. A Compliance report will be attached to the email. Default value = "empty".');
+INSERT INTO txt VALUES ('H5807', 'German', 'Wenn aktiviert, werden die durch die Compliance-Prüfung erzeugten Daten in der Datenbank gespeichert.');
+INSERT INTO txt VALUES ('H5807', 'English', 'If checked, the data that is generated by the compliance check will be persisted in the database.');
+INSERT INTO txt VALUES ('H5808', 'German', 'Hier werden die Dienste definiert, die bei der Compliance-Prüfung berücksichtigt werden sollen. Wenn dieses Feld leer ist, werden keine Dienste eingeschränkt.');
+INSERT INTO txt VALUES ('H5808', 'English', 'Sets up services that should be checked for during compliance check. If this field is empty, no service will be restricted.');
+INSERT INTO txt VALUES ('H5809', 'German', 'Wenn aktiviert, wird beim Erstellen der Compliance-Matrix eine Schachtelung der Netzwerk-Zonen ermöglicht.');
+INSERT INTO txt VALUES ('H5809', 'English', 'If checked, the use of nested network zones for compliance matrices is enabled.');
+INSERT INTO txt VALUES ('H5810', 'German', 'Die Policy, die für den termingesteuerten Compliance Check genutzt wird.');
+INSERT INTO txt VALUES ('H5810', 'English', 'Policy used for the scheduled compliance check.');
+INSERT INTO txt VALUES ('H5811', 'German', 'Das Netzwerkobjekt, dass die Netzwerkzone "Internet" darstellt.');
+INSERT INTO txt VALUES ('H5811', 'English', 'The network object that is taken for the network zone "Internet".');
+INSERT INTO txt VALUES ('H5812', 'German', 'Importieren einer Matrix via JSON Datei. ');
+INSERT INTO txt VALUES ('H5812', 'English', 'Import of a matrix via JSON file. ');
+INSERT INTO txt VALUES ('H5813', 'German', 'Definition vom Kriterien mit fixem Inhalt');
+INSERT INTO txt VALUES ('H5813', 'English', 'Definition of Criteria with fixed content. ');
+INSERT INTO txt VALUES ('H5814', 'German', 'Maximal Anzahl von Violations die im Compliance Report pro Rule angezeigt werden. Bei 0 keine Beschränkung.');
+INSERT INTO txt VALUES ('H5814', 'English', 'Maximum number of violations shown in the compliance report per rule. If set to 0, no limitation is applied.');
+INSERT INTO txt VALUES ('H5815', 'German', 'Wenn aktiviert, werden Netzwerkzonenmatrizen nach Zonen-ID sortiert (Default: Sortierung nach Name).');
+INSERT INTO txt VALUES ('H5815', 'English', 'When enabled, network zone matrices are sorted by zone ID (default: sorted by name).');
+INSERT INTO txt VALUES ('H5816', 'German', 'Durch Komma getrennte Liste von relevanten Management IDs.');
+INSERT INTO txt VALUES ('H5816', 'English', 'Comma-separated list of relevant management IDs.');
+INSERT INTO txt VALUES ('H5817', 'German', 'Wenn aktiviert, wird bei Änderungen in Compliance-Matrizen automatisch eine "Internetzone" hinzugefügt, die alle Adressbereiche beinhaltet, die nicht in anderen Zonen definiert sind.');
+INSERT INTO txt VALUES ('H5817', 'English', 'If enabled, changes to compliance matrices will automatically add an "Internetzone" that includes all address ranges not defined in other zones.');
+INSERT INTO txt VALUES ('H5818', 'German', 'Adressbereiche, die aus der Berechnung der Internetzone ausgeschlossen werden sollen.');
+INSERT INTO txt VALUES ('H5818', 'English', 'Address ranges that should be excluded from the Internet zone calculation.');
+INSERT INTO txt VALUES ('H5819', 'German', 'Wenn aktiviert, wird bei Änderungen in Compliance-Matrizen automatisch eine "Undefiniert-Intern-Zone" hinzugefügt, die alle Adressbereiche beinhaltet, die nicht in anderen Zonen definiert sind und von der Internetzone ausgeschlossen wurden.');
+INSERT INTO txt VALUES ('H5819', 'English', 'When enabled, changes to compliance matrices automatically add an "Undefined Internal Zone" that includes all address ranges not defined in other zones and excluded from the Internet zone.');
+INSERT INTO txt VALUES ('H5820', 'German', 'Teilbereich des privaten Adressbereichs gemäß RFC 1918');
+INSERT INTO txt VALUES ('H5820', 'English', 'Part of the private address range according to RFC 1918');
+INSERT INTO txt VALUES ('H5821', 'German', 'Spezialbereich für „diese“ Netzwerke, Quelle unbekannt (unspezifische Adresse).');
+INSERT INTO txt VALUES ('H5821', 'English', 'Special range for “this” network, used as an unspecified address.');
+INSERT INTO txt VALUES ('H5822', 'German', 'Loopback-Adressbereich zur internen Kommunikation eines Hosts.');
+INSERT INTO txt VALUES ('H5822', 'English', 'Loopback address range for internal host communication.');
+INSERT INTO txt VALUES ('H5823', 'German', 'Link-Local-Adressbereich für automatische Adressvergabe ohne DHCP.');
+INSERT INTO txt VALUES ('H5823', 'English', 'Link-local address range for automatic addressing without DHCP.');
+INSERT INTO txt VALUES ('H5824', 'German', 'Multicast-Adressbereich für die Zustellung an mehrere Empfänger.');
+INSERT INTO txt VALUES ('H5824', 'English', 'Multicast address range for delivery to multiple recipients.');
+INSERT INTO txt VALUES ('H5825', 'German', 'Reservierter Bereich für zukünftige oder experimentelle Nutzung.');
+INSERT INTO txt VALUES ('H5825', 'English', 'Reserved range for future or experimental use.');
+INSERT INTO txt VALUES ('H5826', 'German', 'Broadcast-Adresse für die Ansprache aller Hosts im lokalen Netzwerk.');
+INSERT INTO txt VALUES ('H5826', 'English', 'Broadcast address for addressing all hosts in the local network.');
+INSERT INTO txt VALUES ('H5827', 'German', 'Dokumentationsnetz für Beispiele in Handbüchern und Tutorials.');
+INSERT INTO txt VALUES ('H5827', 'English', 'Documentation network for examples in manuals and tutorials.');
+INSERT INTO txt VALUES ('H5828', 'German', 'Zweites Dokumentationsnetz für Lehr- und Beispielzwecke.');
+INSERT INTO txt VALUES ('H5828', 'English', 'Second documentation network for educational and example purposes.');
+INSERT INTO txt VALUES ('H5829', 'German', 'Drittes Dokumentationsnetz für Lehr- und Beispielzwecke.');
+INSERT INTO txt VALUES ('H5829', 'English', 'Third documentation network for educational and example purposes.');
+INSERT INTO txt VALUES ('H5830', 'German', 'Carrier-Grade NAT-Bereich für Provider-interne Adressierung.');
+INSERT INTO txt VALUES ('H5830', 'English', 'Carrier-grade NAT range for provider-internal addressing.');
+INSERT INTO txt VALUES ('H5831', 'German', 'Reservierter Adressbereich für spezielle Zwecke (IANA).');
+INSERT INTO txt VALUES ('H5831', 'English', 'Reserved address block for special purposes (IANA).');
+INSERT INTO txt VALUES ('H5832', 'German', 'Adressbereich für Netzwerkleistungs- und Benchmark-Tests.');
+INSERT INTO txt VALUES ('H5832', 'English', 'Address range for network performance and benchmark testing.');
+INSERT INTO txt VALUES ('H5833', 'German', 'Ehemaliger IPv6-Übergangsbereich (6to4-Relay), heute obsolet.');
+INSERT INTO txt VALUES ('H5833', 'English', 'Former IPv6 transition range (6to4 relay), now obsolete.');
+INSERT INTO txt VALUES ('H5834', 'German', 'Wenn aktiviert, werden die auto-kalkulierten Zonen am Ende der Matrix-Achsen eingeordnet. Default: am Anfang.');
+INSERT INTO txt VALUES ('H5834', 'English', 'When activated, the auto-calculated zones are placed at the end of the matrix axes. Default: at the beginning.');
+INSERT INTO txt VALUES ('H5835', 'German', 'Wenn aktiviert, werden dynamische und Domain-Objekte in der Compliance-Auswertung als Teil der Internetzone angenommen. Default: Objekte werden gleich behandelt wie andere Netzwerkobjekte.');
+INSERT INTO txt VALUES ('H5835', 'English', 'When enabled, dynamic and domain objects are considered part of the Internet zone in the compliance evaluation. Default: Objects are treated the same as other network objects.');
+INSERT INTO txt VALUES ('H5836', 'German', 'Wenn aktiviert, werden in compliance reports zusätzlich zu den aufgelösten Spalten für Sources, Destinations und Services Spalten mit der Kurzform angezeigt.');
+INSERT INTO txt VALUES ('H5836', 'English', 'When enabled, compliance reports will display columns with the short form in addition to the resolved columns for sources, destinations and services.');
+INSERT INTO txt VALUES ('H5837', 'German', 'Editieren der Compliance-Matrix. Rot: Erlaubte Kommunikation. Grün: Verbotene Kommunikation.');
+INSERT INTO txt VALUES ('H5837', 'English', 'Editing the compliance matrix. Red: allowed communication. Green: restricted communication.');
+INSERT INTO txt VALUES ('H5838', 'German', 'Editieren von importierten Matrizen nicht möglich.');
+INSERT INTO txt VALUES ('H5838', 'English', 'Editing imported matrices not possible.');
+INSERT INTO txt VALUES ('H5839', 'German', 'Matrixverletzung');
+INSERT INTO txt VALUES ('H5839', 'English', 'Matrix violation');
+INSERT INTO txt VALUES ('H5840', 'German', 'Verbotener Service');
+INSERT INTO txt VALUES ('H5840', 'English', 'Restricted Service');
+INSERT INTO txt VALUES ('H5841', 'German', 'Auswertbarkeitsproblem');
+INSERT INTO txt VALUES ('H5841', 'English', 'Assessability issue');
+INSERT INTO txt VALUES ('H5842', 'German', 'Wenn aktiviert, können importierte Matrizen weder gelöscht noch editiert werden.');
+INSERT INTO txt VALUES ('H5842', 'English', 'When enabled, imported matrices cannot be deleted or edited.');
+INSERT INTO txt VALUES ('H5843', 'German', 'Anzahl der Elemente die pro Anfrage geladen werden sollen.');
+INSERT INTO txt VALUES ('H5843', 'English', 'Amount of elements that should be retrieved with each fetch operation.');
+INSERT INTO txt VALUES ('H5844', 'German', 'Anzahl der Prozessoren die für Parallelisierungsoperationen zur Verfügung stehen.');
+INSERT INTO txt VALUES ('H5844', 'English', 'Amount of processors that should be used for parallelized operations.');
+
INSERT INTO txt VALUES ('H6001', 'German', 'Firewall Orchestrator verfügt über zwei APIs:
diff --git a/roles/database/files/sql/maintenance/fworch-change-to-delete-cascade.sql b/roles/database/files/sql/maintenance/fworch-change-to-delete-cascade.sql
index 0394036d36..fa38b251a4 100644
--- a/roles/database/files/sql/maintenance/fworch-change-to-delete-cascade.sql
+++ b/roles/database/files/sql/maintenance/fworch-change-to-delete-cascade.sql
@@ -4,7 +4,7 @@
----------------------------------------------------
adding cascade on delete constraints for all tables
-loesche alle Daten von Systemen, die zu einem Management gehören,
+loesche alle Daten von Systemen, die zu einem Management gehoeren,
das mit do_not_import=true und hide_in_gui=true markiert ist
execute: DELETE FROM management where do_not_import and hide_in_gui;
diff --git a/roles/database/files/upgrade/8.2.2.sql b/roles/database/files/upgrade/8.2.2.sql
index 3d7698f3e5..894127859f 100644
--- a/roles/database/files/upgrade/8.2.2.sql
+++ b/roles/database/files/upgrade/8.2.2.sql
@@ -34,4 +34,4 @@
*/
-insert into config (config_key, config_value, config_user) VALUES ('extTicketSystems', '[{"Url":"","TicketTemplate":"{\"ticket\":{\"subject\":\"@@TICKET_SUBJECT@@\",\"priority\":\"@@PRIORITY@@\",\"requester\":\"@@ONBEHALF@@\",\"domain_name\":\"\",\"workflow\":{\"name\":\"@@WORKFLOW_NAME@@\"},\"steps\":{\"step\":[{\"name\":\"Erfassung des Antrags\",\"tasks\":{\"task\":{\"fields\":{\"field\":[@@TASKS@@]}}}}]}}}","TasksTemplate":"{\"@xsi.type\":\"multi_access_request\",\"name\":\"GewünschterZugang\",\"read_only\":false,\"access_request\":{\"order\":\"AR1\",\"verifier_result\":{\"status\":\"notrun\"},\"use_topology\":true,\"targets\":{\"target\":{\"@type\":\"ANY\"}},\"users\":{\"user\":@@USERS@@},\"sources\":{\"source\":@@SOURCES@@},\"destinations\":{\"destination\":@@DESTINATIONS@@},\"services\":{\"service\":@@SERVICES@@},\"action\":\"@@ACTION@@\",\"labels\":\"\"}},{\"@xsi.type\":\"text_area\",\"name\":\"Grund für den Antrag\",\"read_only\":false,\"text\":\"@@REASON@@\"},{\"@xsi.type\":\"drop_down_list\",\"name\":\"Regel Log aktivieren?\",\"selection\":\"@@LOGGING@@\"},{\"@xsi.type\":\"date\",\"name\":\"Regel befristen bis:\"},{\"@xsi.type\":\"text_field\",\"name\":\"Anwendungs-ID\",\"text\":\"@@APPID@@\"},{\"@xsi.type\":\"checkbox\",\"name\":\"Die benötigte Kommunikationsverbindung ist im Kommunikationsprofil nach IT-Sicherheitsstandard hinterlegt\",\"value\":@@COM_DOCUMENTED@@},{\"@xsi.type\":\"drop_down_list\",\"name\":\"Expertenmodus: Exakt wie beantragt implementieren (Designervorschlag ignorieren)\",\"selection\":\"Nein\"}"}]', 0) ON CONFLICT DO NOTHING;
+insert into config (config_key, config_value, config_user) VALUES ('extTicketSystems', '[{"Url":"","TicketTemplate":"{\"ticket\":{\"subject\":\"@@TICKET_SUBJECT@@\",\"priority\":\"@@PRIORITY@@\",\"requester\":\"@@ONBEHALF@@\",\"domain_name\":\"\",\"workflow\":{\"name\":\"@@WORKFLOW_NAME@@\"},\"steps\":{\"step\":[{\"name\":\"Erfassung des Antrags\",\"tasks\":{\"task\":{\"fields\":{\"field\":[@@TASKS@@]}}}}]}}}","TasksTemplate":"{\"@xsi.type\":\"multi_access_request\",\"name\":\"GewuenschterZugang\",\"read_only\":false,\"access_request\":{\"order\":\"AR1\",\"verifier_result\":{\"status\":\"notrun\"},\"use_topology\":true,\"targets\":{\"target\":{\"@type\":\"ANY\"}},\"users\":{\"user\":@@USERS@@},\"sources\":{\"source\":@@SOURCES@@},\"destinations\":{\"destination\":@@DESTINATIONS@@},\"services\":{\"service\":@@SERVICES@@},\"action\":\"@@ACTION@@\",\"labels\":\"\"}},{\"@xsi.type\":\"text_area\",\"name\":\"Grund fuer den Antrag\",\"read_only\":false,\"text\":\"@@REASON@@\"},{\"@xsi.type\":\"drop_down_list\",\"name\":\"Regel Log aktivieren?\",\"selection\":\"@@LOGGING@@\"},{\"@xsi.type\":\"date\",\"name\":\"Regel befristen bis:\"},{\"@xsi.type\":\"text_field\",\"name\":\"Anwendungs-ID\",\"text\":\"@@APPID@@\"},{\"@xsi.type\":\"checkbox\",\"name\":\"Die benoetigte Kommunikationsverbindung ist im Kommunikationsprofil nach IT-Sicherheitsstandard hinterlegt\",\"value\":@@COM_DOCUMENTED@@},{\"@xsi.type\":\"drop_down_list\",\"name\":\"Expertenmodus: Exakt wie beantragt implementieren (Designervorschlag ignorieren)\",\"selection\":\"Nein\"}"}]', 0) ON CONFLICT DO NOTHING;
diff --git a/roles/database/files/upgrade/8.8.9.sql b/roles/database/files/upgrade/8.8.9.sql
index 90820183b1..95b7402a26 100644
--- a/roles/database/files/upgrade/8.8.9.sql
+++ b/roles/database/files/upgrade/8.8.9.sql
@@ -44,6 +44,66 @@ alter table owner add column if not exists next_recert_date Timestamp;
alter table owner drop constraint if exists owner_last_recertifier_uiuser_uiuser_id_f_key;
alter table owner add constraint owner_last_recertifier_uiuser_uiuser_id_f_key foreign key (last_recertifier) references uiuser (uiuser_id) on update restrict;
+
+DO $$
+BEGIN
+ -- recertification.owner_recert_id → owner_recertification(id)
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint c
+ JOIN pg_class t ON t.oid = c.conrelid
+ JOIN pg_namespace n ON n.oid = t.relnamespace
+ WHERE c.conname = 'recertification_owner_recertification_foreign_key'
+ AND t.relname = 'recertification'
+ AND n.nspname = current_schema()
+ ) THEN
+ ALTER TABLE recertification
+ ADD CONSTRAINT recertification_owner_recertification_foreign_key
+ FOREIGN KEY (owner_recert_id)
+ REFERENCES owner_recertification(id)
+ ON UPDATE RESTRICT
+ ON DELETE CASCADE;
+ END IF;
+
+ -- owner_recertification.owner_id → owner(id)
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint c
+ JOIN pg_class t ON t.oid = c.conrelid
+ JOIN pg_namespace n ON n.oid = t.relnamespace
+ WHERE c.conname = 'owner_recertification_owner_foreign_key'
+ AND t.relname = 'owner_recertification'
+ AND n.nspname = current_schema()
+ ) THEN
+ ALTER TABLE owner_recertification
+ ADD CONSTRAINT owner_recertification_owner_foreign_key
+ FOREIGN KEY (owner_id)
+ REFERENCES owner(id)
+ ON UPDATE RESTRICT
+ ON DELETE CASCADE;
+ END IF;
+
+ -- owner_recertification.report_id → report(report_id)
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint c
+ JOIN pg_class t ON t.oid = c.conrelid
+ JOIN pg_namespace n ON n.oid = t.relnamespace
+ WHERE c.conname = 'owner_recertification_report_foreign_key'
+ AND t.relname = 'owner_recertification'
+ AND n.nspname = current_schema()
+ ) THEN
+ ALTER TABLE owner_recertification
+ ADD CONSTRAINT owner_recertification_report_foreign_key
+ FOREIGN KEY (report_id)
+ REFERENCES report(report_id)
+ ON UPDATE RESTRICT
+ ON DELETE CASCADE;
+ END IF;
+END
+$$;
+
+
insert into config (config_key, config_value, config_user) VALUES ('modDecommEmailReceiver', 'None', 0) ON CONFLICT DO NOTHING;
insert into config (config_key, config_value, config_user) VALUES ('modDecommEmailSubject', '', 0) ON CONFLICT DO NOTHING;
insert into config (config_key, config_value, config_user) VALUES ('modDecommEmailBody', '', 0) ON CONFLICT DO NOTHING;
diff --git a/roles/database/files/upgrade/9.0.sql b/roles/database/files/upgrade/9.0.sql
new file mode 100644
index 0000000000..4a65dff012
--- /dev/null
+++ b/roles/database/files/upgrade/9.0.sql
@@ -0,0 +1,2022 @@
+-- next steps:
+ -- add rule_to, rule_service to importer
+ -- consolidate: not only first import but also subsequent imports should work
+ -- improve rollback - currently if import stops in the middle, the rollback is not automatically called
+
+-- pre 9.0 upgrade scripts
+
+--- 8.6.3
+
+insert into config (config_key, config_value, config_user) VALUES ('dnsLookup', 'False', 0) ON CONFLICT DO NOTHING;
+insert into config (config_key, config_value, config_user) VALUES ('overwriteExistingNames', 'False', 0) ON CONFLICT DO NOTHING;
+insert into config (config_key, config_value, config_user) VALUES ('autoReplaceAppServer', 'False', 0) ON CONFLICT DO NOTHING;
+
+ALTER TABLE modelling.change_history ADD COLUMN IF NOT EXISTS change_source Varchar default 'manual';
+
+
+CREATE TABLE IF NOT EXISTS refresh_log (
+ id SERIAL PRIMARY KEY,
+ view_name TEXT NOT NULL,
+ refreshed_at TIMESTAMPTZ DEFAULT now(),
+ status TEXT
+);
+
+CREATE OR REPLACE FUNCTION refresh_view_rule_with_owner()
+RETURNS SETOF refresh_log AS $$
+DECLARE
+ status_message TEXT;
+BEGIN
+ -- Attempt to refresh the materialized view
+ BEGIN
+ REFRESH MATERIALIZED VIEW view_rule_with_owner;
+ status_message := 'Materialized view refreshed successfully';
+ EXCEPTION
+ WHEN OTHERS THEN
+ status_message := format('Failed to refresh view: %s', SQLERRM);
+ END;
+
+ -- Log the operation
+ INSERT INTO refresh_log (view_name, status)
+ VALUES ('view_rule_with_owner', status_message);
+
+ -- Return the log entry
+ RETURN QUERY SELECT * FROM refresh_log WHERE view_name = 'view_rule_with_owner' ORDER BY refreshed_at DESC LIMIT 1;
+END;
+$$ LANGUAGE plpgsql VOLATILE;
+
+---
+--- 8.7.1
+
+Alter table "ldap_connection" ADD COLUMN IF NOT EXISTS "ldap_writepath_for_groups" Varchar;
+
+CREATE OR REPLACE FUNCTION insertLocalLdapWithEncryptedPasswords(
+ serverName TEXT,
+ port INTEGER,
+ userSearchPath TEXT,
+ roleSearchPath TEXT,
+ groupSearchPath TEXT,
+ groupWritePath TEXT,
+ tenantLevel INTEGER,
+ searchUser TEXT,
+ searchUserPwd TEXT,
+ writeUser TEXT,
+ writeUserPwd TEXT,
+ ldapType INTEGER
+) RETURNS VOID AS $$
+DECLARE
+ t_key TEXT;
+ t_encryptedReadPwd TEXT;
+ t_encryptedWritePwd TEXT;
+BEGIN
+ IF (SELECT 1 FROM ldap_connection WHERE ldap_server = serverName LIMIT 1) IS NULL
+ THEN
+ SELECT INTO t_key * FROM getMainKey();
+ SELECT INTO t_encryptedReadPwd * FROM encryptText(searchUserPwd, t_key);
+ SELECT INTO t_encryptedWritePwd * FROM encryptText(writeUserPwd, t_key);
+ INSERT INTO ldap_connection
+ (ldap_server, ldap_port, ldap_searchpath_for_users, ldap_searchpath_for_roles, ldap_searchpath_for_groups, ldap_writepath_for_groups,
+ ldap_tenant_level, ldap_search_user, ldap_search_user_pwd, ldap_write_user, ldap_write_user_pwd, ldap_type)
+ VALUES (serverName, port, userSearchPath, roleSearchPath, groupSearchPath, groupWritePath, tenantLevel, searchUser, t_encryptedReadPwd, writeUser, t_encryptedWritePwd, ldapType);
+ END IF;
+END;
+$$ LANGUAGE plpgsql;
+
+
+-- 8.7.2
+ALTER TABLE ext_request ADD COLUMN IF NOT EXISTS attempts int DEFAULT 0;
+insert into config (config_key, config_value, config_user) VALUES ('modModelledMarker', 'FWOC', 0) ON CONFLICT DO NOTHING;
+insert into config (config_key, config_value, config_user) VALUES ('modModelledMarkerLocation', 'rulename', 0) ON CONFLICT DO NOTHING;
+insert into config (config_key, config_value, config_user) VALUES ('ruleRecognitionOption', '{"nwRegardIp":true,"nwRegardName":false,"nwRegardGroupName":false,"nwResolveGroup":false,"svcRegardPortAndProt":true,"svcRegardName":false,"svcRegardGroupName":false,"svcResolveGroup":true}', 0) ON CONFLICT DO NOTHING;
+insert into config (config_key, config_value, config_user) VALUES ('availableReportTypes', '[1,2,3,4,5,6,7,8,9,10,21,22]', 0) ON CONFLICT DO NOTHING;
+
+-- 8.8.2
+insert into config (config_key, config_value, config_user) VALUES ('varianceAnalysisSleepTime', '0', 0) ON CONFLICT DO NOTHING;
+insert into config (config_key, config_value, config_user) VALUES ('varianceAnalysisStartAt', '00:00:00', 0) ON CONFLICT DO NOTHING;
+insert into config (config_key, config_value, config_user) VALUES ('varianceAnalysisSync', 'false', 0) ON CONFLICT DO NOTHING;
+insert into config (config_key, config_value, config_user) VALUES ('varianceAnalysisRefresh', 'false', 0) ON CONFLICT DO NOTHING;
+insert into config (config_key, config_value, config_user) VALUES ('resolveNetworkAreas', 'false', 0) ON CONFLICT DO NOTHING;
+
+-- 8.8.3
+ALTER TABLE modelling.connection ADD COLUMN IF NOT EXISTS requested_on_fw boolean default false;
+ALTER TABLE modelling.connection ADD COLUMN IF NOT EXISTS removed boolean default false;
+ALTER TABLE modelling.connection ADD COLUMN IF NOT EXISTS removal_date timestamp;
+UPDATE modelling.connection SET requested_on_fw=true WHERE NOT requested_on_fw;
+
+-- 8.8.4
+insert into stm_action (action_id,action_name) VALUES (30,'ask') ON CONFLICT DO NOTHING; -- cp
+
+-- 8.8.5
+ALTER TYPE rule_field_enum ADD VALUE IF NOT EXISTS 'modelled_source';
+ALTER TYPE rule_field_enum ADD VALUE IF NOT EXISTS 'modelled_destination';
+
+-- 8.8.6
+insert into stm_track (track_id,track_name) VALUES (23,'detailed log') ON CONFLICT DO NOTHING; -- check point R8x
+insert into stm_track (track_id,track_name) VALUES (24,'extended log') ON CONFLICT DO NOTHING; -- check point R8x
+
+-- 8.8.8
+DO $$
+BEGIN
+ IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'fwo_ro') THEN
+ CREATE ROLE fwo_ro WITH LOGIN NOSUPERUSER INHERIT NOCREATEDB NOCREATEROLE;
+ END IF;
+END
+$$;
+
+
+GRANT CONNECT ON DATABASE fworchdb TO fwo_ro;
+
+GRANT USAGE ON SCHEMA compliance TO fwo_ro;
+GRANT SELECT ON ALL TABLES IN SCHEMA compliance TO fwo_ro;
+GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA compliance TO fwo_ro;
+ALTER DEFAULT PRIVILEGES IN SCHEMA compliance GRANT SELECT ON TABLES TO fwo_ro;
+ALTER DEFAULT PRIVILEGES IN SCHEMA compliance GRANT USAGE, SELECT ON SEQUENCES TO fwo_ro;
+
+GRANT USAGE ON SCHEMA modelling TO fwo_ro;
+GRANT SELECT ON ALL TABLES IN SCHEMA modelling TO fwo_ro;
+GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA modelling TO fwo_ro;
+ALTER DEFAULT PRIVILEGES IN SCHEMA modelling GRANT SELECT ON TABLES TO fwo_ro;
+ALTER DEFAULT PRIVILEGES IN SCHEMA modelling GRANT USAGE, SELECT ON SEQUENCES TO fwo_ro;
+
+GRANT USAGE ON SCHEMA public TO fwo_ro;
+GRANT SELECT ON ALL TABLES IN SCHEMA public TO fwo_ro;
+GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO fwo_ro;
+ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO fwo_ro;
+ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT USAGE, SELECT ON SEQUENCES TO fwo_ro;
+
+GRANT USAGE ON SCHEMA request TO fwo_ro;
+GRANT SELECT ON ALL TABLES IN SCHEMA request TO fwo_ro;
+GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA request TO fwo_ro;
+ALTER DEFAULT PRIVILEGES IN SCHEMA request GRANT SELECT ON TABLES TO fwo_ro;
+ALTER DEFAULT PRIVILEGES IN SCHEMA request GRANT USAGE, SELECT ON SEQUENCES TO fwo_ro;
+
+-- 8.8.9
+
+ALTER TABLE recertification ADD column IF NOT EXISTS owner_recert_id BIGINT;
+
+create table if not exists owner_recertification
+(
+ id BIGSERIAL PRIMARY KEY,
+ owner_id int NOT NULL,
+ user_dn varchar,
+ recertified boolean default false,
+ recert_date Timestamp,
+ comment varchar,
+ next_recert_date Timestamp
+);
+
+create table if not exists notification
+(
+ id SERIAL PRIMARY KEY,
+ notification_client Varchar,
+ user_id int,
+ owner_id int,
+ channel Varchar,
+ recipient_to Varchar,
+ email_address_to Varchar,
+ recipient_cc Varchar,
+ email_address_cc Varchar,
+ email_subject Varchar,
+ layout Varchar,
+ deadline Varchar,
+ interval_before_deadline int,
+ offset_before_deadline int,
+ repeat_interval_after_deadline int,
+ repeat_offset_after_deadline int,
+ repetitions_after_deadline int,
+ last_sent Timestamp
+);
+
+alter table notification drop constraint if exists notification_owner_foreign_key;
+ALTER TABLE notification ADD CONSTRAINT notification_owner_foreign_key FOREIGN KEY (owner_id) REFERENCES owner(id) ON UPDATE RESTRICT ON DELETE CASCADE;
+alter table notification drop constraint if exists notification_user_foreign_key;
+ALTER TABLE notification ADD CONSTRAINT notification_user_foreign_key FOREIGN KEY (user_id) REFERENCES uiuser(uiuser_id) ON UPDATE RESTRICT ON DELETE CASCADE;
+
+alter table owner add column if not exists last_recertified Timestamp;
+alter table owner add column if not exists last_recertifier int;
+alter table owner add column if not exists last_recertifier_dn Varchar;
+alter table owner add column if not exists next_recert_date Timestamp;
+
+alter table owner drop constraint if exists owner_last_recertifier_uiuser_uiuser_id_f_key;
+alter table owner add constraint owner_last_recertifier_uiuser_uiuser_id_f_key foreign key (last_recertifier) references uiuser (uiuser_id) on update restrict;
+
+insert into config (config_key, config_value, config_user) VALUES ('modDecommEmailReceiver', 'None', 0) ON CONFLICT DO NOTHING;
+insert into config (config_key, config_value, config_user) VALUES ('modDecommEmailSubject', '', 0) ON CONFLICT DO NOTHING;
+insert into config (config_key, config_value, config_user) VALUES ('modDecommEmailBody', '', 0) ON CONFLICT DO NOTHING;
+
+alter table report add column if not exists read_only Boolean default FALSE;
+
+-- alter table owner_recertification drop constraint if exists owner_recertification_owner_foreign_key;
+-- ALTER TABLE owner_recertification ADD CONSTRAINT owner_recertification_owner_foreign_key FOREIGN KEY (owner_id) REFERENCES owner(id) ON UPDATE RESTRICT ON DELETE CASCADE;
+
+DO $$
+BEGIN
+ -- recertification.owner_recert_id → owner_recertification(id)
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint c
+ JOIN pg_class t ON t.oid = c.conrelid
+ JOIN pg_namespace n ON n.oid = t.relnamespace
+ WHERE c.conname = 'recertification_owner_recertification_foreign_key'
+ AND t.relname = 'recertification'
+ AND n.nspname = current_schema()
+ ) THEN
+ ALTER TABLE recertification
+ ADD CONSTRAINT recertification_owner_recertification_foreign_key
+ FOREIGN KEY (owner_recert_id)
+ REFERENCES owner_recertification(id)
+ ON UPDATE RESTRICT
+ ON DELETE CASCADE;
+ END IF;
+
+ -- owner_recertification.owner_id → owner(id)
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint c
+ JOIN pg_class t ON t.oid = c.conrelid
+ JOIN pg_namespace n ON n.oid = t.relnamespace
+ WHERE c.conname = 'owner_recertification_owner_foreign_key'
+ AND t.relname = 'owner_recertification'
+ AND n.nspname = current_schema()
+ ) THEN
+ ALTER TABLE owner_recertification
+ ADD CONSTRAINT owner_recertification_owner_foreign_key
+ FOREIGN KEY (owner_id)
+ REFERENCES owner(id)
+ ON UPDATE RESTRICT
+ ON DELETE CASCADE;
+ END IF;
+END
+$$;
+
+-- 8.9.1
+
+alter table report add column if not exists owner_id Integer;
+
+alter table report drop constraint if exists report_owner_foreign_key;
+ALTER TABLE report ADD CONSTRAINT report_owner_foreign_key FOREIGN KEY (owner_id) REFERENCES owner(id) ON UPDATE RESTRICT ON DELETE CASCADE;
+
+alter table owner_recertification add column if not exists report_id bigint;
+alter table owner_recertification drop constraint if exists owner_recertification_report_foreign_key;
+ALTER TABLE owner_recertification ADD CONSTRAINT owner_recertification_report_foreign_key FOREIGN KEY (report_id) REFERENCES report(report_id) ON UPDATE RESTRICT ON DELETE CASCADE;
+
+alter table owner add column if not exists recert_active boolean default false;
+
+alter table recertification add column if not exists owner_recert_id bigint;
+
+alter table recertification drop constraint if exists recertification_owner_recertification_foreign_key;
+ALTER TABLE recertification ADD CONSTRAINT recertification_owner_recertification_foreign_key FOREIGN KEY (owner_recert_id) REFERENCES owner_recertification(id) ON UPDATE RESTRICT ON DELETE CASCADE;
+
+-- 8.9.2
+CREATE TABLE if not exists owner_lifecycle_state (
+ id SERIAL PRIMARY KEY,
+ name Varchar NOT NULL
+);
+
+alter table owner add column if not exists owner_lifecycle_state_id int;
+
+alter table owner drop constraint if exists owner_owner_lifecycle_state_foreign_key;
+ALTER TABLE owner ADD CONSTRAINT owner_owner_lifecycle_state_foreign_key FOREIGN KEY (owner_lifecycle_state_id)REFERENCES owner_lifecycle_state(id) ON DELETE SET NULL;
+
+-- changes to nw obj ip constraints
+ALTER TABLE "object" DROP CONSTRAINT IF EXISTS "object_obj_ip_not_null" CASCADE;
+ALTER TABLE "object" DROP CONSTRAINT IF EXISTS "object_obj_ip_end_not_null" CASCADE;
+
+-- magic numbers here: 1 = host object, 3 = network object, 4 = range object
+ALTER TABLE "object" ADD CONSTRAINT object_obj_ip_not_null CHECK (NOT (obj_ip IS NULL AND obj_typ_id IN (1, 3, 4)));
+ALTER TABLE "object" ADD CONSTRAINT object_obj_ip_end_not_null CHECK (NOT (obj_ip_end IS NULL AND obj_typ_id IN (1, 3, 4)));
+
+-- 8.9.2
+CREATE TABLE if not exists owner_lifecycle_state (
+ id SERIAL PRIMARY KEY,
+ name Varchar NOT NULL
+);
+
+alter table owner add column if not exists owner_lifecycle_state_id int;
+
+alter table owner drop constraint if exists owner_owner_lifecycle_state_foreign_key;
+ALTER TABLE owner ADD CONSTRAINT owner_owner_lifecycle_state_foreign_key FOREIGN KEY (owner_lifecycle_state_id)REFERENCES owner_lifecycle_state(id) ON DELETE SET NULL;
+
+
+------------------------------------------------------------------------------------
+
+-- rename changes_found column to rule_changes_found in import_control table
+DO $$
+BEGIN
+ IF EXISTS (
+ SELECT 1
+ FROM information_schema.columns
+ WHERE table_name = 'import_control'
+ AND column_name = 'changes_found'
+ ) THEN
+ EXECUTE 'ALTER TABLE import_control RENAME COLUMN changes_found TO rule_changes_found';
+ END IF;
+END
+$$;
+
+-- add any_changes_found column to import_control table
+ALTER table "import_control"
+ ADD COLUMN IF NOT EXISTS "any_changes_found" Boolean Default FALSE;
+
+
+-- now set the any_changes_found column to true for all imports that have security relevant changes
+
+DROP VIEW IF EXISTS view_imports_with_security_relevant_changes;
+
+CREATE OR REPLACE VIEW view_imports_with_security_relevant_changes AS
+ SELECT clr.control_id AS import_id, clr.mgm_id
+ FROM changelog_rule clr
+ WHERE clr.security_relevant
+
+ UNION
+
+ SELECT clo.control_id AS import_id, clo.mgm_id
+ FROM changelog_object clo
+ WHERE clo.security_relevant
+
+ UNION
+
+ SELECT cls.control_id AS import_id, cls.mgm_id
+ FROM changelog_service cls
+ WHERE cls.security_relevant
+
+ UNION
+
+ SELECT clu.control_id AS import_id, clu.mgm_id
+ FROM changelog_user clu
+ WHERE clu.security_relevant;
+
+
+UPDATE import_control
+SET any_changes_found = true
+WHERE control_id IN (
+ SELECT import_id
+ FROM view_imports_with_security_relevant_changes
+);
+
+DROP VIEW IF EXISTS view_imports_with_security_relevant_changes;
+
+--- pre 9.0 changes (old import)
+
+DELETE FROM stm_dev_typ WHERE dev_typ_id IN (2,4,5,6,7);
+
+DROP TRIGGER IF EXISTS gw_route_add ON gw_route CASCADE;
+CREATE TRIGGER gw_route_add BEFORE INSERT ON gw_route FOR EACH ROW EXECUTE PROCEDURE gw_route_add();
+
+CREATE OR REPLACE FUNCTION import_config_from_json ()
+ RETURNS TRIGGER
+ AS $BODY$
+DECLARE
+ i_mgm_id INTEGER;
+ i_count INTEGER;
+BEGIN
+ -- networking
+ SELECT INTO i_mgm_id mgm_id FROM import_control WHERE control_id=NEW.import_id;
+ -- before importing, delete all old interfaces and routes belonging to the current management:
+
+ -- now re-insert the currently found interfaces:
+ SELECT INTO i_count COUNT(*) FROM jsonb_populate_recordset(NULL::gw_interface, NEW.config -> 'interfaces');
+ IF i_count>0 THEN
+ DELETE FROM gw_interface WHERE routing_device IN
+ (SELECT dev_id FROM device LEFT JOIN management ON (device.mgm_id=management.mgm_id) WHERE management.mgm_id=i_mgm_id);
+ INSERT INTO gw_interface SELECT * FROM jsonb_populate_recordset(NULL::gw_interface, NEW.config -> 'interfaces');
+ END IF;
+
+ SELECT INTO i_count COUNT(*) FROM jsonb_populate_recordset(NULL::gw_route, NEW.config -> 'routing');
+ IF i_count>0 THEN
+ DELETE FROM gw_route WHERE routing_device IN
+ (SELECT dev_id FROM device LEFT JOIN management ON (device.mgm_id=management.mgm_id) WHERE management.mgm_id=i_mgm_id);
+ -- now re-insert the currently found routes:
+ INSERT INTO gw_route SELECT * FROM jsonb_populate_recordset(NULL::gw_route, NEW.config -> 'routing');
+ END IF;
+
+ -- firewall objects and rules
+
+ INSERT INTO import_object
+ SELECT
+ *
+ FROM
+ jsonb_populate_recordset(NULL::import_object, NEW.config -> 'network_objects');
+
+ INSERT INTO import_service
+ SELECT
+ *
+ FROM
+ jsonb_populate_recordset(NULL::import_service, NEW.config -> 'service_objects');
+
+ INSERT INTO import_user
+ SELECT
+ *
+ FROM
+ jsonb_populate_recordset(NULL::import_user, NEW.config -> 'user_objects');
+
+ INSERT INTO import_zone
+ SELECT
+ *
+ FROM
+ jsonb_populate_recordset(NULL::import_zone, NEW.config -> 'zone_objects');
+
+ INSERT INTO import_rule
+ SELECT
+ *
+ FROM
+ jsonb_populate_recordset(NULL::import_rule, NEW.config -> 'rules');
+
+ IF NEW.start_import_flag THEN
+ -- finally start the stored procedure import
+ PERFORM import_all_main(NEW.import_id, NEW.debug_mode);
+ END IF;
+ RETURN NEW;
+END;
+$BODY$
+LANGUAGE plpgsql
+VOLATILE;
+ALTER FUNCTION public.import_config_from_json () OWNER TO fworch;
+
+DROP TRIGGER IF EXISTS import_config_insert ON import_config CASCADE;
+
+CREATE TRIGGER import_config_insert
+ BEFORE INSERT ON import_config
+ FOR EACH ROW
+ EXECUTE PROCEDURE import_config_from_json ();
+
+---------------------------------------------------------------------------------------------
+-- new import
+
+ALTER TABLE management ADD COLUMN IF NOT EXISTS "mgm_uid" Varchar NOT NULL DEFAULT '';
+ALTER TABLE management ADD COLUMN IF NOT EXISTS "rulebase_name" Varchar NOT NULL DEFAULT '';
+ALTER TABLE management ADD COLUMN IF NOT EXISTS "rulebase_uid" Varchar NOT NULL DEFAULT '';
+-- Alter table rule_metadata add column if not exists rulebase_id integer; -- not null;
+
+ALTER TABLE device ADD COLUMN IF NOT EXISTS "dev_uid" Varchar NOT NULL DEFAULT '';
+
+Alter table stm_action add column if not exists allowed BOOLEAN NOT NULL DEFAULT TRUE;
+UPDATE stm_action SET allowed = FALSE WHERE action_name = 'deny' OR action_name = 'drop' OR action_name = 'reject';
+
+Create table IF NOT EXISTS "rulebase"
+(
+ "id" SERIAL primary key,
+ "name" Varchar NOT NULL,
+ "uid" Varchar NOT NULL,
+ "mgm_id" Integer NOT NULL,
+ "is_global" BOOLEAN DEFAULT FALSE NOT NULL,
+ "created" BIGINT,
+ "removed" BIGINT
+);
+
+ALTER TABLE "rulebase" DROP CONSTRAINT IF EXISTS "fk_rulebase_mgm_id" CASCADE;
+Alter table "rulebase" add CONSTRAINT fk_rulebase_mgm_id foreign key ("mgm_id") references "management" ("mgm_id") on update restrict on delete cascade;
+
+ALTER TABLE "rulebase" DROP CONSTRAINT IF EXISTS "unique_rulebase_mgm_id_name" CASCADE;
+ALTER TABLE "rulebase" DROP CONSTRAINT IF EXISTS "unique_rulebase_mgm_id_uid" CASCADE;
+Alter table "rulebase" add CONSTRAINT unique_rulebase_mgm_id_uid UNIQUE ("mgm_id", "uid");
+-----------------------------------------------
+
+ALTER TABLE "management" ADD COLUMN IF NOT EXISTS "is_super_manager" BOOLEAN DEFAULT FALSE;
+ALTER TABLE "rule" ADD COLUMN IF NOT EXISTS "is_global" BOOLEAN DEFAULT FALSE NOT NULL;
+ALTER TABLE "rule" ADD COLUMN IF NOT EXISTS "rulebase_id" INTEGER;
+
+-- permanent table for storing latest config to calc diffs
+CREATE TABLE IF NOT EXISTS "latest_config" (
+ "mgm_id" integer NOT NULL,
+ "import_id" bigint NOT NULL,
+ "config" jsonb NOT NULL,
+ PRIMARY KEY ("mgm_id")
+);
+
+
+-- Drop old primary key if it exists
+DO $$
+BEGIN
+ IF EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conrelid = 'latest_config'::regclass
+ AND contype = 'p'
+ ) THEN
+ ALTER TABLE latest_config DROP CONSTRAINT latest_config_pkey;
+ END IF;
+END $$;
+
+-- Add new primary key on mgm_id if not already set
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conrelid = 'latest_config'::regclass
+ AND contype = 'p'
+ AND conkey = ARRAY[
+ (SELECT attnum FROM pg_attribute
+ WHERE attrelid = 'latest_config'::regclass
+ AND attname = 'mgm_id')
+ ]
+ ) THEN
+ ALTER TABLE latest_config ADD CONSTRAINT latest_config_pkey PRIMARY KEY (mgm_id);
+ END IF;
+END $$;
+
+
+ALTER table "import_control" ADD COLUMN IF NOT EXISTS "is_full_import" BOOLEAN DEFAULT FALSE;
+
+-----------------------------------------------
+
+Create Table IF NOT EXISTS "rule_enforced_on_gateway"
+(
+ "rule_id" Integer NOT NULL,
+ "dev_id" Integer, -- NULL if rule is available for all gateways of its management
+ "created" BIGINT,
+ "removed" BIGINT
+);
+
+ALTER TABLE "rule_enforced_on_gateway"
+ DROP CONSTRAINT IF EXISTS "fk_rule_enforced_on_gateway_rule_rule_id" CASCADE;
+Alter table "rule_enforced_on_gateway" add CONSTRAINT fk_rule_enforced_on_gateway_rule_rule_id foreign key ("rule_id") references "rule" ("rule_id") on update restrict on delete cascade;
+
+ALTER TABLE "rule_enforced_on_gateway"
+ DROP CONSTRAINT IF EXISTS "fk_rule_enforced_on_gateway_device_dev_id" CASCADE;
+Alter table "rule_enforced_on_gateway" add CONSTRAINT fk_rule_enforced_on_gateway_device_dev_id foreign key ("dev_id") references "device" ("dev_id") on update restrict on delete cascade;
+
+ALTER TABLE "rule_enforced_on_gateway"
+ DROP CONSTRAINT IF EXISTS "fk_rule_enforced_on_gateway_created_import_control_control_id" CASCADE;
+Alter table "rule_enforced_on_gateway" add CONSTRAINT fk_rule_enforced_on_gateway_created_import_control_control_id
+ foreign key ("created") references "import_control" ("control_id") on update restrict on delete cascade;
+
+ALTER TABLE "rule_enforced_on_gateway"
+ DROP CONSTRAINT IF EXISTS "fk_rule_enforced_on_gateway_removed_import_control_control_id" CASCADE;
+
+-- just temp for migration purposes - will be removed later
+ALTER TABLE "rule_enforced_on_gateway"
+ DROP CONSTRAINT IF EXISTS "fk_rule_enforced_on_gateway_deleted_import_control_control_id" CASCADE;
+
+Alter table "rule_enforced_on_gateway" add CONSTRAINT fk_rule_enforced_on_gateway_removed_import_control_control_id
+ foreign key ("removed") references "import_control" ("control_id") on update restrict on delete cascade;
+
+-----------------------------------------------
+
+CREATE OR REPLACE FUNCTION get_next_rule_number_after_uid(mgmId int, current_rule_uid text)
+RETURNS NUMERIC AS $$
+ SELECT r.rule_num_numeric as ruleNumber
+ FROM rule r
+ WHERE r.mgm_id = mgmId and active
+ AND r.rule_num_numeric > (
+ SELECT rule_num_numeric
+ FROM rule
+ WHERE rule_uid = current_rule_uid AND mgm_id = mgmId AND active
+ LIMIT 1
+ )
+ ORDER BY r.rule_num_numeric ASC
+ LIMIT 1;
+$$ LANGUAGE sql;
+
+ALTER table "svcgrp_flat" ALTER COLUMN "svcgrp_flat_id" TYPE BIGINT;
+ALTER table "svcgrp_flat" ALTER COLUMN "svcgrp_flat_member_id" TYPE BIGINT;
+ALTER table "svcgrp_flat" ALTER COLUMN "import_created" TYPE BIGINT;
+ALTER table "svcgrp_flat" ALTER COLUMN "import_last_seen" TYPE BIGINT;
+
+ALTER TABLE "rule" DROP CONSTRAINT IF EXISTS "fk_rule_rulebase_id" CASCADE;
+ALTER TABLE "rule" ADD CONSTRAINT fk_rule_rulebase_id FOREIGN KEY ("rulebase_id") REFERENCES "rulebase" ("id") ON UPDATE RESTRICT ON DELETE CASCADE;
+
+-- Alter Table "rule_metadata" ADD Constraint "rule_metadata_alt_key" UNIQUE ("rule_uid", "dev_id", "rulebase_id");
+-- TODO: this needs to analysed (as dev_id will be removed from rule):
+-- Alter table "rule" add constraint "rule_metadata_dev_id_rule_uid_f_key"
+-- foreign key ("dev_id", "rule_uid", "rulebase_id") references "rule_metadata" ("dev_id", "rule_uid", "rulebase_id") on update restrict on delete cascade;
+
+-- Create table IF NOT EXISTS "rule_hit"
+-- (
+-- "rule_id" BIGINT NOT NULL,
+-- "rule_uid" VARCHAR NOT NULL,
+-- "gw_id" INTEGER NOT NULL,
+-- "metadata_id" BIGINT NOT NULL,
+-- "rule_first_hit" Timestamp,
+-- "rule_last_hit" Timestamp,
+-- "rule_hit_counter" BIGINT
+-- );
+-- Alter table "rule_hit" DROP CONSTRAINT IF EXISTS fk_rule_hit_rule_id;
+-- Alter table "rule_hit" DROP CONSTRAINT IF EXISTS fk_hit_gw_id;
+-- Alter table "rule_hit" DROP CONSTRAINT IF EXISTS fk_hit_metadata_id;
+-- Alter table "rule_hit" add CONSTRAINT fk_hit_rule_id foreign key ("rule_id") references "rule" ("rule_id") on update restrict on delete cascade;
+-- Alter table "rule_hit" add CONSTRAINT fk_hit_gw_id foreign key ("gw_id") references "device" ("dev_id") on update restrict on delete cascade;
+-- Alter table "rule_hit" add CONSTRAINT fk_hit_metadata_id foreign key ("metadata_id") references "rule_metadata" ("dev_id") on update restrict on delete cascade;
+
+-----------------------------------------------
+-- METADATA part
+-- we are removing dev_id and rulebase_id from rule_metadata
+-- even CP API does not provide this information regarding hits (the target parameter is ignored, so hits are returned per rule not per rule per gw)
+
+Alter table "rule" drop constraint IF EXISTS "rule_metadata_dev_id_rule_uid_f_key";
+Alter Table "rule_metadata" drop Constraint IF EXISTS "rule_metadata_alt_key";
+
+ -- TODO: fix this:
+ -- ALTER TABLE rule_metadata DROP Constraint IF EXISTS "rule_metadata_rule_uid_unique";
+ -- ALTER TABLE rule_metadata ADD Constraint "rule_metadata_rule_uid_unique" unique ("rule_uid");
+ -- causes error:
+ -- None: FEHLER: kann Constraint rule_metadata_rule_uid_unique für Tabelle rule_metadata nicht löschen, weil andere Objekte davon abhängen\nDETAIL:
+ -- Constraint rule_metadata_rule_uid_f_key für Tabelle rule hängt von Index rule_metadata_rule_uid_unique ab\nHINT: Verwenden Sie DROP ... CASCADE, um die abhängigen Objekte ebenfalls zu löschen.\n"}
+
+ALTER TABLE rule_metadata DROP Constraint IF EXISTS "rule_metadata_rule_uid_unique" CASCADE;
+ALTER TABLE rule_metadata ADD Constraint "rule_metadata_rule_uid_unique" unique ("rule_uid");
+Alter table "rule" DROP constraint IF EXISTS "rule_rule_metadata_rule_uid_f_key";
+
+-- wenn es regeln mit derselben uid gibt, funktioniert der folgende constraint nicht
+-- koennen wir dafuer sorgen, dass jede rule_uid nur exakt 1x in der datenbank steht?
+-- brauchen wir zusaetzlich die einschraenkung auf das mgm?
+-- mindestens gibt es ein Problem mit den (implicit) NAT Regeln: CP_default_Office_Mode_addresses_pool
+-- rule_id | last_change_admin | rule_name | mgm_id | parent_rule_id | parent_rule_type | active | rule_num | rule_num_numeric | rule_ruleid | rule_uid | rule_disabled | rule_src_neg | rule_dst_neg | rule_svc_neg | action_id | track_id | rule_src | rule_dst | rule_svc | rule_src_refs | rule_dst_refs | rule_svc_refs | rule_from_zone | rule_to_zone | rule_action | rule_track | rule_installon | rule_time | rule_comment | rule_head_text | rule_implied | rule_create | rule_last_seen | dev_id | rule_custom_fields | access_rule | nat_rule | xlate_rule
+-----------+-------------------+-----------+--------+----------------+------------------+--------+----------+------------------+-------------+--------------------------------------+---------------+--------------+--------------+--------------+-----------+----------+---------------------------------------+----------+----------+--------------------------------------+--------------------------------------+--------------------------------------+----------------+--------------+-------------+------------+----------------+-----------+--------------+----------------+--------------+-------------+----------------+--------+--------------------+-------------+----------+------------
+-- 274 | | | 19 | | | t | 10 | 17000.00000000 | | dc1a7110-e431-4f56-a84a-31b17acf7ee7 | f | f | f | f | 2 | 2 | CP_default_Office_Mode_addresses_pool | Any | Any | e7c5a3b6-e20f-4756-bb56-f2b394baf7a9 | 97aeb369-9aea-11d5-bd16-0090272ccb30 | 97aeb369-9aea-11d5-bd16-0090272ccb30 | | | drop | None | Policy Targets | Any | | | f | 19 | 19 | 19 | | f | t | 261
+
+Alter table "rule" add constraint "rule_rule_metadata_rule_uid_f_key"
+ foreign key ("rule_uid") references "rule_metadata" ("rule_uid") on update restrict on delete cascade;
+
+
+CREATE OR REPLACE VIEW v_rule_with_rule_owner AS
+ SELECT r.rule_id, ow.id as owner_id, ow.name as owner_name, 'rule' AS matches,
+ ow.recert_interval, met.rule_last_certified, met.rule_last_certifier
+ FROM v_active_access_allow_rules r
+ LEFT JOIN rule_metadata met ON (r.rule_uid=met.rule_uid)
+ LEFT JOIN rule_owner ro ON (ro.rule_metadata_id=met.rule_metadata_id)
+ LEFT JOIN owner ow ON (ro.owner_id=ow.id)
+ WHERE NOT ow.id IS NULL
+ GROUP BY r.rule_id, ow.id, ow.name, met.rule_last_certified, met.rule_last_certifier;
+
+CREATE OR REPLACE VIEW v_rule_with_src_owner AS
+ SELECT
+ r.rule_id, ow.id as owner_id, ow.name as owner_name,
+ CASE
+ WHEN onw.ip = onw.ip_end
+ THEN SPLIT_PART(CAST(onw.ip AS VARCHAR), '/', 1) -- Single IP overlap, removing netmask
+ ELSE
+ CASE WHEN -- range is a single network
+ host(broadcast(inet_merge(onw.ip, onw.ip_end))) = host (onw.ip_end) AND
+ host(inet_merge(onw.ip, onw.ip_end)) = host (onw.ip)
+ THEN
+ text(inet_merge(onw.ip, onw.ip_end))
+ ELSE
+ CONCAT(SPLIT_PART(onw.ip::VARCHAR,'/', 1), '-', SPLIT_PART(onw.ip_end::VARCHAR, '/', 1))
+ END
+ END AS matching_ip,
+ 'source' AS match_in,
+ ow.recert_interval, met.rule_last_certified, met.rule_last_certifier
+ FROM v_active_access_allow_rules r
+ LEFT JOIN rule_from ON (r.rule_id=rule_from.rule_id)
+ LEFT JOIN objgrp_flat of ON (rule_from.obj_id=of.objgrp_flat_id)
+ LEFT JOIN object o ON (of.objgrp_flat_member_id=o.obj_id)
+ LEFT JOIN owner_network onw ON (onw.ip_end >= o.obj_ip AND onw.ip <= o.obj_ip_end)
+ LEFT JOIN owner ow ON (onw.owner_id=ow.id)
+ LEFT JOIN rule_metadata met ON (r.rule_uid=met.rule_uid)
+ WHERE r.rule_id NOT IN (SELECT distinct rwo.rule_id FROM v_rule_with_rule_owner rwo) AND
+ CASE
+ when (select mode from v_rule_ownership_mode) = 'exclusive' then (NOT o.obj_ip IS NULL) AND o.obj_ip NOT IN (select * from v_excluded_src_ips)
+ else NOT o.obj_ip IS NULL
+ END
+ GROUP BY r.rule_id, o.obj_ip, o.obj_ip_end, onw.ip, onw.ip_end, ow.id, ow.name, met.rule_last_certified, met.rule_last_certifier;
+
+CREATE OR REPLACE VIEW v_rule_with_dst_owner AS
+ SELECT
+ r.rule_id, ow.id as owner_id, ow.name as owner_name,
+ CASE
+ WHEN onw.ip = onw.ip_end
+ THEN SPLIT_PART(CAST(onw.ip AS VARCHAR), '/', 1) -- Single IP overlap, removing netmask
+ ELSE
+ CASE WHEN -- range is a single network
+ host(broadcast(inet_merge(onw.ip, onw.ip_end))) = host (onw.ip_end) AND
+ host(inet_merge(onw.ip, onw.ip_end)) = host (onw.ip)
+ THEN
+ text(inet_merge(onw.ip, onw.ip_end))
+ ELSE
+ CONCAT(SPLIT_PART(onw.ip::VARCHAR,'/', 1), '-', SPLIT_PART(onw.ip_end::VARCHAR, '/', 1))
+ END
+ END AS matching_ip,
+ 'destination' AS match_in,
+ ow.recert_interval, met.rule_last_certified, met.rule_last_certifier
+ FROM v_active_access_allow_rules r
+ LEFT JOIN rule_to rt ON (r.rule_id=rt.rule_id)
+ LEFT JOIN objgrp_flat of ON (rt.obj_id=of.objgrp_flat_id)
+ LEFT JOIN object o ON (of.objgrp_flat_member_id=o.obj_id)
+ LEFT JOIN owner_network onw ON (onw.ip_end >= o.obj_ip AND onw.ip <= o.obj_ip_end)
+ LEFT JOIN owner ow ON (onw.owner_id=ow.id)
+ LEFT JOIN rule_metadata met ON (r.rule_uid=met.rule_uid)
+ WHERE r.rule_id NOT IN (SELECT distinct rwo.rule_id FROM v_rule_with_rule_owner rwo) AND
+ CASE
+ when (select mode from v_rule_ownership_mode) = 'exclusive' then (NOT o.obj_ip IS NULL) AND o.obj_ip NOT IN (select * from v_excluded_dst_ips)
+ else NOT o.obj_ip IS NULL
+ END
+ GROUP BY r.rule_id, o.obj_ip, o.obj_ip_end, onw.ip, onw.ip_end, ow.id, ow.name, met.rule_last_certified, met.rule_last_certifier;
+
+
+ALTER TABLE rule_metadata DROP COLUMN IF EXISTS "rulebase_id";
+ALTER TABLE rule_metadata DROP COLUMN IF EXISTS "dev_id";
+
+-----------------------------------------------
+-- bulid rule-rulebase graph
+-- rules may spawn rulebase children with new link table rulebase_link
+
+Create table IF NOT EXISTS "stm_link_type"
+(
+ "id" SERIAL primary key,
+ "name" Varchar NOT NULL
+);
+
+Create table IF NOT EXISTS "rulebase_link"
+(
+ "id" SERIAL primary key,
+ "gw_id" Integer,
+ "from_rulebase_id" Integer, -- either from_rulebase_id or from_rule_id must be SET or the is_initial flag
+ "from_rule_id" BIGINT,
+ "to_rulebase_id" Integer NOT NULL,
+ "link_type" Integer,
+ "is_initial" BOOLEAN DEFAULT FALSE,
+ "is_global" BOOLEAN DEFAULT FALSE,
+ "is_section" BOOLEAN DEFAULT TRUE,
+ "created" BIGINT,
+ "removed" BIGINT
+);
+
+
+
+
+-- only for developers who already have on old 9.0 database:
+Alter table "rulebase_link" add column IF NOT EXISTS "is_initial" BOOLEAN;
+Alter table "rulebase_link" add column IF NOT EXISTS "is_global" BOOLEAN;
+Alter table "rulebase_link" add column IF NOT EXISTS "from_rulebase_id" Integer;
+Alter table "rulebase_link" add column IF NOT EXISTS "is_section" BOOLEAN DEFAULT TRUE;
+
+DO $$
+BEGIN
+ IF EXISTS (
+ SELECT 1
+ FROM information_schema.columns
+ WHERE table_name = 'rulebase_link'
+ AND column_name = 'from_rule_id'
+ AND data_type = 'integer'
+ ) THEN
+ Alter table "rulebase_link" alter column "from_rule_id" TYPE bigint;
+ END IF;
+END
+$$;
+---
+
+Alter table "rulebase_link" drop constraint IF EXISTS "fk_rulebase_link_from_rulebase_id";
+Alter table "rulebase_link" add constraint "fk_rulebase_link_from_rulebase_id" foreign key ("from_rulebase_id") references "rulebase" ("id") on update restrict on delete cascade;
+Alter table "rulebase_link" drop constraint IF EXISTS "fk_rulebase_link_to_rulebase_id";
+Alter table "rulebase_link" add constraint "fk_rulebase_link_to_rulebase_id" foreign key ("to_rulebase_id") references "rulebase" ("id") on update restrict on delete cascade;
+Alter table "rulebase_link" drop constraint IF EXISTS "fk_rulebase_link_from_rule_id";
+Alter table "rulebase_link" add constraint "fk_rulebase_link_from_rule_id" foreign key ("from_rule_id") references "rule" ("rule_id") on update restrict on delete cascade;
+Alter table "rulebase_link" drop constraint IF EXISTS "fk_rulebase_link_link_type";
+Alter table "rulebase_link" add constraint "fk_rulebase_link_link_type" foreign key ("link_type") references "stm_link_type" ("id") on update restrict on delete cascade;
+Alter table "rulebase_link" drop constraint IF EXISTS "fk_rulebase_link_gw_id";
+Alter table "rulebase_link" add constraint "fk_rulebase_link_gw_id" foreign key ("gw_id") references "device" ("dev_id") on update restrict on delete cascade;
+Alter table "rulebase_link" drop constraint IF EXISTS "unique_rulebase_link";
+Alter table "rulebase_link" add CONSTRAINT unique_rulebase_link
+ UNIQUE (
+ "gw_id",
+ "from_rulebase_id",
+ "from_rule_id",
+ "to_rulebase_id",
+ "created"
+ );
+
+ALTER TABLE "rulebase_link"
+ DROP CONSTRAINT IF EXISTS "fk_rulebase_link_created_import_control_control_id" CASCADE;
+Alter table "rulebase_link" add CONSTRAINT fk_rulebase_link_created_import_control_control_id
+ foreign key ("created") references "import_control" ("control_id") on update restrict on delete cascade;
+ALTER TABLE "rulebase_link"
+ DROP CONSTRAINT IF EXISTS "fk_rulebase_link_removed_import_control_control_id" CASCADE;
+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;
+
+insert into stm_link_type (id, name) VALUES (2, 'ordered') ON CONFLICT DO NOTHING;
+insert into stm_link_type (id, name) VALUES (3, 'inline') ON CONFLICT DO NOTHING;
+insert into stm_link_type (id, name) VALUES (4, 'concatenated') ON CONFLICT DO NOTHING;
+insert into stm_link_type (id, name) VALUES (5, 'domain') ON CONFLICT DO NOTHING;
+delete from stm_link_type where name in ('initial','global','local','section'); -- initial and global/local are additional flags now
+
+-- TODO delete all rule.parent_rule_id and rule.parent_rule_type, always = None so far
+ -- migration plan:
+ -- 1) create rulebases without rules (derive from device table)
+ -- 2) set rule.rulebase_id to reference the correct rulebase
+ -- 3) set not null constratint for rule.rulebase_id
+ -- 4) do we really dare to delete duplicate rules here? yes, we should.
+ -- 5) after upgrade start import
+ -- TODO: deal with global policies --> move them to the global mgm_id
+
+CREATE OR REPLACE FUNCTION deleteDuplicateRulebases() RETURNS VOID
+ LANGUAGE plpgsql
+ VOLATILE
+AS $function$
+ BEGIN
+ -- TODO: make sure that we have at least one rulebase for each device
+ DELETE FROM rule WHERE rulebase_id IS NULL;
+ END;
+$function$;
+
+-- get latest import id for this management
+
+-- needs to be rewritten to rulebase_link
+CREATE OR REPLACE FUNCTION addRuleEnforcedOnGatewayEntries() RETURNS VOID
+ LANGUAGE plpgsql
+ VOLATILE
+AS $function$
+ DECLARE
+ r_rulebase RECORD;
+ r_rule RECORD;
+ r_dev_null RECORD;
+ i_dev_id INTEGER;
+ a_all_dev_ids_of_rulebase INTEGER[];
+ a_target_gateways VARCHAR[];
+ v_gw_name VARCHAR;
+ BEGIN
+ FOR r_rulebase IN
+ SELECT * FROM rulebase
+ LOOP
+ -- collect all device ids for this rulebase
+ SELECT ARRAY(
+ SELECT gw_id FROM rulebase_link
+ WHERE to_rulebase_id=r_rulebase.id
+ ) INTO a_all_dev_ids_of_rulebase;
+
+ FOR r_rule IN
+ SELECT rule_installon, rule_id FROM rule
+ LOOP
+ -- depending on install_on field:
+ -- add enry for all gateways of the management
+ -- or just add specific gateway entries
+ IF r_rule.rule_installon='Policy Targets' THEN
+ -- need to find out other platforms equivivalent keywords
+ FOREACH i_dev_id IN ARRAY a_all_dev_ids_of_rulebase
+ LOOP
+ INSERT INTO rule_enforced_on_gateway (rule_id, dev_id, created)
+ VALUES (r_rule.rule_id, i_dev_id, (SELECT * FROM get_last_import_id_for_mgmt(r_rulebase.mgm_id)));
+ END LOOP;
+ ELSE
+ -- need to deal with entries separately - split rule_installon field by '|'
+ IF r_rule.rule_installon IS NULL OR btrim(r_rule.rule_installon) = '' THEN
+ r_rule.rule_installon := 'Policy Targets';
+ END IF;
+ SELECT ARRAY(
+ SELECT string_to_array(r_rule.rule_installon, '|')
+ ) INTO a_target_gateways;
+ FOREACH v_gw_name IN ARRAY a_target_gateways
+ LOOP
+ -- get dev_id for gw_name
+ SELECT INTO i_dev_id dev_id FROM device WHERE dev_name=v_gw_name;
+ IF FOUND THEN
+ INSERT INTO rule_enforced_on_gateway (rule_id, dev_id, created)
+ VALUES (r_rule.rule_id, i_dev_id, (SELECT * FROM get_last_import_id_for_mgmt(r_rulebase.mgm_id)));
+ ELSE
+ -- decide what to do with misses
+ END IF;
+ END LOOP;
+ END IF;
+ END LOOP;
+ END LOOP;
+ END;
+$function$;
+
+CREATE OR REPLACE FUNCTION addMetadataRulebaseEntries() RETURNS VOID
+ LANGUAGE plpgsql
+ VOLATILE
+AS $function$
+ DECLARE
+ r_dev RECORD;
+ BEGIN
+ FOR r_dev IN
+ -- TODO: deal with global rulebases here
+ SELECT d.dev_id, rb.id as rulebase_id FROM device d LEFT JOIN rulebase rb ON (d.local_rulebase_name=rb.name)
+ LOOP
+ UPDATE rule_metadata SET rulebase_id=r_dev.rulebase_id WHERE rule_metadata.dev_id=r_dev.dev_id;
+ END LOOP;
+ -- now we can add the "not null" constraint for rule_metadata.rulebase_id
+ IF EXISTS (
+ SELECT 1
+ FROM information_schema.columns
+ WHERE table_name = 'rule_metadata'
+ AND column_name = 'rulebase_id'
+ AND is_nullable = 'YES'
+ ) THEN
+ ALTER TABLE rule_metadata
+ ALTER COLUMN rulebase_id SET NOT NULL;
+ END IF;
+ END;
+$function$;
+
+-- TODO: set created to current import id
+CREATE OR REPLACE FUNCTION addRulebaseLinkEntries() RETURNS VOID
+ LANGUAGE plpgsql
+ VOLATILE
+AS $function$
+ DECLARE
+ r_dev RECORD;
+ r_dev_null RECORD;
+ i_rulebase_id INTEGER;
+ i_initial_rulebase_id INTEGER;
+ BEGIN
+ FOR r_dev IN
+ SELECT * FROM device
+ LOOP
+ -- find the id of the matching rulebase
+ SELECT INTO i_rulebase_id id FROM rulebase WHERE name=r_dev.local_rulebase_name AND mgm_id=r_dev.mgm_id;
+ -- check if rulebase_link already exists
+ IF i_rulebase_id IS NOT NULL THEN
+ SELECT INTO r_dev_null * FROM rulebase_link WHERE to_rulebase_id=i_rulebase_id AND gw_id=r_dev.dev_id AND removed IS NULL;
+ IF NOT FOUND THEN
+ INSERT INTO rulebase_link (gw_id, from_rule_id, to_rulebase_id, created, link_type, is_initial)
+ VALUES (r_dev.dev_id, NULL, i_rulebase_id, (SELECT * FROM get_last_import_id_for_mgmt(r_dev.mgm_id)), 2, True)
+ RETURNING id INTO i_initial_rulebase_id; -- when migrating, there cannot be more than one (the initial) rb per device
+ END IF;
+ END IF;
+
+ -- global rulebase:
+ -- find the id of the matching rulebase
+ IF r_dev.global_rulebase_name IS NOT NULL THEN
+ SELECT INTO i_rulebase_id id FROM rulebase WHERE name=r_dev.global_rulebase_name AND mgm_id=r_dev.mgm_id;
+ -- check if rulebase_link already exists
+ IF i_rulebase_id IS NOT NULL THEN
+ SELECT INTO r_dev_null * FROM rulebase_link WHERE to_rulebase_id=i_rulebase_id AND gw_id=r_dev.dev_id;
+ IF NOT FOUND THEN
+ INSERT INTO rulebase_link (gw_id, from_rule_id, to_rulebase_id, created, link_type, is_initial)
+ VALUES (r_dev.dev_id, NULL, i_rulebase_id, (SELECT * FROM get_last_import_id_for_mgmt(r_dev.mgm_id)), 2, TRUE);
+ END IF;
+ END IF;
+ END IF;
+ END LOOP;
+ END;
+$function$;
+
+CREATE OR REPLACE FUNCTION addRulebaseEntriesAndItsRuleRefs() RETURNS VOID
+ LANGUAGE plpgsql
+ VOLATILE
+AS $function$
+ DECLARE
+ r_dev RECORD;
+ r_rule RECORD;
+ r_dev_null RECORD;
+ i_new_rulebase_id INTEGER;
+ BEGIN
+
+ FOR r_dev IN
+ SELECT * FROM device
+ LOOP
+ -- if rulebase does not exist yet: insert it
+ SELECT INTO r_dev_null * FROM rulebase WHERE name=r_dev.local_rulebase_name;
+ IF NOT FOUND AND r_dev.local_rulebase_name IS NOT NULL THEN
+ -- first create rulebase entries
+ INSERT INTO rulebase (name, uid, mgm_id, is_global, created)
+ VALUES (r_dev.local_rulebase_name, r_dev.local_rulebase_name, r_dev.mgm_id, FALSE, 1)
+ RETURNING id INTO i_new_rulebase_id;
+ -- now update references in all rules to the newly created rulebase
+ UPDATE rule SET rulebase_id=i_new_rulebase_id WHERE dev_id=r_dev.dev_id;
+ END IF;
+
+ SELECT INTO r_dev_null * FROM rulebase WHERE name=r_dev.global_rulebase_name;
+ IF NOT FOUND AND r_dev.global_rulebase_name IS NOT NULL THEN
+ INSERT INTO rulebase (name, uid, mgm_id, is_global, created)
+ VALUES (r_dev.global_rulebase_name, r_dev.global_rulebase_name, r_dev.mgm_id, TRUE, 1)
+ RETURNING id INTO i_new_rulebase_id;
+ -- now update references in all rules to the newly created rulebase
+ UPDATE rule SET rulebase_id=i_new_rulebase_id WHERE dev_id=r_dev.dev_id;
+ -- add entries in rule_enforced_on_gateway
+ END IF;
+ END LOOP;
+
+ -- now check for remaining rules without rulebase_id
+ -- TODO: decide how to deal with this - ONLY DUMMY SOLUTION FOR NOW
+ FOR r_rule IN
+ SELECT * FROM rule WHERE rulebase_id IS NULL
+ -- how do we deal with this? we simply pick the smallest rulebase id for now
+ LOOP
+ SELECT INTO i_new_rulebase_id id FROM rulebase ORDER BY id LIMIT 1;
+ UPDATE rule SET rulebase_id=i_new_rulebase_id WHERE rule_id=r_rule.rule_id;
+ END LOOP;
+
+ -- now we can add the "not null" constraint for rule.rulebase_id
+ IF EXISTS (
+ SELECT 1
+ FROM information_schema.columns
+ WHERE table_name = 'rule'
+ AND column_name = 'rulebase_id'
+ AND is_nullable = 'YES'
+ ) THEN
+ ALTER TABLE rule
+ ALTER COLUMN rulebase_id SET NOT NULL;
+ END IF;
+ END;
+$function$;
+
+-- in this migration, in scenarios where a rulebase is used on more than one gateway,
+-- only the rules of the first gw get a rulebase_id, the others (copies) will be deleted
+CREATE OR REPLACE FUNCTION migrateToRulebases() RETURNS VOID
+ LANGUAGE plpgsql
+ VOLATILE
+AS $function$
+ BEGIN
+
+ PERFORM addRulebaseEntriesAndItsRuleRefs();
+ PERFORM addRulebaseLinkEntries();
+ -- danger zone: delete all rules that have no rulebase_id
+ -- the deletion might take some time
+ PERFORM deleteDuplicateRulebases();
+ -- PERFORM addMetadataRulebaseEntries(); -- this does not work as we just removed the dev_id column from rule_metadata
+ -- add entries in rule_enforced_on_gateway
+ PERFORM addRuleEnforcedOnGatewayEntries();
+ END;
+$function$;
+
+SELECT * FROM migrateToRulebases();
+
+-- now we can set the new constraint on rule_uid:
+Alter Table "rule" DROP Constraint IF EXISTS "rule_altkey";
+Alter Table "rule" DROP Constraint IF EXISTS "rule_unique_mgm_id_rule_uid_rule_create_xlate_rule";
+Alter Table "rule" ADD Constraint "rule_unique_mgm_id_rule_uid_rule_create_xlate_rule" UNIQUE ("mgm_id", "rule_uid","rule_create","xlate_rule");
+
+
+-- rewrite get_rulebase_for_owner to work with rulebase instead of device
+CREATE OR REPLACE FUNCTION public.get_rulebase_for_owner(rulebase_row rulebase, ownerid integer)
+RETURNS SETOF rule
+LANGUAGE plpgsql
+STABLE
+AS $function$
+BEGIN
+ RETURN QUERY
+ SELECT *
+ FROM (
+ WITH src_rules AS (
+ SELECT r.*, rf_o.obj_ip, rf_o.obj_ip_end, rf.negated
+ FROM rule r
+ LEFT JOIN rule_from rf ON r.rule_id = rf.rule_id
+ LEFT JOIN objgrp_flat rf_of ON rf.obj_id = rf_of.objgrp_flat_id
+ LEFT JOIN object rf_o ON rf_of.objgrp_flat_member_id = rf_o.obj_id
+ WHERE r.rulebase_id = rulebase_row.id AND owner_id = ownerid AND rule_head_text IS NULL
+ ),
+ dst_rules AS (
+ SELECT r.*, rt_o.obj_ip, rt_o.obj_ip_end, rt.negated
+ FROM rule r
+ LEFT JOIN rule_to rt ON r.rule_id = rt.rule_id
+ LEFT JOIN objgrp_flat rt_of ON rt.obj_id = rt_of.objgrp_flat_id
+ LEFT JOIN object rt_o ON rt_of.objgrp_flat_member_id = rt_o.obj_id
+ WHERE r.rulebase_id = rulebase_row.id AND owner_id = ownerid AND rule_head_text IS NULL
+ )
+ SELECT s.*
+ FROM src_rules s
+ LEFT JOIN owner_network ON ip_ranges_overlap(s.obj_ip, s.obj_ip_end, ip, ip_end, s.negated != s.rule_src_neg)
+ UNION
+ SELECT d.*
+ FROM dst_rules d
+ LEFT JOIN owner_network ON ip_ranges_overlap(d.obj_ip, d.obj_ip_end, ip, ip_end, d.negated != d.rule_dst_neg)
+ ) AS combined
+ ORDER BY rule_name ASC;
+END;
+$function$;
+
+
+-- drop only after migration
+
+ALTER TABLE rule DROP CONSTRAINT IF EXISTS rule_dev_id_fkey;
+
+ALTER TABLE "rule_metadata" DROP CONSTRAINT IF EXISTS "rule_metadata_rulebase_id_f_key" CASCADE;
+-- Alter table "rule_metadata" add constraint "rule_metadata_rulebase_id_f_key"
+-- foreign key ("rulebase_id") references "rulebase" ("id") on update restrict on delete cascade;
+-- ALTER TABLE "rule_metadata" DROP CONSTRAINT IF EXISTS "rule_metadata_alt_key" CASCADE;
+-- Alter Table "rule_metadata" add Constraint "rule_metadata_alt_key" UNIQUE ("rule_uid","dev_id","rulebase_id");
+
+-- reverse last_seen / removed logic for objects
+ALTER TABLE "object" ADD COLUMN IF NOT EXISTS "removed" BIGINT;
+ALTER TABLE "objgrp" ADD COLUMN IF NOT EXISTS "removed" BIGINT;
+ALTER TABLE "objgrp_flat" ADD COLUMN IF NOT EXISTS "removed" BIGINT;
+ALTER TABLE "service" ADD COLUMN IF NOT EXISTS "removed" BIGINT;
+ALTER TABLE "svcgrp" ADD COLUMN IF NOT EXISTS "removed" BIGINT;
+ALTER TABLE "svcgrp_flat" ADD COLUMN IF NOT EXISTS "removed" BIGINT;
+ALTER TABLE "zone" ADD COLUMN IF NOT EXISTS "removed" BIGINT;
+ALTER TABLE "usr" ADD COLUMN IF NOT EXISTS "removed" BIGINT;
+ALTER TABLE "usergrp" ADD COLUMN IF NOT EXISTS "removed" BIGINT;
+ALTER TABLE "usergrp_flat" ADD COLUMN IF NOT EXISTS "removed" BIGINT;
+ALTER TABLE "rule" ADD COLUMN IF NOT EXISTS "removed" BIGINT;
+ALTER TABLE "rule_from" ADD COLUMN IF NOT EXISTS "removed" BIGINT;
+ALTER TABLE "rule_to" ADD COLUMN IF NOT EXISTS "removed" BIGINT;
+ALTER TABLE "rule_service" ADD COLUMN IF NOT EXISTS "removed" BIGINT;
+ALTER TABLE "rule_enforced_on_gateway" ADD COLUMN IF NOT EXISTS "removed" BIGINT;
+ALTER TABLE "rulebase" ADD COLUMN IF NOT EXISTS "removed" BIGINT;
+ALTER TABLE "rulebase_link" ADD COLUMN IF NOT EXISTS "removed" BIGINT;
+
+-- add obj type access-role for cp import
+INSERT INTO stm_obj_typ (obj_typ_id,obj_typ_name) VALUES (21,'access-role') ON CONFLICT DO NOTHING;
+
+-- remove dev_id fk and set nullable if column exists
+ALTER TABLE changelog_rule DROP CONSTRAINT IF EXISTS changelog_rule_dev_id_fkey;
+
+DO $$
+BEGIN
+ IF EXISTS (
+ SELECT 1
+ FROM information_schema.columns
+ WHERE table_name = 'changelog_rule'
+ AND column_name = 'dev_id'
+ ) THEN
+ ALTER TABLE changelog_rule ALTER COLUMN dev_id DROP NOT NULL;
+ END IF;
+END
+$$;
+
+-- add new compliance tables
+
+CREATE TABLE IF NOT EXISTS compliance.policy
+(
+ id SERIAL PRIMARY KEY,
+ name TEXT,
+ created_date timestamp default now(),
+ disabled bool
+);
+
+CREATE TABLE IF NOT EXISTS compliance.policy_criterion
+(
+ policy_id INT NOT NULL,
+ criterion_id INT NOT NULL,
+ removed timestamp with time zone,
+ created timestamp with time zone default now()
+);
+
+CREATE TABLE IF NOT EXISTS compliance.criterion
+(
+ id SERIAL PRIMARY KEY,
+ name TEXT,
+ comment TEXT,
+ criterion_type TEXT,
+ content TEXT,
+ removed timestamp with time zone,
+ created timestamp with time zone default now(),
+ import_source TEXT
+);
+
+CREATE TABLE IF NOT EXISTS compliance.violation
+(
+ id BIGSERIAL PRIMARY KEY,
+ rule_id bigint NOT NULL,
+ found_date timestamp with time zone default now(),
+ removed_date timestamp with time zone,
+ details TEXT,
+ risk_score real,
+ policy_id INT NOT NULL,
+ criterion_id INT NOT NULL
+);
+
+-- add columns in existing compliance tables
+
+ALTER TABLE compliance.network_zone ADD COLUMN IF NOT EXISTS "created" TIMESTAMP WITH TIME ZONE DEFAULT NOW();
+ALTER TABLE compliance.network_zone ADD COLUMN IF NOT EXISTS "removed" TIMESTAMP WITH TIME ZONE;
+ALTER TABLE compliance.network_zone ADD COLUMN IF NOT EXISTS "criterion_id" INT;
+ALTER TABLE compliance.network_zone ADD COLUMN IF NOT EXISTS "id_string" TEXT;
+ALTER TABLE compliance.network_zone_communication ADD COLUMN IF NOT EXISTS "created" TIMESTAMP WITH TIME ZONE DEFAULT NOW();
+ALTER TABLE compliance.network_zone_communication ADD COLUMN IF NOT EXISTS "removed" TIMESTAMP WITH TIME ZONE;
+ALTER TABLE compliance.network_zone_communication ADD COLUMN IF NOT EXISTS "criterion_id" INT;
+ALTER TABLE compliance.ip_range ADD COLUMN IF NOT EXISTS "created" TIMESTAMP WITH TIME ZONE DEFAULT NOW();
+ALTER TABLE compliance.ip_range ADD COLUMN IF NOT EXISTS "removed" TIMESTAMP WITH TIME ZONE;
+ALTER TABLE compliance.ip_range ADD COLUMN IF NOT EXISTS "criterion_id" INT;
+ALTER TABLE compliance.ip_range ADD COLUMN IF NOT EXISTS "name" TEXT;
+
+-- tables altered inside this version
+
+ALTER TABLE compliance.criterion ADD COLUMN IF NOT EXISTS "created" TIMESTAMP WITH TIME ZONE DEFAULT NOW();
+ALTER TABLE compliance.criterion ADD COLUMN IF NOT EXISTS "removed" TIMESTAMP WITH TIME ZONE;
+ALTER TABLE compliance.criterion ADD COLUMN IF NOT EXISTS "import_source" TEXT;
+ALTER TABLE compliance.criterion ADD COLUMN IF NOT EXISTS "comment" TEXT;
+ALTER TABLE compliance.network_zone ALTER COLUMN "removed" DROP DEFAULT;
+ALTER TABLE compliance.network_zone ADD COLUMN IF NOT EXISTS "is_auto_calculated_internet_zone" BOOLEAN DEFAULT FALSE;
+ALTER TABLE compliance.network_zone ADD COLUMN IF NOT EXISTS "is_auto_calculated_undefined_internal_zone" BOOLEAN DEFAULT FALSE;
+ALTER TABLE compliance.network_zone_communication ALTER COLUMN "removed" DROP DEFAULT;
+ALTER TABLE compliance.ip_range ALTER COLUMN "removed" DROP DEFAULT;
+ALTER TABLE compliance.policy_criterion ALTER COLUMN "removed" DROP DEFAULT;
+ALTER TABLE compliance.violation ALTER COLUMN "removed_date" DROP DEFAULT;
+ALTER TABLE compliance.violation ALTER COLUMN "found_date" TYPE TIMESTAMP WITH TIME ZONE; -- takes local timezone. can be set wxplicitly by "USING found_date AT TIME ZONE 'UTC'";
+
+-- alter ip_range's PK
+
+ALTER TABLE compliance.ip_range DROP CONSTRAINT IF EXISTS ip_range_pkey;
+ALTER TABLE compliance.ip_range
+ADD CONSTRAINT ip_range_pkey
+PRIMARY KEY (network_zone_id, ip_range_start, ip_range_end, created);
+
+-- add FKs
+
+ALTER TABLE compliance.network_zone
+DROP CONSTRAINT IF EXISTS compliance_criterion_network_zone_foreign_key;
+ALTER TABLE compliance.network_zone
+ADD CONSTRAINT compliance_criterion_network_zone_foreign_key
+FOREIGN KEY (criterion_id) REFERENCES compliance.criterion(id)
+ON UPDATE RESTRICT ON DELETE CASCADE;
+
+ALTER TABLE compliance.ip_range
+DROP CONSTRAINT IF EXISTS compliance_criterion_ip_range_foreign_key;
+ALTER TABLE compliance.ip_range
+ADD CONSTRAINT compliance_criterion_ip_range_foreign_key
+FOREIGN KEY (criterion_id) REFERENCES compliance.criterion(id)
+ON UPDATE RESTRICT ON DELETE CASCADE;
+
+ALTER TABLE compliance.network_zone_communication
+DROP CONSTRAINT IF EXISTS compliance_criterion_network_zone_communication_foreign_key;
+ALTER TABLE compliance.network_zone_communication
+ADD CONSTRAINT compliance_criterion_network_zone_communication_foreign_key
+FOREIGN KEY (criterion_id) REFERENCES compliance.criterion(id)
+ON UPDATE RESTRICT ON DELETE CASCADE;
+
+ALTER TABLE compliance.policy_criterion
+DROP CONSTRAINT IF EXISTS compliance_policy_policy_criterion_foreign_key;
+ALTER TABLE compliance.policy_criterion
+ADD CONSTRAINT compliance_policy_policy_criterion_foreign_key
+FOREIGN KEY (policy_id) REFERENCES compliance.policy(id)
+ON UPDATE RESTRICT ON DELETE CASCADE;
+
+ALTER TABLE compliance.policy_criterion
+DROP CONSTRAINT IF EXISTS compliance_criterion_policy_criterion_foreign_key;
+ALTER TABLE compliance.policy_criterion
+ADD CONSTRAINT compliance_criterion_policy_criterion_foreign_key
+FOREIGN KEY (criterion_id) REFERENCES compliance.criterion(id)
+ON UPDATE RESTRICT ON DELETE CASCADE;
+
+ALTER TABLE compliance.violation
+DROP CONSTRAINT IF EXISTS compliance_policy_violation_foreign_key;
+ALTER TABLE compliance.violation
+ADD CONSTRAINT compliance_policy_violation_foreign_key
+FOREIGN KEY (policy_id) REFERENCES compliance.policy(id)
+ON UPDATE RESTRICT ON DELETE CASCADE;
+
+ALTER TABLE compliance.violation
+DROP CONSTRAINT IF EXISTS compliance_criterion_violation_foreign_key;
+ALTER TABLE compliance.violation
+ADD CONSTRAINT compliance_criterion_violation_foreign_key
+FOREIGN KEY (criterion_id) REFERENCES compliance.criterion(id)
+ON UPDATE RESTRICT ON DELETE CASCADE;
+
+ALTER TABLE compliance.violation
+DROP CONSTRAINT IF EXISTS compliance_rule_violation_foreign_key;
+ALTER TABLE compliance.violation
+ADD CONSTRAINT compliance_rule_violation_foreign_key
+FOREIGN KEY (rule_id) REFERENCES public.rule(rule_id)
+ON UPDATE RESTRICT ON DELETE CASCADE;
+
+-- add report type Compliance
+
+UPDATE config SET config_value = '[1,2,3,4,5,6,7,8,9,10,21,22,31,32]' WHERE config_key = 'availableReportTypes';
+
+--- prevent overlapping active ip address ranges in the same zone
+ALTER TABLE compliance.ip_range DROP CONSTRAINT IF EXISTS exclude_overlapping_ip_ranges;
+ALTER TABLE compliance.ip_range ADD CONSTRAINT exclude_overlapping_ip_ranges
+EXCLUDE USING gist (
+ network_zone_id WITH =,
+ numrange(ip_range_start - '0.0.0.0'::inet, ip_range_end - '0.0.0.0'::inet, '[]') WITH &&
+)
+WHERE (removed IS NULL);
+
+-- add config parameter debugConfig if not exists
+
+INSERT INTO config (config_key, config_value, config_user)
+VALUES ('debugConfig', '{"debugLevel":8, "extendedLogComplianceCheck":true, "extendedLogReportGeneration":true, "extendedLogScheduler":true}', 0)
+ON CONFLICT (config_key, config_user) DO NOTHING;
+
+-- add config parameter complianceCheckPolicy if not exists
+
+INSERT INTO config (config_key, config_value, config_user)
+VALUES ('complianceCheckPolicy', '0', 0)
+ON CONFLICT (config_key, config_user) DO NOTHING;
+
+-- add management and rule uids to table violation
+
+ALTER TABLE compliance.violation ADD COLUMN IF NOT EXISTS rule_uid TEXT;
+ALTER TABLE compliance.violation ADD COLUMN IF NOT EXISTS mgmt_uid TEXT;
+
+-- add assessability issue
+
+-- create table if not exists compliance.assessability_issue
+-- (
+-- violation_id BIGINT NOT NULL,
+-- type_id INT NOT NULL,
+-- PRIMARY KEY(violation_id, type_id)
+-- );
+
+-- create table if not exists compliance.assessability_issue_type
+-- (
+-- type_id INT PRIMARY KEY,
+-- type_name VARCHAR(50) NOT NULL
+-- );
+
+
+-- ALTER TABLE compliance.assessability_issue
+-- DROP CONSTRAINT IF EXISTS compliance_assessability_issue_type_foreign_key;
+-- ALTER TABLE compliance.assessability_issue ADD CONSTRAINT compliance_assessability_issue_type_foreign_key FOREIGN KEY (type_id) REFERENCES compliance.assessability_issue_type(type_id) ON UPDATE RESTRICT ON DELETE CASCADE;
+-- ALTER TABLE compliance.assessability_issue
+-- DROP CONSTRAINT IF EXISTS compliance_assessability_issue_violation_foreign_key;
+-- ALTER TABLE compliance.assessability_issue ADD CONSTRAINT compliance_assessability_issue_violation_foreign_key FOREIGN KEY (violation_id) REFERENCES compliance.violation(id) ON UPDATE RESTRICT ON DELETE CASCADE;
+
+-- insert into compliance.assessability_issue_type (type_id, type_name) VALUES (1, 'empty group') ON CONFLICT DO NOTHING;
+-- insert into compliance.assessability_issue_type (type_id, type_name) VALUES (2, 'broadcast address') ON CONFLICT DO NOTHING;
+-- insert into compliance.assessability_issue_type (type_id, type_name) VALUES (3, 'DHCP IP undefined address') ON CONFLICT DO NOTHING;
+-- insert into compliance.assessability_issue_type (type_id, type_name) VALUES (4, 'dynamic internet address') ON CONFLICT DO NOTHING;
+
+-- add unique constraint for report_template_name
+
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'unique_report_template_name'
+ ) THEN
+ ALTER TABLE report_template
+ ADD CONSTRAINT unique_report_template_name UNIQUE (report_template_name);
+ END IF;
+END$$;
+
+-- add new report template for compliance: unresolved violations
+
+INSERT INTO "report_template" ("report_filter","report_template_name","report_template_comment","report_template_owner", "report_parameters")
+ VALUES ('action=accept',
+ 'Compliance: Unresolved violations','T0108', 0,
+ '{"report_type":31,"device_filter":{"management":[]},
+ "time_filter": {
+ "is_shortcut": true,
+ "shortcut": "now",
+ "report_time": "2022-01-01T00:00:00.0000000+01:00",
+ "timerange_type": "SHORTCUT",
+ "shortcut_range": "this year",
+ "offset": 0,
+ "interval": "DAYS",
+ "start_time": "2022-01-01T00:00:00.0000000+01:00",
+ "end_time": "2022-01-01T00:00:00.0000000+01:00",
+ "open_start": false,
+ "open_end": false},
+ "compliance_filter": {
+ "diff_reference_in_days": 0,
+ "show_non_impact_rules": true}}')
+ON CONFLICT (report_template_name) DO NOTHING;
+
+-- add new report template for compliance: diffs
+
+INSERT INTO "report_template" ("report_filter","report_template_name","report_template_comment","report_template_owner", "report_parameters")
+ VALUES ('action=accept',
+ 'Compliance: Diffs','T0109', 0,
+ '{"report_type":32,"device_filter":{"management":[]},
+ "time_filter": {
+ "is_shortcut": true,
+ "shortcut": "now",
+ "report_time": "2022-01-01T00:00:00.0000000+01:00",
+ "timerange_type": "SHORTCUT",
+ "shortcut_range": "this year",
+ "offset": 0,
+ "interval": "DAYS",
+ "start_time": "2022-01-01T00:00:00.0000000+01:00",
+ "end_time": "2022-01-01T00:00:00.0000000+01:00",
+ "open_start": false,
+ "open_end": false},
+ "compliance_filter": {
+ "diff_reference_in_days": 7,
+ "show_non_impact_rules": false}}')
+ON CONFLICT (report_template_name) DO NOTHING;
+
+-- add parameter to limit number of printed violations in compliance report to config
+
+INSERT INTO config (config_key, config_value, config_user)
+VALUES ('complianceCheckMaxPrintedViolations', '0', 0)
+ON CONFLICT (config_key, config_user) DO NOTHING;
+
+-- add parameter to persist report scheduler configs to config
+
+INSERT INTO config (config_key, config_value, config_user)
+VALUES ('reportSchedulerConfig', '', 0)
+ON CONFLICT (config_key, config_user) DO NOTHING;
+
+-- add parameter to choose order by column of network matrix between name and id
+
+INSERT INTO config (config_key, config_value, config_user)
+VALUES ('complianceCheckSortMatrixByID', 'false', 0)
+ON CONFLICT (config_key, config_user) DO NOTHING;
+
+-- internal zone parameters
+
+INSERT INTO config (config_key, config_value, config_user) VALUES ('internalZoneRange_10_0_0_0_8', 'true', 0) ON CONFLICT (config_key, config_user) DO NOTHING;
+INSERT INTO config (config_key, config_value, config_user) VALUES ('internalZoneRange_172_16_0_0_12', 'true', 0) ON CONFLICT (config_key, config_user) DO NOTHING;
+INSERT INTO config (config_key, config_value, config_user) VALUES ('internalZoneRange_192_168_0_0_16', 'true', 0) ON CONFLICT (config_key, config_user) DO NOTHING;
+INSERT INTO config (config_key, config_value, config_user) VALUES ('internalZoneRange_0_0_0_0_8', 'true', 0) ON CONFLICT (config_key, config_user) DO NOTHING;
+INSERT INTO config (config_key, config_value, config_user) VALUES ('internalZoneRange_127_0_0_0_8', 'true', 0) ON CONFLICT (config_key, config_user) DO NOTHING;
+INSERT INTO config (config_key, config_value, config_user) VALUES ('internalZoneRange_169_254_0_0_16', 'true', 0) ON CONFLICT (config_key, config_user) DO NOTHING;
+INSERT INTO config (config_key, config_value, config_user) VALUES ('internalZoneRange_224_0_0_0_4', 'true', 0) ON CONFLICT (config_key, config_user) DO NOTHING;
+INSERT INTO config (config_key, config_value, config_user) VALUES ('internalZoneRange_240_0_0_0_4', 'true', 0) ON CONFLICT (config_key, config_user) DO NOTHING;
+INSERT INTO config (config_key, config_value, config_user) VALUES ('internalZoneRange_255_255_255_255_32', 'true', 0) ON CONFLICT (config_key, config_user) DO NOTHING;
+INSERT INTO config (config_key, config_value, config_user) VALUES ('internalZoneRange_192_0_2_0_24', 'true', 0) ON CONFLICT (config_key, config_user) DO NOTHING;
+INSERT INTO config (config_key, config_value, config_user) VALUES ('internalZoneRange_198_51_100_0_24', 'true', 0) ON CONFLICT (config_key, config_user) DO NOTHING;
+INSERT INTO config (config_key, config_value, config_user) VALUES ('internalZoneRange_203_0_113_0_24', 'true', 0) ON CONFLICT (config_key, config_user) DO NOTHING;
+INSERT INTO config (config_key, config_value, config_user) VALUES ('internalZoneRange_100_64_0_0_10', 'true', 0) ON CONFLICT (config_key, config_user) DO NOTHING;
+INSERT INTO config (config_key, config_value, config_user) VALUES ('internalZoneRange_192_0_0_0_24', 'true', 0) ON CONFLICT (config_key, config_user) DO NOTHING;
+INSERT INTO config (config_key, config_value, config_user) VALUES ('internalZoneRange_192_88_99_0_24', 'true', 0) ON CONFLICT (config_key, config_user) DO NOTHING;
+INSERT INTO config (config_key, config_value, config_user) VALUES ('internalZoneRange_198_18_0_0_15', 'true', 0) ON CONFLICT (config_key, config_user) DO NOTHING;
+
+-- auto calculate special zone parameters
+
+INSERT INTO config (config_key, config_value, config_user)
+VALUES ('autoCalculateInternetZone', 'true', 0)
+ON CONFLICT (config_key, config_user) DO NOTHING;
+
+INSERT INTO config (config_key, config_value, config_user)
+VALUES ('autoCalculateUndefinedInternalZone', 'true', 0)
+ON CONFLICT (config_key, config_user) DO NOTHING;
+
+INSERT INTO config (config_key, config_value, config_user)
+VALUES ('autoCalculatedZonesAtTheEnd', 'true', 0)
+ON CONFLICT (config_key, config_user) DO NOTHING;
+
+INSERT INTO config (config_key, config_value, config_user)
+VALUES ('treatDynamicAndDomainObjectsAsInternet', 'true', 0)
+ON CONFLICT (config_key, config_user) DO NOTHING;
+
+INSERT INTO config (config_key, config_value, config_user)
+VALUES ('showShortColumnsInComplianceReports', 'true', 0)
+ON CONFLICT (config_key, config_user) DO NOTHING;
+
+INSERT INTO config (config_key, config_value, config_user)
+VALUES ('autoCalculatedZonesAtTheEnd', 'true', 0)
+ON CONFLICT (config_key, config_user) DO NOTHING;
+
+INSERT INTO config (config_key, config_value, config_user)
+VALUES ('treatDynamicAndDomainObjectsAsInternet', 'true', 0)
+ON CONFLICT (config_key, config_user) DO NOTHING;
+
+INSERT INTO config (config_key, config_value, config_user)
+VALUES ('showShortColumnsInComplianceReports', 'true', 0)
+ON CONFLICT (config_key, config_user) DO NOTHING;
+
+-- set deprecated field rule_num to 0 for all rules to avoid inconsistencies
+UPDATE rule SET rule_num = 0 WHERE rule_num <> 0;;
+
+-- add config value to make imported matrices editable
+
+INSERT INTO config (config_key, config_value, config_user)
+VALUES ('importedMatrixReadOnly', 'true', 0)
+ON CONFLICT (config_key, config_user) DO NOTHING;
+
+-- add config values to make parralelization in compliance check configurable
+
+INSERT INTO config (config_key, config_value, config_user)
+VALUES ('complianceCheckElementsPerFetch', '500', 0)
+ON CONFLICT (config_key, config_user) DO NOTHING;
+
+INSERT INTO config (config_key, config_value, config_user)
+VALUES ('complianceCheckAvailableProcessors', '4', 0)
+ON CONFLICT (config_key, config_user) DO NOTHING;
+
+-- adding labels (simple version without mapping tables and without foreign keys)
+
+-- CREATE TABLE label (
+-- id SERIAL PRIMARY KEY,
+-- name TEXT NOT NULL
+-- );
+
+-- ALTER TABLE "rule" ADD COLUMN IF NOT EXISTS "labels" INT[];
+-- ALTER TABLE "service" ADD COLUMN IF NOT EXISTS "labels" INT[];
+-- ALTER TABLE "object" ADD COLUMN IF NOT EXISTS "labels" INT[];
+-- ALTER TABLE "usr" ADD COLUMN IF NOT EXISTS "labels" INT[];
+
+
+-- ALTER TABLE "object" DROP COLUMN IF EXISTS "deleted" ;
+-- ALTER TABLE "objgrp" DROP COLUMN IF EXISTS "deleted" ;
+-- ALTER TABLE "objgrp_flat" DROP COLUMN IF EXISTS "deleted" ;
+-- ALTER TABLE "service" DROP COLUMN IF EXISTS "deleted" ;
+-- ALTER TABLE "svcgrp" DROP COLUMN IF EXISTS "deleted" ;
+-- ALTER TABLE "svcgrp_flat" DROP COLUMN IF EXISTS "deleted" ;
+-- ALTER TABLE "zone" DROP COLUMN IF EXISTS "deleted" ;
+-- ALTER TABLE "usr" DROP COLUMN IF EXISTS "deleted" ;
+-- ALTER TABLE "usergrp" DROP COLUMN IF EXISTS "deleted" ;
+-- ALTER TABLE "usergrp_flat" DROP COLUMN IF EXISTS "deleted" ;
+-- ALTER TABLE "rule" DROP COLUMN IF EXISTS "deleted" ;
+-- ALTER TABLE "rule_from" DROP COLUMN IF EXISTS "deleted" ;
+-- ALTER TABLE "rule_to" DROP COLUMN IF EXISTS "deleted" ;
+-- ALTER TABLE "rule_service" DROP COLUMN IF EXISTS "deleted" ;
+-- ALTER TABLE "rule_enforced_on_gateway" DROP COLUMN IF EXISTS "deleted";
+-- ALTER TABLE "rulebase" DROP COLUMN IF EXISTS "deleted";
+-- ALTER TABLE "rulebase_link" DROP COLUMN IF EXISTS "deleted";
+-- TODO: fill all rulebase_id s and then add not null constraint
+
+
+-- TODOs
+
+-- Rename table rulebase_on_gateways to gateway_rulebase to get correct plural gateway_rulebases in hasura
+
+-- REPORTING:
+-- - RulesReportRazor line 23: deal with multiple ordered rulebases (later)
+-- style="font-size:small" TableClass="table table-bordered table-sm th-bg-secondary table-responsive overflow-auto sticky-header" TableItem="Rule" Items="device.Rules" ShowSearchBar="false"
+-- - ObjectGroup.Razor line 431:
+-- Rule? ruleUpdated = managementsUpdate.ManagementData.SelectMany(m => m.Devices).SelectMany(d => d.Rulebases[0].Rulebase.Rules ?? new Rule[0]).FirstOrDefault();
+-- - Statistics Report #rules per device are 0 (device report is null)
+-- - recertification: without rule.dev_id we have lost all rule information in report!!!
+-- - certification information should be aimed at a rule on a gateway
+-- - this might lead to a rulebase which is enforced on multiple gateways to be changed only in
+-- the rule_enforced_on_gateway field
+-- - how do we get the link between rule_metadata (cert) and the actual rule details?
+-- --> can we add a fk from rule_metadatum to rulebase to fix this?
+-- query rulesReport($limit: Int, $offset: Int, $mgmId: [Int!], $relevantImportId: bigint, $cut: timestamp, $tolerance: timestamp) {
+-- management(where: {hide_in_gui: {_eq: false}, mgm_id: {_in: $mgmId}, stm_dev_typ: {dev_typ_is_multi_mgmt: {_eq: false}, is_pure_routing_device: {_eq: false}}}, order_by: {mgm_name: asc}) {
+-- id: mgm_id
+-- name: mgm_name
+-- devices(where: {hide_in_gui: {_eq: false}}) {
+-- id: dev_id
+-- rule_metadata {
+-- rule_last_hit
+-- rule_uid
+-- dev_id
+-- # here we do not have any rule details
+-- }
+-- name: dev_name
+-- rulebase_on_gateways(order_by: {order_no: asc}) {
+-- rulebase_id
+-- order_no
+-- rulebase {
+-- id
+-- name
+-- rules {
+-- mgm_id: mgm_id
+-- rule_metadatum {
+-- # here, the rule_metadata is always empty!
+-- rule_last_hit
+-- }
+-- ...ruleOverview
+-- }
+-- }
+-- }
+-- }
+-- }
+-- }
+-- - need to enhance certifications (add dev_id?)
+
+-- - make sure that xlate rules get unique UIDs
+-- - with each major version released:
+-- add fwo version to demo config files on fwodemo to ensure all versions can be served
+
+-- - add install on column to the following reports:
+-- - recert
+-- - change (all 3)
+-- - statistics (optional: only count rules per gw which are active on gw)
+
+-- - adjust report tests (add column)
+-- import install on information (need to find out, where it is encoded) from
+-- - fortimanger - simply add name of current gw?
+-- - fortios - simply add name of current gw?
+-- - others? - simply add name of current gw?
+
+-- importer cp get changes:
+-- {'uid': 'cf8c7582-fd95-464c-81a0-7297df3c5ad9', 'type': 'access-rule', 'domain': {'uid': '41e821a0-3720-11e3-aa6e-0800200c9fde', 'name': 'SMC User', 'domain-type': 'domain'}, 'position': 7, 'track': {'type': {...}, 'per-session': False, 'per-connection': False, 'accounting': False, 'enable-firewall-session': False, 'alert': 'none'}, 'layer': '0f45100c-e4ea-4dc1-bf22-74d9d98a4811', 'source': [{...}], 'source-negate': False, 'destination': [{...}], 'destination-negate': False, 'service': [{...}], 'service-negate': False, 'service-resource': '', 'vpn': [{...}], 'action': {'uid': '6c488338-8eec-4103-ad21-cd461ac2c472', 'name': 'Accept', 'type': 'RulebaseAction', 'domain': {...}, 'color': 'none', 'meta-info': {...}, 'tags': [...], 'icon': 'Actions/actionsAccept', 'comments': 'Accept', 'display-name': 'Accept', 'customFields': None}, 'action-settings': {'enable-identity-captive-portal': False}, 'content': [{...}], 'content-negate': False, 'content-direction': 'any', 'time': [{...}], 'custom-fields': {'field-1': '', 'field-2': '', 'field-3': ''}, 'meta-info': {'lock': 'unlocked', 'validation-state': 'ok', 'last-modify-time': {...}, 'last-modifier': 'tim-admin', 'creation-time': {...}, 'creator': 'tim-admin'}, 'comments': '', 'enabled': True, 'install-on': [{...}], 'available-actions': {'clone': 'not_supported'}, 'tags': []}
+
+-- - change (cp) importer to read rulebases and mappings from rulebase to device
+-- - each rule is only stored once
+-- - each rulebase is only stored once
+-- --- global changes ----
+-- - allow conversion from new to old format (would lose information when working with rulebases)
+-- - allow conversion from old to new format (only for simple setups with 1:1 gw to rulebase matches
+
+-- Cleanups (after cp importer works with all config variants):
+-- - re-add users (cp),check ida rules - do we have networks here?
+-- #parse_users_from_rulebases(full_config, full_config['rulebases'], full_config['users'], config2import, current_import_id)
+-- --> replace by api call?
+-- - re-add config splits
+-- - add the following functions to all modules:
+-- - getNativeConfig
+-- - normalizeConfig
+-- - getNormalizedConfig (a combination of the two above)
+-- - re-add global / domain policies
+-- - update all importers:
+-- - fortimanager
+-- - azure
+-- - cisco firepower
+-- - Palo
+-- - NSX
+-- - Azure
+-- - legacy?
+-- - netscreen?!
+-- - barracuda
+
+-- can we get everything working with old config format? no!
+
+-- optimization: add mgm_id to all tables like objgrp, ... ?
+
+-- disabled in UI:
+-- recertification.razor
+-- in report.razor:
+-- - RSB
+-- - TicketCreate Komponente
+
+-- 2024-10-09 planning
+-- - calculate rule_num_numeric
+-- - config mapping gateway to rulebase(s)
+-- - do not store/use any rulebase names in device table
+-- - instead get current config with every import
+-- - id for gateway needs to be fixated:
+
+-- - check point:
+-- - read interface information from show-gateways-and-servers details-level=full
+-- - where to get routing infos?
+-- - optional: also get publish time per policy (push):
+-- "publish-time" : {
+-- "posix" : 1727978692716,
+-- "iso-8601" : "2024-10-03T20:04+0200"
+-- },
+-- filter out vswitches?
+
+-- - goal:
+-- - in device table:
+-- - for CP only save policy-name per gateway (gotten from show-gateways-and-servers
+-- - in config file storage:
+-- - store all policies with the management rathen than with the gateway?
+-- - per gateway only store the ordered mapping gw --> policies
+-- - also allow for mapping a gateway to a policy from the manager's super-manager
+
+-- - TODO: set is_super_manager flag = true for MDS
+
+-- {
+-- "ConfigFormat": "NORMALIZED",
+-- "ManagerSet": [
+-- {
+-- "ManagerUid": "6ae3760206b9bfbd2282b5964f6ea07869374f427533c72faa7418c28f7a77f2",
+-- "ManagerName": "schting2",
+-- "IsGlobal": false,
+-- "DependantManagerUids": [],
+-- "Configs": [
+-- {
+-- "ConfigFormat": "NORMALIZED_LEGACY",
+-- "action": "INSERT",
+-- "rules": [
+-- {
+-- "Uid": "FirstLayer shared with inline layer",
+-- "Name": "FirstLayer shared with inline layer",
+-- "Rules": {
+-- "828b0f42-4b18-4352-8bdf-c9c864d692eb": {
+-- }
+-- ],
+-- "gateways": [
+-- Uid: str
+-- Name: str
+-- Routing: List[dict] = []
+-- Interfaces: List[dict] = []
+-- # GlobalPolicyUid: Optional[str] = None
+-- "EnforcedPolicyUids": [
+-- ":",
+-- "FirstLayer shared with inline layer",
+-- "second-layer",
+-- ":",
+-- ]
+-- EnforcedNatPolicyUids: List[str] = []
+-- ]
+-- }
+-- ]
+-- }
+-- ]
+-- }
+
+
+-- - config mapping global start rb, local rb, global end rb
+-- - import inline layers
+-- - get reports working
+-- - valentin: open issues for k01 UI problems
+-- - decide how to implement ordered layer (all must match) vs. e.g. global policies (first match)
+-- - allow for also importing native configs from file
+
+
+-- TODOs after full importer migration
+-- -- ALTER table "import_config" DROP COLUMN IF EXISTS "chunk_number";
+-- -- ALTER TABLE "rule" DROP COLUMN IF EXISTS "rule_installon"; -- here we would need to rebuild views
+-- -- ALTER TABLE "rule" DROP COLUMN IF EXISTS "rule_ruleid"; -- here we would need to rebuild views
+-- -- ALTER TABLE "rule" DROP COLUMN IF EXISTS "dev_id"; -- final step when the new structure works
+-- -- ALTER TABLE "import_rule" DROP COLUMN IF EXISTS "rulebase_name";
+
+-- ALTER TABLE "object" DROP COLUMN IF EXISTS "obj_last_seen";
+-- ALTER TABLE "objgrp" DROP COLUMN IF EXISTS "objgrp_last_seen";
+-- ALTER TABLE "objgrp_flat" DROP COLUMN IF EXISTS "objgrp_flat_last_seen";
+-- ALTER TABLE "service" DROP COLUMN IF EXISTS "svc_last_seen";
+-- ALTER TABLE "svcgrp" DROP COLUMN IF EXISTS "svcgrp_last_seen";
+-- ALTER TABLE "svcgrp_flat" DROP COLUMN IF EXISTS "svcgrp_flat_last_seen";
+-- ALTER TABLE "zone" DROP COLUMN IF EXISTS "zone_last_seen";
+-- ALTER TABLE "usr" DROP COLUMN IF EXISTS "user_last_seen";
+-- ALTER TABLE "rule" DROP COLUMN IF EXISTS "rule_last_seen";
+-- 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_from_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_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_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_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_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_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
+DO $$
+DECLARE
+ inserted_source INT := 0;
+ inserted_destination INT := 0;
+ 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
+ -- 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 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;
+
+ 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 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,
+ --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;
+END
+$$;
+
+
+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 (28,'Cisco Asa','9','Cisco','',false,true,false)
+ ON CONFLICT (dev_typ_id) DO NOTHING;
+
+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;
+
+
+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;
+
+
+
+-- rule_metadata add mgm_id + fk, drop constraint
+ALTER TABLE rule_metadata ADD COLUMN IF NOT EXISTS mgm_id Integer;
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'rule_metadata_mgm_id_management_id_fk'
+ ) THEN
+ ALTER TABLE rule_metadata
+ ADD CONSTRAINT rule_metadata_mgm_id_management_id_fk
+ FOREIGN KEY (mgm_id) REFERENCES management(mgm_id)
+ ON UPDATE RESTRICT ON DELETE CASCADE;
+ END IF;
+END$$;
+
+
+
+DO $$
+DECLARE
+ rec RECORD;
+ v_do_not_import_true_count INT;
+ v_do_not_import_false_count INT;
+ missing_uids TEXT;
+ too_many_mgm_ids_on_uid_and_no_resolve TEXT;
+ all_errors_with_no_resolve TEXT := '';
+
+BEGIN
+--Check rule_metadata has entries in rule
+ SELECT string_agg(rm.rule_uid::text, ', ')
+ INTO missing_uids
+ FROM rule_metadata rm
+ LEFT JOIN rule r ON rm.rule_uid = r.rule_uid
+ WHERE r.rule_uid IS NULL;
+
+ IF missing_uids IS NOT NULL THEN
+ RAISE NOTICE 'Missing rule(s): %', missing_uids;
+ DELETE FROM rule_metadata
+ WHERE rule_uid IN (
+ SELECT rm.rule_uid
+ FROM rule_metadata rm
+ LEFT JOIN rule r ON rm.rule_uid = r.rule_uid
+ WHERE r.rule_uid IS NULL
+ );
+ END IF;
+
+ -- Constraints droppen
+ ALTER TABLE rule DROP CONSTRAINT IF EXISTS rule_metadatum;
+ ALTER TABLE rule DROP CONSTRAINT IF EXISTS rule_rule_metadata_rule_uid_f_key;
+ ALTER TABLE rule_metadata DROP CONSTRAINT IF EXISTS rule_metadata_rule_uid_unique;
+
+-- Start loop for rule_uid und mgm_id import/transfer
+ FOR rec IN
+ SELECT
+ rm.rule_uid,
+ COUNT(DISTINCT r.mgm_id) AS mgm_count
+ FROM rule_metadata rm
+ JOIN rule r ON rm.rule_uid = r.rule_uid
+ GROUP BY rm.rule_uid
+ HAVING COUNT(DISTINCT r.mgm_id) >= 1
+ LOOP
+ -- Case 1: exactly one mgm_id gefunden
+ IF rec.mgm_count = 1 THEN
+ --
+ UPDATE rule_metadata rm
+ SET mgm_id = r.mgm_id
+ FROM rule r
+ WHERE rm.rule_uid = r.rule_uid
+ AND rm.mgm_id IS NULL
+ AND rm.rule_uid = rec.rule_uid;
+
+ -- Case 2: found more then two mgm_id found
+ ELSIF rec.mgm_count >= 2 THEN
+ -- Count flag "do_not_import" for rule_uid
+ SELECT
+ COUNT(*) FILTER (WHERE m.do_not_import IS TRUE),
+ COUNT(*) FILTER (WHERE m.do_not_import IS FALSE)
+ INTO v_do_not_import_true_count, v_do_not_import_false_count
+ FROM rule r
+ JOIN management m ON r.mgm_id = m.mgm_id
+ WHERE r.rule_uid = rec.rule_uid;
+
+ -- check if there is just 1 "do_not_import" = false
+ IF v_do_not_import_false_count = 1 THEN
+ UPDATE rule_metadata rm
+ SET mgm_id = r.mgm_id
+ FROM rule r
+ JOIN management m ON r.mgm_id = m.mgm_id
+ WHERE rm.rule_uid = r.rule_uid
+ AND m.do_not_import IS FALSE
+ AND rm.rule_uid = rec.rule_uid
+ AND rm.mgm_id IS NULL;
+
+ -- Warning: Not used mgm_ids where do_not_import=true
+ RAISE NOTICE 'rule_uid % has % additional mgm_id(s) marked do_not_import=true: %',
+ rec.rule_uid, v_do_not_import_true_count,
+ (SELECT string_agg(format('mgm_id=%s', r.mgm_id), ', ')
+ FROM rule r
+ JOIN management m ON r.mgm_id = m.mgm_id
+ WHERE r.rule_uid = rec.rule_uid
+ AND m.do_not_import IS TRUE);
+
+ ELSE
+ -- No resolve
+ SELECT string_agg(
+ format('rule_uid=%s → mgm_id=%s (do_not_import=%s)',
+ r.rule_uid, r.mgm_id, m.do_not_import),
+ E'\n'
+ )
+ INTO too_many_mgm_ids_on_uid_and_no_resolve
+ FROM rule r
+ JOIN management m ON r.mgm_id = m.mgm_id
+ WHERE r.rule_uid = rec.rule_uid;
+
+ all_errors_with_no_resolve := all_errors_with_no_resolve || format(
+ E'\n\nrule_uid %s has ambiguous mgm_id assignments:\n%s',
+ rec.rule_uid,
+ too_many_mgm_ids_on_uid_and_no_resolve
+ );
+
+ END IF;
+ END IF;
+ END LOOP;
+
+ IF all_errors_with_no_resolve <> '' THEN
+ RAISE EXCEPTION 'Ambiguous mgm_id assignments detected:%s', all_errors_with_no_resolve;
+ END IF;
+
+ -- redo constraints
+ ALTER TABLE rule_metadata ALTER COLUMN mgm_id SET NOT NULL;
+ ALTER TABLE rule_metadata ADD CONSTRAINT rule_metadata_rule_uid_unique UNIQUE(rule_uid);
+ ALTER TABLE rule ADD CONSTRAINT rule_rule_metadata_rule_uid_f_key
+ FOREIGN KEY (rule_uid) REFERENCES rule_metadata (rule_uid);
+
+ -- set Unique constraint to (mgm_id + rule_uid)
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'rule_metadata_mgm_id_rule_uid_unique'
+ ) THEN
+ ALTER TABLE rule_metadata ADD CONSTRAINT rule_metadata_mgm_id_rule_uid_unique UNIQUE (mgm_id, rule_uid);
+ END IF;
+END$$;
+
+-- Set stm* tables hardcoded only - no Serial - stm_color filled via csv
+ALTER TABLE stm_link_type ALTER COLUMN id DROP DEFAULT;
+ALTER TABLE stm_track ALTER COLUMN track_id DROP DEFAULT;
+ALTER TABLE stm_obj_typ ALTER COLUMN obj_typ_id DROP DEFAULT;
+ALTER TABLE stm_change_type ALTER COLUMN change_type_id DROP DEFAULT;
+ALTER TABLE stm_action ALTER COLUMN action_id DROP DEFAULT;
+ALTER TABLE stm_dev_typ ALTER COLUMN dev_typ_id DROP DEFAULT;
+ALTER TABLE parent_rule_type ALTER COLUMN id DROP DEFAULT;
+
+-- Drop Sequence
+DROP SEQUENCE IF EXISTS public.stm_link_type_id_seq;
+DROP SEQUENCE IF EXISTS public.stm_track_track_id_seq;
+DROP SEQUENCE IF EXISTS public.stm_obj_typ_obj_typ_id_seq;
+DROP SEQUENCE IF EXISTS public.stm_change_type_change_type_id_seq;
+DROP SEQUENCE IF EXISTS public.stm_action_action_id_seq;
+DROP SEQUENCE IF EXISTS public.stm_dev_typ_dev_typ_id_seq;
+DROP SEQUENCE IF EXISTS public.parent_rule_type_id_seq;
\ No newline at end of file
diff --git a/roles/database/tasks/main.yml b/roles/database/tasks/main.yml
index 48e61a488a..215bc281a6 100644
--- a/roles/database/tasks/main.yml
+++ b/roles/database/tasks/main.yml
@@ -59,62 +59,69 @@
postgresql_hba_file: /var/lib/pgsql/data/pg_hba.conf
when: ansible_os_family == 'RedHat'
- - name: edit postgresql.conf log_destination
+ - name: edit {{ postgresql_config_file }} log_destination
lineinfile:
path: "{{ postgresql_config_file }}"
line: log_destination = 'syslog'
regexp: '\s*log_destination'
backup: true
- - name: edit postgresql.conf client_min_messages
+ - name: edit {{ postgresql_config_file }} client_min_messages
lineinfile:
path: "{{ postgresql_config_file }}"
line: client_min_messages = WARNING
regexp: '\s*client_min_messages'
backup: true
- - name: edit postgresql.conf log_min_messages
+ - name: edit {{ postgresql_config_file }} log_min_messages
lineinfile:
path: "{{ postgresql_config_file }}"
line: log_min_messages = WARNING
regexp: '\s*log_min_messages'
backup: true
- - name: edit postgresql.conf application_name
+ - name: edit {{ postgresql_config_file }} application_name
lineinfile:
path: "{{ postgresql_config_file }}"
line: application_name = {{ product_name }}-database
regexp: '\s*application_name'
backup: true
- - name: edit postgresql.conf log_error_verbosity
+ - name: edit {{ postgresql_config_file }} log_error_verbosity
lineinfile:
path: "{{ postgresql_config_file }}"
line: log_error_verbosity = DEFAULT
regexp: '\s*log_error_verbosity'
backup: true
- - name: edit postgresql.conf log_min_error_statement
+ - name: edit {{ postgresql_config_file }} log_min_error_statement
lineinfile:
path: "{{ postgresql_config_file }}"
line: log_min_error_statement = ERROR
regexp: '\s*log_min_error_statement'
backup: true
- - name: edit postgresql.conf log_line_prefix
+ - name: edit {{ postgresql_config_file }} log_line_prefix
lineinfile:
path: "{{ postgresql_config_file }}"
line: log_line_prefix = '%d '
regexp: '\s*log_line_prefix'
backup: true
- - name: edit postgresql.conf listening IPs
+ - name: edit {{ postgresql_config_file }} listening IPs
lineinfile:
path: "{{ postgresql_config_file }}"
line: "listen_addresses = '{{ api_network_listening_ip_address }},127.0.0.1'"
regexp: listen_addresses
backup: true
+ - name: edit {{ postgresql_config_file }} set client encoding to UTF8
+ lineinfile:
+ path: "{{ postgresql_config_file }}"
+ line: "client_encoding = 'UTF8'"
+ regexp: client_encoding
+ backup: true
+
- name: edit pg_hba.conf
blockinfile:
path: "{{ postgresql_hba_file }}"
diff --git a/roles/docker/tasks/main.yml b/roles/docker/tasks/main.yml
index 5835a25bbb..4e0814bf6f 100644
--- a/roles/docker/tasks/main.yml
+++ b/roles/docker/tasks/main.yml
@@ -1,67 +1,65 @@
---
-- block:
- - set_fact:
- ansible_user: "{{ lookup('env','USER') }}"
- os_codename: "{{ ansible_lsb.codename | lower }}"
-
- # for trixie there is no docker image yet
- - set_fact:
- os_codename: bookworm
- when: "os_codename == 'trixie'"
-
- - name: Install packages for docker download n installation
- package:
- name: "{{ item }}"
- loop:
- - gnupg2
- - apt-transport-https
- - ca-certificates
- - curl
+- block:
+ - set_fact:
+ ansible_user: "{{ lookup('env','USER') }}"
+ os_codename: "{{ ansible_lsb.codename | lower }}"
-# - software-properties-common
+ # for trixie there is no docker image yet
+ - set_fact:
+ os_codename: bookworm
+ when: "os_codename == 'trixie'"
- - name: adding docker apt signing key
- get_url:
- url: https://download.docker.com/linux/ubuntu/gpg
- dest: /etc/apt/trusted.gpg.d/docker.asc
- force: true
- mode: "0644"
- environment: "{{ proxy_env }}"
+ - name: Install packages for docker download n installation
+ package:
+ name: "{{ item }}"
+ loop:
+ - gnupg2
+ - apt-transport-https
+ - ca-certificates
+ - curl
+
+ - name: adding docker apt signing key
+ get_url:
+ url: https://download.docker.com/linux/ubuntu/gpg
+ dest: /etc/apt/trusted.gpg.d/docker.asc
+ force: true
+ mode: "0644"
+ environment: "{{ proxy_env }}"
- - name: add docker repo
- lineinfile:
- path: "/etc/apt/sources.list.d/docker.list"
- create: true
- regexp: 'download.docker.com'
- line: "deb [arch=amd64] https://download.docker.com/linux/{{ ansible_distribution | lower }} {{ os_codename }} stable"
+ - name: add docker repo
+ lineinfile:
+ path: "/etc/apt/sources.list.d/docker.list"
+ regexp: ".*https://download.docker.com/linux/.*"
+ create: true
+ line: "deb [arch=amd64] https://download.docker.com/linux/{{ ansible_distribution | lower }} {{ os_codename }} stable"
- - name: apt update
- apt: update_cache=true
- environment: "{{ proxy_env }}"
+ - name: apt update
+ apt: update_cache=true
+ environment: "{{ proxy_env }}"
+
+ - name: Install all packages for docker
+ package:
+ name: "{{ item }}"
+ loop:
+ - docker-ce
+ - docker-ce-cli
+ - containerd.io
- - name: Install all packages for docker
- package:
- name: "{{ item }}"
- loop:
- - docker-ce
- - docker-ce-cli
- - containerd.io
-
- - name: Add the group 'docker' for {{ ansible_user }} and {{ fworch_user }} to allow running docker
- user:
+ - name: Add the group 'docker' for {{ ansible_user }} and {{ fworch_user }} to allow running docker
+ user:
name: "{{ item }}"
groups: docker
append: true
- loop:
- - "{{ ansible_user }}"
- - "{{ fworch_user }}"
+ loop:
+ - "{{ ansible_user }}"
+ - "{{ fworch_user }}"
- - name: set proxy for docker daemon to get images via proxy
- import_tasks: set-docker-daemon-proxy.yml
- notify: "docker restart"
+ - name: set proxy for docker daemon to get images via proxy
+ import_tasks: set-docker-daemon-proxy.yml
+ notify: "docker restart"
- - name: include upgrade script
- import_tasks: run-upgrades.yml
- when: "installation_mode == 'upgrade'"
+ - name: include upgrade script
+ import_tasks: run-upgrades.yml
+ when: "installation_mode == 'upgrade'"
become: true
diff --git a/roles/importer/files/importer/CACTUS/FWORCH.pm b/roles/importer/files/importer/CACTUS/FWORCH.pm
deleted file mode 100644
index 098b72160c..0000000000
--- a/roles/importer/files/importer/CACTUS/FWORCH.pm
+++ /dev/null
@@ -1,992 +0,0 @@
-package CACTUS::FWORCH;
-
-use strict;
-use warnings;
-use DBI;
-require DBD::Pg;
-use IO::File;
-use Getopt::Long;
-use File::Basename;
-use CGI qw(:standard);
-require Exporter;
-require Sys::Syslog;
-use CACTUS::read_config;
-
-our @ISA = qw(Exporter);
-
-our %EXPORT_TAGS = (
- 'basic' => [ qw(
- &is_numeric &is_empty &calc_md5_of_files &file_exists &iconv_config_files_2_utf8 &iconv_2_utf8
- &replace_special_chars &remove_quotes &remove_space_at_end &print_syslog
- &remove_literal_carriage_return
- &output_txt &print_txt &print_linebreak &print_txt_with_linebreak &print_header &print_bold &print_error
- &print_html_header &print_html_footer &print_html_only
- &error_handler &error_handler_add &error_handler_get
- $output_method $dbdriver
- $echo_bin $chmod_bin $scp_bin $ssh_bin $scp_batch_mode_switch $ssh_client_screenos
- $fworch_database $fworch_srv_host $fworch_srv_user $fworch_srv_user $fworch_srv_port $fworch_srv_pw $psql_exe $psql_params
- &get_client_filter &get_device_ids_for_mgm
- &eval_boolean_sql &exec_pgsql_file &exec_pgsql_cmd &exec_pgsql_cmd_no_result
- &exec_pgsql_cmd_return_value &exec_pgsql_cmd_return_array_ref &exec_pgsql_cmd_return_table_ref
- ©_file_to_db &get_rulebase_names &get_ruleset_name_list &get_local_ruleset_name_list &get_global_ruleset_name_list &evaluate_parameters &replace_import_id_in_csv
- ) ]);
-
-our @EXPORT = (@{$EXPORT_TAGS{'basic'}});
-our $VERSION = '0.3';
-
-# globale Variablen bzw. Konstanten
-our $echo_bin = CACTUS::read_config::read_config('echo_bin');
-our $chmod_bin = CACTUS::read_config::read_config('chmod_bin');
-our $scp_bin = CACTUS::read_config::read_config('scp_bin');
-our $ssh_bin = CACTUS::read_config::read_config('ssh_bin');
-our $ssh_client_screenos = CACTUS::read_config::read_config('ssh_client_screenos');
-our $scp_batch_mode_switch = CACTUS::read_config::read_config('scp_batch_mode_switch');
-our $output_method = CACTUS::read_config::read_config('output_method');
-our $syslog_type = CACTUS::read_config::read_config('syslog_type');
-our $syslog_ident = CACTUS::read_config::read_config('syslog_ident');
-our $syslog_facility = CACTUS::read_config::read_config('syslog_facility');
-
-our $webuser;
-our $fworch_srv_pw = '';
-our $fworch_srv_host = &CACTUS::read_config::read_config("fworch database hostname");
-our $fworch_database = &CACTUS::read_config::read_config("fworch database name");
-our $fworch_srv_port = &CACTUS::read_config::read_config("fworch database port");
-our $csv_delimiter = &CACTUS::read_config::read_config("csv_delimiter");
-our $group_delimiter = &CACTUS::read_config::read_config("group_delimiter");
-our $csv_user_delimiter = &CACTUS::read_config::read_config("csv_user_delimiter");
-our $fworch_srv_user = &CACTUS::read_config::read_config("fworch_srv_user");
-our $psql_exe = &CACTUS::read_config::read_config("psql_exe");
-our $psql_params = &CACTUS::read_config::read_config("psql_params");
-our $dbdriver = "Pg";
-our $ssh_id_basename = 'import_user_secret';
-
-############################################################
-# getnum
-# hilfsfunktion fuer is_numeric
-############################################################
-sub getnum {
- use POSIX qw(strtod);
- my $str = shift;
- if (!defined($str)) {return undef;}
- # $str =~ s/^\s+//;
- # $str =~ s/\s+$//;
- $str =~ s/\./\,/g;
- $! = 0;
- my ($num, $unparsed) = strtod($str);
- if (($str eq '') || ($unparsed != 0) || $!) {
- return undef;
- }
- else {
- return $num;
- }
-}
-
-############################################################
-# is_numeric(scalar)
-# liefert true wenn scalar ein numerischer Wert ist
-############################################################
-sub is_numeric {defined getnum($_[0])}
-
-############################################################
-# is_empty(scalar)
-# liefert true wenn scalar leer ist (nicht definiert oder leerer String)
-############################################################
-sub is_empty {return (!defined($_[0]) || $_[0] eq '');}
-
-##############################################################################################
-# package output;
-
-############################################################
-# print_syslog(text, prio)
-# Ausgabe des Textes an syslog mit prio
-############################################################
-sub print_syslog {
- my $txt = shift;
- my $syslog_priority = shift;
-
- if ($^O eq 'linux') {
-
- # syslog does not work properly for windows
- &Sys::Syslog::setlogsock($syslog_type) or die $!;
- &Sys::Syslog::openlog($syslog_ident, 'cons', $syslog_facility)
- or die $!;
- &Sys::Syslog::syslog($syslog_priority, $txt)
- or die "syslog failed $!";
- &Sys::Syslog::closelog();
- }
- return;
-}
-
-############################################################
-# print_txt(text)
-# Ausgabe des Textes
-############################################################
-sub print_txt {
- my $txt = shift;
- print($txt);
- return;
-}
-
-sub output_txt {
- my $txt = shift;
- my $errlvl = shift;
-
- print($txt);
- if (defined($errlvl) && $errlvl > 0) {
- print_syslog($txt, 'err');
-
- }
- else {
- print_syslog($txt, 'notice');
- }
- return;
-}
-
-############################################################
-# print_linebreak
-# Ausgabe eines linebreaks
-############################################################
-sub print_linebreak {
- our $output_method;
- if ($output_method eq 'html') {print_txt(" \n");}
- else {print_txt("\n");}
- return;
-}
-
-############################################################
-# print_txt_with_linebreak(text)
-# Ausgabe des Textes mit anschliessendem linebreak
-############################################################
-sub print_txt_with_linebreak {
- my $txt = shift;
- print_txt($txt);
- print_linebreak();
- return;
-}
-
-############################################################
-# print_header(text)
-# Ausgabe des Textes, bei html als
-############################################################
-sub print_header {
- my $txt = shift;
- our $output_method;
- print_syslog($txt, 'info');
- if ($output_method eq 'html') {print_txt("$txt ");}
- else {print_txt($txt);}
- return;
-}
-
-############################################################
-# print_bold(text)
-# Ausgabe des Textes in fett, wenn html
-############################################################
-sub print_bold {
- my $txt = shift;
- our $output_method;
- print_syslog($txt, 'notice');
- if ($output_method eq 'html') {print_txt("$txt ");}
- else {print_txt($txt);}
- return;
-}
-
-############################################################
-# print_error(text)
-# Ausgabe des Textes in rot (wenn HTML)
-############################################################
-sub print_error {
- my $txt = shift;
- our $output_method;
- print_syslog($txt, 'warning');
- if ($output_method eq 'html') {
- print_txt("");
- print_bold($txt);
- print_txt(" ");
- }
- else {print_txt($txt);}
- return;
-}
-
-############################################################
-# print_html_only($html_txt)
-# Ausgabe von HTML-Code, falls html
-############################################################
-sub print_html_only {
- my $htmlcode = shift;
- our $output_method;
- if ($output_method eq 'html') {print_txt("$htmlcode");}
- return;
-}
-
-############################################################
-# print_html_header()
-# Ausgabe eines HTML-Headers, falls html
-############################################################
-sub print_html_header {
- our $output_method;
- if ($output_method eq 'html') {
- print_txt("Content-Type: text/html\n\n");
- print_txt("");
- print_txt(
- "");
- print_txt(
- "");
- print_txt(
- " "
- );
- print_txt(""
- );
- }
- return;
-}
-
-############################################################
-# print_html_footer()
-# Ausgabe der schliessenden TAGS
-############################################################
-sub print_html_footer {
- our $output_method;
- if ($output_method eq 'html') {
- print_txt("");
- }
- return;
-}
-
-############################################################
-# iconv_config_files_2_utf8 ($file,$tmpdir)
-# convert latin1 to utf-8
-############################################################
-sub iconv_config_files_2_utf8 {
- my $str_of_files_to_sum = shift;
- my $tmpdir = shift;
- my $file;
- my @file_ar;
-
- if (@file_ar) {
- @file_ar = split(/,/, $str_of_files_to_sum);
- foreach $file (@file_ar) {
- if ($file !~ /^fwauth\.NDB/) {
- iconv_2_utf8($file, $tmpdir);
- }
- }
- }
- return;
-}
-
-############################################################
-# iconv_2_utf8 ($file)
-# convert latin1 to utf-8
-############################################################
-sub iconv_2_utf8 {
- my $file_to_conv = shift;
- my $tmpdir = shift;
- my $md5sum_file = "$tmpdir/cfg_hash.md5";
- my $cmd = "/usr/bin/iconv --from-code latin1 --to-code utf-8 $file_to_conv --output $file_to_conv.utf-8";
- system($cmd);
- $cmd = "/bin/mv $file_to_conv.utf-8 $file_to_conv";
- system($cmd);
-}
-
-
-############################################################
-# calc_md5_of_files ($file,$tmpdir)
-# gibt den kontaktenierten MD5-Hash-Wert aller Files zurueck
-############################################################
-sub calc_md5_of_files {
- my $str_of_files_to_sum = shift;
- my $tmpdir = shift;
- my $file;
- my $total = '';
- my @file_ar;
-
- @file_ar = split(/,/, $str_of_files_to_sum);
- foreach $file (@file_ar) {
- $total .= calc_md5_of_file($file, $tmpdir);
- }
- return $total;
-}
-
-############################################################
-# calc_md5_of_file ($file)
-# gibt den MD5-Hash-Wert zurueck
-############################################################
-sub calc_md5_of_file {
- my $file_to_sum = shift;
- my $tmpdir = shift;
- my $md5sum_file = "$tmpdir/cfg_hash.md5";
- my $cmd = "/usr/bin/md5sum $file_to_sum | cut --bytes=-32 >$md5sum_file";
- system($cmd);
- open(MD5SUM, "<$md5sum_file");
- my $md5sum = ;
- close MD5SUM;
- $cmd = "/bin/rm -f $md5sum_file";
- system($cmd);
-
- if (defined($md5sum)) {
- return substr($md5sum, 0, 32);
- }
- else {
- return "error-in-md5-sum-calc";
- }
-}
-
-############################################################
-# file_exists ($file)
-# true, wenn das file $file existiert
-############################################################
-sub file_exists {
- return (-e $_[0]);
-}
-
-# package error_handling;
-############################################################
-# error_handler(string)
-# fehlerbehandlung
-# in der einfachsten Fassung Ausgabe des Fehlerstrings
-# und exit 1
-############################################################
-sub error_handler {
- my $err_str = $_[0];
-
- # print ("Fehler: $err_str\n");
- return $err_str;
-
- # exit 1;
-}
-
-############################################################
-# error_handler_get
-# fehlermeldungen auslesen
-############################################################
-sub error_handler_get {
- my $current_import_id = shift;
-
- if (defined($current_import_id)) {
- my $existing_error_str = &exec_pgsql_cmd_return_value("SELECT import_errors FROM import_control WHERE control_id=$current_import_id");
- return $existing_error_str;
- }
- else {
- return undef;
- }
-}
-
-############################################################
-# error_handler_update
-# fehlermeldungen anhaengen in import_control
-############################################################
-sub error_handler_update {
- my $current_import_id = shift;
- my $new_error_str = shift;
-
- if (defined($current_import_id)) {
- my $existing_error_str = &error_handler_get($current_import_id);
- if (!defined($existing_error_str) || $existing_error_str eq '') {$existing_error_str = '';}
- else {$existing_error_str .= "; ";}
- &exec_pgsql_cmd_no_result("UPDATE import_control SET import_errors='$existing_error_str$new_error_str', successful_import=FALSE WHERE control_id=$current_import_id");
- }
-}
-
-############################################################
-# error_handler_add
-# fehlermeldungen an string anhaengen, wenn definiert
-############################################################
-sub error_handler_add {
- my $current_import_id = shift;
- my $error_level = shift;
- my $current_error_str = shift;
- my $current_error_count = shift;
- my $previous_errors_count = shift;
- my $error_tag = 'DEBUG';
-
- my $previous_errors_string = &error_handler_get($current_import_id);
- # print ("debug error handler: lvl=$error_level, curr_err_str=$current_error_str, glob_err_str=$previous_errors_string, #curr_errors=$current_error_count, #total_errs=$previous_errors_count\n");
- my $total_error_str = (defined($previous_errors_string) ? "$previous_errors_string" : '');
- my $total_error_count = $previous_errors_count;
- if ($error_level) {
- if ($error_level == 1) {$error_tag = 'INFO';}
- if ($error_level == 2) {$error_tag = 'WARN';}
- if ($error_level == 3) {$error_tag = 'ERR';}
- if ($error_level > 3) {$error_tag = 'FATAL-ERR';}
- if (!defined($previous_errors_string)) {$previous_errors_string = '';}
- if ($previous_errors_string ne '') {$previous_errors_string .= "\n";}
- if ($current_error_count) {if ($current_error_str ne '') {$current_error_str = "$error_tag-$current_error_str";}}
- if ($error_level > 1 && $current_error_count) {$total_error_str = "$previous_errors_string$current_error_str";}
- # print ("current_error_count: $current_error_count\n");
- $total_error_count = $previous_errors_count + $current_error_count;
- if ($error_level > 1 && $current_error_count) {
- output_txt("$current_error_str\n", $error_level);
- &error_handler_update($current_import_id, $current_error_str);
- }
- if ($error_level > 3 && $current_error_count) {exit $error_level;}
- }
- return $total_error_count;
-}
-
-# package string_manipulation;
-############################################################
-# remove_quotes(string)
-# entfernt aus string alle Anfuehrungszeichen
-############################################################
-sub remove_quotes {
- my $str = $_[0];
- $str =~ s/"//g;
- $str =~ s/'//g;
- $str =~ s/`//g;
- $str =~ s/´//g;
- return $str;
-}
-
-############################################################
-# remove_space_at_end(string)
-# entfernt aus string das letzte Zeichen, wenn es ein space ist
-############################################################
-sub remove_space_at_end {
- my $str = $_[0];
- $str =~ s/\s$//g;
- return $str;
-}
-
-############################################################
-# remove_literal_carriage_return(filename)
-# entfernt jedes Auftreten eines literal_carriage_returns inenrhalb des Files
-############################################################
-sub remove_literal_carriage_return {
- my $in_file = $_[0];
- my $out_file = "$in_file.no_cr";
- my $line;
-
- open(IN, $in_file) || die "$in_file konnte nicht geoeffnet werden.\n";
- open(OUT, ">$out_file")
- || die "$out_file konnte nicht geoeffnet werden.\n";
-
- while () {
- $line = $_; # Zeileninhalt merken
- $line =~ s/\r\n/\n/; # dos2unix
- $line =~ s/\x0D/\\r/g; # literal carriage return entfernen
- print OUT $line;
- }
- close(IN);
- close(OUT);
- rename($in_file, "$in_file.orig");
- rename($out_file, $in_file);
- return;
-}
-
-############################################################
-# replace_special_chars_in_string(string)
-# ersetzt in string alle Sonderzeichen durch Standard ASCII-Zeichen
-# hier fehlt noch ein Mechanismus, der gefundene Ersetzungen protokolliert
-############################################################
-sub replace_special_chars_in_str {
- my $str = $_[0];
- $str =~ s/ä/ae/g;
- $str =~ s/ö/oe/g;
- $str =~ s/ü/ue/g;
- $str =~ s/Ä/Ae/g;
- $str =~ s/Ö/Oe/g;
- $str =~ s/Ü/Ue/g;
- $str =~ s/ß/ss/g;
- $str =~ s/é/e/g;
- $str =~ s/è/e/g;
- $str =~ s/á/a/g;
- $str =~ s/à/a/g;
- $str =~ s/í/i/g;
- $str =~ s/ì/i/g;
- return $str;
-}
-
-############################################################
-# replace_special_chars(file_name)
-# ersetzt in file_name alle Sonderzeichen durch ASCII-Zeichen
-############################################################
-sub replace_special_chars {
- my ($orig_line, $line, $input, $file, $output, @text);
- @text = qw();
-
- $input = new IO::File("< $_[0]")
- or die "Cannot open file $_[0] for reading: $!";
-
- while (<$input>) {
- $line = $_;
- $orig_line = $line;
- $line = replace_special_chars_in_str($line);
- push @text, ($line);
-
- # print $output $line;
- if ($orig_line ne $line) {print "changed $orig_line to $line\n";}
- }
- $input->close;
- $output = new IO::File("> $_[0]")
- or die "Cannot open file $_[0] for writing: $!";
- foreach $line (@text) {
- print $output $line;
- }
- $output->close;
-}
-
-
-############################################################
-# replace_import_id_in_csv
-#
-############################################################
-sub replace_import_id_in_csv {
- my $csv_file = shift;
- my $import_id = shift;
- my $result = 0;
- my $line;
-
- # s/^\"(\d+)\"/\"$import_id\"/g' $csv_file;
- my $CSVFILE = new IO::File("< $csv_file");
- my $NEWCSVFILE = new IO::File("> $csv_file.tmp");
- if ($CSVFILE && $NEWCSVFILE)
- {
- while (<$CSVFILE>) {
- $line = $_;
- $line =~ s/^\"(\d+)\"/\"$import_id\"/;
- #print ("line=$line");
- print $NEWCSVFILE $line;
- }
- $CSVFILE->close;
- $NEWCSVFILE->close;
- system("mv '$csv_file.tmp' '$csv_file'");
- }
- else
- {
- output_txt("Cannot open file $csv_file for reading: $!", 3);
- }
- return (!$result);
-}
-
-############################################################
-# copy_file_to_db
-#
-############################################################
-sub copy_file_to_db {
- my $sqlcode = shift;
- my $csv_file = shift;
- my ($dbh, $sth);
- my $result = 0;
-
- my $CSVFILE = new IO::File("< $csv_file");
- if ($CSVFILE) {
- $dbh = DBI->connect("dbi:Pg:dbname=$fworch_database;host=$fworch_srv_host;port=$fworch_srv_port", "$fworch_srv_user", "$fworch_srv_pw");
- if (!defined $dbh) {
- output_txt("Cannot connect to database!\n", 3);
- }
- else {
- $dbh->do($sqlcode);
- while (<$CSVFILE>) {
- $dbh->pg_putline($_);
- }
- $result = $dbh->pg_endcopy;
- $dbh->disconnect;
- $CSVFILE->close;
- }
- }
- else {
- output_txt("Cannot open file $csv_file for reading: $!", 3);
- }
- return (!$result);
-}
-
-############################################################
-# eval_boolean_sql(sql-befehl)
-# wertet das Ergebnis des sql-befehls aus (true oder false
-############################################################
-sub eval_boolean_sql {
- my ($sqlcode);
- my ($rc, $dbh, $sth);
- my (@result);
- our ($fworch_srv_host, $fworch_database, $fworch_srv_port, $fworch_srv_user, $fworch_srv_pw, $dbdriver);
-
- $sqlcode = $_[0];
- $dbh = DBI->connect("dbi:$dbdriver:dbname=$fworch_database;host=$fworch_srv_host;port=$fworch_srv_port", "$fworch_srv_user", "$fworch_srv_pw");
- if (!defined $dbh) {die "Cannot connect to database!\n";}
- $sth = $dbh->prepare($sqlcode);
- if (!defined $sth) {die "Cannot prepare statement: $DBI::errstr\n";}
- $sth->execute;
- @result = $sth->fetchrow_array;
- $sth->finish;
- $dbh->disconnect;
- return $result[0];
-}
-
-############################################################
-# exec_pgsql_cmd_return_value(cmd)
-# fuehrt den SQL-Befehl cmd aus und gibt das Resultat des SQL-Befehls zurueck
-############################################################
-sub exec_pgsql_cmd_return_value {
- return eval_boolean_sql($_[0]);
-}
-
-############################################################
-# exec_pgsql_cmd_return_array_ref(cmd)
-# fuehrt den SQL-Befehl cmd aus und gibt das Resultat des SQL-Befehls
-# als Verweis auf ein Array zurueck
-############################################################
-sub exec_pgsql_cmd_return_array_ref {
- my ($res, $err_str, $err_flag, $sqlcode, $result);
- # $result: references the result array
- my ($rc, $dbh, $sth);
- our ($fworch_srv_host, $fworch_database, $fworch_srv_port, $fworch_srv_user, $fworch_srv_pw, $dbdriver);
-
- $sqlcode = $_[0];
- $err_flag = 0;
- $dbh = DBI->connect("dbi:$dbdriver:dbname=$fworch_database;host=$fworch_srv_host;port=$fworch_srv_port", "$fworch_srv_user", "$fworch_srv_pw");
- if (!defined $dbh) {die "Cannot connect to database!\n";}
- if ($sqlcode !~ /^$/) {
- # print "$sqlcode\n";
- $sth = $dbh->prepare($sqlcode);
- if (!defined $sth) {die "Cannot prepare statement: $DBI::errstr\n";}
- $res = $sth->execute;
- $err_str = $sth->errstr;
- if (defined($err_str) && length($err_str) > 0) {
- $err_flag = 1;
- }
- }
- $result = $sth->fetchrow_arrayref;
- if (!$err_flag) {
- $err_str = $sth->errstr;
- if (defined($err_str) && length($err_str) > 0) {
- $err_flag = 1;
- }
- }
- $sth->finish;
- $dbh->disconnect;
- # print("locally: result[0][0]=$result->[0][0]");
- return $result;
-}
-
-############################################################
-# exec_pgsql_cmd_return_table_ref(cmd)
-# fuehrt den SQL-Befehl cmd aus und gibt das Resultat des SQL-Befehls
-# als Verweis auf eine Tabelle (2-dim-Array) zurueck
-############################################################
-sub exec_pgsql_cmd_return_table_ref {
- my ($res, $err_str, $err_flag, $sqlcode, $result);
- # $result: references the result array
- my ($rc, $dbh, $sth);
- our ($fworch_srv_host, $fworch_database, $fworch_srv_port, $fworch_srv_user, $fworch_srv_pw);
-
- $sqlcode = $_[0];
- my $keyfield = $_[1];
- $err_flag = 0;
- $dbh = DBI->connect("dbi:Pg:dbname=$fworch_database;host=$fworch_srv_host;port=$fworch_srv_port", "$fworch_srv_user", "$fworch_srv_pw");
- if (!defined $dbh) {die "Cannot connect to database!\n";}
- if ($sqlcode !~ /^$/) {
- # print "$sqlcode\n";
- $sth = $dbh->prepare($sqlcode);
- if (!defined $sth) {die "Cannot prepare statement: $DBI::errstr\n";}
- $res = $sth->execute;
- $err_str = $sth->errstr;
- if (defined($err_str) && length($err_str) > 0) {
- $err_flag = 1;
- }
- }
- # $result = $sth->fetchrow_arrayref;
- # $result = $sth->fetchall_arrayref;
- $result = $sth->fetchall_hashref($keyfield);
- if (!$err_flag) {
- $err_str = $sth->errstr;
- if (defined($err_str) && length($err_str) > 0) {
- $err_flag = 1;
- }
- }
- $sth->finish;
- $dbh->disconnect;
- # print("locally: result[0][0]=$result->[0][0]");
- return $result;
-}
-
-############################################################
-# exec_pgsql_cmd(cmd)
-# fuehrt den SQL-Befehl cmd aus und gibt error_code zurueck
-# das Resultat des SQL-Befehls wird ueber $_[1] gemeldet
-############################################################
-sub exec_pgsql_cmd {
- my ($res, $err_str, $err_flag, $sqlcode, @result);
- my ($rc, $dbh, $sth);
- our ($fworch_srv_host, $fworch_database, $fworch_srv_port, $fworch_srv_user, $fworch_srv_pw);
-
- $sqlcode = $_[0];
- $err_flag = 0;
- $dbh = DBI->connect("dbi:Pg:dbname=$fworch_database;host=$fworch_srv_host;port=$fworch_srv_port", "$fworch_srv_user", "$fworch_srv_pw");
- if (!defined $dbh) {die "Cannot connect to database!\n";}
- if ($sqlcode !~ /^$/) {
- # print "$sqlcode\n";
- $sth = $dbh->prepare($sqlcode);
- if (!defined $sth) {die "Cannot prepare statement: $DBI::errstr\n";}
- $res = $sth->execute;
- $err_str = $sth->errstr;
- if (defined($err_str) && length($err_str) > 0) {
- $err_flag = 1;
- }
- }
- @result = $sth->fetchrow_array;
- if (!$err_flag) {
- $err_str = $sth->errstr;
- if (defined($err_str) && length($err_str) > 0) {
- $err_flag = 1;
- }
- }
- $sth->finish;
- $dbh->disconnect;
- # return $result[0];
- if (defined($_[1])) {
- $_[1] = $result[0];
- if (!defined($result[0])) {
- return error_handler('EMPTY_SQL_RESULT');
- }
- }
- if (!$err_flag) {return 0;}
- else {return $err_str;}
-}
-
-############################################################
-# exec_pgsql_cmd_no_result(cmd)
-# fuehrt den SQL-Befehl cmd aus und gibt kein Resultat zurueck
-############################################################
-sub exec_pgsql_cmd_no_result {
- my ($res, $err_str, $sqlcode, $err_flag);
- my ($dbh, $sth);
- our ($fworch_srv_host, $fworch_database, $fworch_srv_port, $fworch_srv_user, $fworch_srv_pw);
-
- $err_flag = 0;
- $sqlcode = $_[0];
- $dbh = DBI->connect("dbi:Pg:dbname=$fworch_database;host=$fworch_srv_host;port=$fworch_srv_port", "$fworch_srv_user", "$fworch_srv_pw");
- if (!defined $dbh) {die "Cannot connect to database!\n";}
- if ($sqlcode !~ /^$/) {
- # print "$sqlcode\n";
- $sth = $dbh->prepare($sqlcode);
- if (!defined $sth) {die "Cannot prepare statement: $DBI::errstr\n";}
- $res = $sth->execute;
- $err_str = $sth->errstr;
- if (defined($err_str) && length($err_str) > 0) {
- $err_flag = 1;
- }
- }
- $sth->finish;
- $dbh->disconnect;
- if (!$err_flag) {return 0;}
- else {return $err_str;}
-}
-
-############################################################
-# exec_pgsql_file(file_name)
-# fuehrt die SQL-Befehle in file_name aus
-############################################################
-sub exec_pgsql_file {
- my ($line, $input, $file, $sqlcode, $res, $err_str, $err_flag);
- my ($dbh, $sth);
- our ($fworch_srv_host, $fworch_database, $fworch_srv_port, $fworch_srv_user, $fworch_srv_pw);
-
- $err_flag = 0;
- $input = new IO::File("< $_[0]") or die "Cannot open file $_[0] for reading: $!";
- $dbh = DBI->connect("dbi:Pg:dbname=$fworch_database;host=$fworch_srv_host;port=$fworch_srv_port", "$fworch_srv_user", "$fworch_srv_pw");
- if (!defined $dbh) {die "Cannot connect to database!\n";}
- while (<$input>) {
- $sqlcode = $_;
- if ($sqlcode !~ /^$/) {
- # print "$sqlcode\n";
- $sth = $dbh->prepare($sqlcode);
- if (!defined $sth) {die "Cannot prepare statement: $DBI::errstr\n";}
- $res = $sth->execute;
- $err_str = $sth->errstr;
- if (defined($err_str) && length($err_str) > 0) {
- $err_flag = 1;
- last;
- }
- }
- }
- $input->close;
- $sth->finish;
- $dbh->disconnect;
- if (!$err_flag) {return 0;}
- else {return $err_str;}
-}
-
-############################################################
-############################################################
-
-sub get_device_ids_for_mgm {
- my $mgm_id = $_[0];
- my $aref = exec_pgsql_cmd_return_table_ref("SELECT dev_id FROM device WHERE mgm_id=$mgm_id", 'dev_id');
- # print("aref0: $aref->[0], aref1: $aref->[1]");
- return $aref;
-}
-
-############################################################
-# get_client_filter(client_id)
-# zu einem Tenant einen Filter generieren
-# Ergebnis ist ein String: zB. "ip<<'1.0.0.0/8' AND ip<<'2.0.0.0/16'"
-# Sonderfall: wenn client_id=0 (nicht existent): RETURN "TRUE" --> kein Filter
-# parameter1: client_id
-############################################################
-sub get_client_filter {
- my $client_id = $_[0];
- my ($client_net_ip, $filter, $dbh, $sth, $relevant_import_id, $err_str, $sqlcode);
- our ($fworch_database, $fworch_srv_host, $fworch_srv_port, $fworch_srv_user, $fworch_srv_pw);
-
- $filter = "TRUE";
- if (defined($client_id) && $client_id ne '' && $client_id != 0) {
- $sqlcode = "SELECT client_net_ip FROM client_network WHERE client_id=$client_id";
- $dbh = DBI->connect("dbi:Pg:dbname=$fworch_database;host=$fworch_srv_host;port=$fworch_srv_port", "$fworch_srv_user", "$fworch_srv_pw");
- if (!defined $dbh) {die "Cannot connect to database!\n";}
- $sth = $dbh->prepare($sqlcode);
- if (!defined $sth) {die "Cannot prepare statement: $DBI::errstr\n";}
- $sth->execute;
- $err_str = $sth->errstr;
- if (defined($err_str) && length($err_str) > 0) {error_handler($err_str);}
- $filter = "(";
- while (($client_net_ip) = $sth->fetchrow()) {
- $filter .= " obj_ip<<='$client_net_ip' OR '$client_net_ip'<<=obj_ip OR";
- # sollte eigentlich ein noch zu definierenden Operator sein: ip1 # ip2 ip1 und ip2 haben eine nicht leere Schnittmenge
- }
- $filter .= " FALSE)";
- $sth->finish;
- $dbh->disconnect;
- }
- return $filter;
-}
-
-sub get_rulebase_names {
- # getting device-info for all devices of the current mgmt
- my $mgm_id = shift;
- my $dbdriver = shift;
- my $fworch_database = shift;
- my $fworch_srv_host = shift;
- my $fworch_srv_port = shift;
- my $fworch_srv_user = shift;
- my $fworch_srv_pw = shift;
-
- my $dbh = DBI->connect("dbi:$dbdriver:dbname=$fworch_database;host=$fworch_srv_host;port=$fworch_srv_port", "$fworch_srv_user", "$fworch_srv_pw");
- if (!defined $dbh) {die "Cannot connect to database!\n";}
-# my $sth = $dbh->prepare("SELECT dev_id,dev_name,local_rulebase_name,global_rulebase_name FROM device WHERE mgm_id=$mgm_id AND NOT do_not_import");
- my $sth = $dbh->prepare("SELECT dev_id,dev_name,local_rulebase_name FROM device WHERE mgm_id=$mgm_id AND NOT do_not_import");
- if (!defined $sth) {die "Cannot prepare statement: $DBI::errstr\n";}
- $sth->execute;
- my $rulebases = $sth->fetchall_hashref('dev_id');
- $sth->finish;
- $dbh->disconnect;
- return $rulebases;
-}
-
-# convert hash to comma separated string
-# sub get_ruleset_name_list {
-# my $href_rulesetname = shift;
-# my $result = '';
-
-# while ( (my $key, my $value) = each %{$href_rulesetname}) {
-# $result .= $value->{'dev_rulebase'} . ',';
-# }
-# if ($result =~ /^(.+?)\,$/) { # stripping off last comma
-# return $1;
-# }
-# return $result;
-# }
-
-# convert hash to comma separated string
-sub get_local_ruleset_name_list {
- my $href_rulesetname = shift;
- my $result = '';
-
- while ( (my $key, my $value) = each %{$href_rulesetname}) {
- $result .= $value->{'local_rulebase_name'} . ',';
- }
- if ($result =~ /^(.+?)\,$/) { # stripping off last comma
- return $1;
- }
- return $result;
-}
-
-# convert hash to comma separated string
-sub get_global_ruleset_name_list {
- my $href_rulesetname = shift;
- my $result = '';
-
- if (defined($href_rulesetname)) {
- while ( (my $key, my $value) = each %{$href_rulesetname}) {
- if (defined($value->{'global_rulebase_name'})) {
- $result .= $value->{'global_rulebase_name'} . ',';
- } else {
- $result .= ',';
- }
- }
- if ($result =~ /^(.+?)\,$/) { # stripping off last comma
- return $1;
- }
- return $result;
- } else {
- return "";
- }
-}
-
-sub evaluate_parameters {
- my $mgm_id = shift;
- my $mgm_name = shift;
-
- if (!defined($mgm_id) || $mgm_id eq '') {
- if (defined($mgm_name) && $mgm_name ne '') {
- $mgm_id = exec_pgsql_cmd_return_value("select mgm_id from management where mgm_name='$mgm_name'");
- }
- else {&error_handler_add(undef, my $error_level = 5, "fworch-importer-single.pl: missing argument (mgm_id || mgm_name)", 1, 0);} # no valid input given
- }
- if (!defined($mgm_id) || $mgm_id eq '') {
- &error_handler_add(undef, my $error_level = 5, 'Management ' . (($mgm_name ne '') ? $mgm_name . ' ' : '') . 'not found', 1, 0);
- }
- return ($mgm_id, $mgm_name);
-}
-
-#####################################################################################
-
-1;
-__END__
-
-=head1 NAME
-
-FWORCH - Perl extension for fworch
-
-=head1 SYNOPSIS
-
- use FWORCH;
- the function read_basic_fworch_data() must be called first to
- generate the hashes before using the DB-lookup functions
-
-=head1 DESCRIPTION
-
-fworch Perl Module
-support for
-- importing configs into fworch Database
-- basic functions to access fworch DB
-
-=head2 EXPORT
-
- global variables
- %managementID
- %object_typID
- %IP_protocolID
-
- DB functions
- read_basic_fworch_data()
- these functions perform the lookups into the above hashes:
- get_mgmtID(management-name)
- get_object_typID(objekt-name)
- get_ip_protoID(IP-protocol-name)
-
- Basic functions
- is_numeric(scalar)
-
-=head1 SEE ALSO
-
- behind the door
-
-=head1 AUTHOR
-
- Tim Purschke, tmp@cactus.de
-
-=cut
diff --git a/roles/importer/files/importer/CACTUS/FWORCH/import.pm b/roles/importer/files/importer/CACTUS/FWORCH/import.pm
deleted file mode 100644
index f4dd78e16d..0000000000
--- a/roles/importer/files/importer/CACTUS/FWORCH/import.pm
+++ /dev/null
@@ -1,1332 +0,0 @@
-package CACTUS::FWORCH::import;
-use strict;
-use warnings;
-use DBD::Pg;
-use DBI;
-use IO::File;
-use Getopt::Long;
-use File::Basename;
-use FileHandle;
-use CGI qw(:standard);
-use Time::HiRes qw(time); # fuer hundertstelsekundengenaue Messung der Ausfuehrdauer
-use CACTUS::FWORCH;
-use CACTUS::read_config;
-
-require Exporter;
-our @ISA = qw(Exporter);
-
-our %EXPORT_TAGS = (
- 'basic' => [ qw(
- &get_mgm_id_of_import &is_import_running &get_matching_import_id &remove_control_entry
- &insert_control_entry &is_initial_import
- &put_ssh_keys_in_place
- &get_import_infos_for_device &get_import_infos_for_mgm
- &import_cleanup_and_summary &ruleset_does_not_fit &clean_up_fworch_db
- &print_debug &print_verbose &d2u &calc_subnetmask &convert_mask_to_dot_notation
- &print_results_monitor
- &print_results_files_objects
- &print_results_files_rules
- &print_results_files_users
- &print_results_files_zones
- &print_results_files_audit_log
- &get_proto_number
- &set_last_change_time
- &fill_import_tables_from_csv_with_sql
- &fill_import_tables_from_csv
- %network_objects @network_objects %services @services %rulebases @ruleorder @rulebases @zones
- %user %usergroup @user_ar $usergroupdelimiter
- $audit_log_count %auditlog
- $mode $last_change_time_of_config
- $verbose $debug $mgm_name
- @obj_outlist @srv_outlist @rule_outlist
- ) ] );
-
-our @EXPORT = ( @{ $EXPORT_TAGS{'basic'} } );
-our $VERSION = '0.3';
-
-
-our $verbose = 0; # globaler Schalter bzgl. der Ausgabe: 1=verbose, 0=silent;
-our $debug = 0; # globaler Schalter bzgl. Druck der Debug info: 1=debug, 0=silent;
-our $mode;
-
-# Laufvariablen Parser
-our $mgm_name = ''; # globale Variable fuer den Namen des Managements
-# nur innerhalb der objekte gueltig
-# hashes zur Speicherung der Parserergebnisse (Eigenschaften)
-our @zones;
-our %network_objects;
-our %services;
-our %rulebases; # hash der rulebases
-our @ruleorder; # Array mit Regel-IDs
-# wird derzeit eigentlich nur fuer netscreen benoetigt
-# CheckPoint: 1-n
-# Netscreen: policy id), Feld startet mit 1!
-# phion: wie Check Point
-
-our @network_objects = (); # liste aller namen der netzobjekte
-our @services = (); # liste aller namen der services
-our @rulebases = (); # liste der definierten rulebases
-
-our $audit_log_count = 0;
-our %auditlog;
-
-our %user;
-our %usergroup;
-our @user_ar = ();
-our $usergroupdelimiter = &CACTUS::read_config::read_config('usergroup_delimiter');
-
-our $last_change_time_of_config;
-
-# Listen mit Schnittstelleninformationen
-# xxx_outlist: Felder im Perl-Hash, die in CSV-Datei geschrieben werden
-# xxx_import_fields: Felder der xxx_import-Tabellen, die aus der CSV-Datei gefuellt werden
-# @xxx: (@services, @objects, ...): Listen mit den Namen? aller Basiselemente
-
-# TODO: zu jedem Gruppenmitglied die Moeglichkeit eines Negationsflags
-# TODO: zu jeder Referenz (Gruppenmitglieder, Regelbestandteile) eindeutige UIDs einfuehren und nur diese fuer Import-Prozess verwenden
-
-# Ausgabe der Objektparameter
-our @obj_outlist =(qw ( name type members member_refs cpver ipaddr ipaddr_last color comments location zone
- UID last_change_admin last_change_time));
-# Planung:
-# - member enthaelt nur noch eine Liste mit Referenzen und nicht mit Namen
-# - Separator: |
-# - Anzeige der Negation mit "$$__not__$$"
-# - Neues DB-Feld fuer Negation fuer alle Gruppen- und Regelanteilbeziehungen
-
-our @obj_import_fields = qw (
- control_id
- obj_name
- obj_typ
- obj_member_names
- obj_member_refs
- obj_sw
- obj_ip
- obj_ip_end
- obj_color
- obj_comment
- obj_location
- obj_zone
- obj_uid
- last_change_admin
- last_change_time
-);
-
-# Ausgabe der Serviceparameter
-our @srv_outlist =(qw ( name typ type members member_refs color ip_proto port port_last src_port src_port_last
- comments rpc_port timeout_std timeout UID last_change_admin last_change_time));
-
-our @svc_import_fields = qw (
- control_id
- svc_name
- svc_typ
- svc_prod_specific
- svc_member_names
- svc_member_refs
- svc_color
- ip_proto
- svc_port
- svc_port_end
- svc_source_port
- svc_source_port_end
- svc_comment
- rpc_nr
- svc_timeout_std
- svc_timeout
- svc_uid
- last_change_admin
- last_change_time
-);
-# Ausgabe der Userparameter
-our @user_outlist =(qw ( type members member_refs color comments uid expdate last_change_admin ));
-
-our @user_import_fields = qw (
- control_id
- user_name
- user_typ
- user_member_names
- user_member_refs
- user_color
- user_comment
- user_uid
- user_valid_until
- last_change_admin
-);
-
-our @zone_outlist =(qw ( zone ));
-
-our @zone_import_fields = qw (
- control_id
- zone_name
-);
-
-# Ausgabe der Regelparameter
-our @rule_outlist =(qw ( rule_id disabled src.op src src.refs dst.op dst dst.refs services.op services services.refs
- action track install time comments name UID header_text src.zone dst.zone last_change_admin parent_rule_uid));
-
-our @rule_import_fields = qw (
- control_id
- rule_num
- rulebase_name
- rule_ruleid
- rule_disabled
- rule_src_neg
- rule_src
- rule_src_refs
- rule_dst_neg
- rule_dst
- rule_dst_refs
- rule_svc_neg
- rule_svc
- rule_svc_refs
- rule_action
- rule_track
- rule_installon
- rule_time
- rule_comment
- rule_name
- rule_uid
- rule_head_text
- rule_from_zone
- rule_to_zone
- last_change_admin
- parent_rule_uid
-);
-
-our @auditlog_outlist = qw ( change_time management_name changed_object_name changed_object_uid changed_object_type change_action change_admin );
-
-our @auditlog_import_fields = qw ( control_id import_changelog_nr change_time management_name changed_object_name changed_object_uid changed_object_type change_action change_admin );
-
-############################################################################################################
-# allgemeine Funktionen
-############################################################################################################
-
-sub put_ssh_keys_in_place {
- my $workdir = shift;
- my $ssh_public_key = shift;
- my $ssh_private_key = shift;
- my $fehler_count = 0;
-
- # debugging
- # print ("put_ssh_keys_in_place::workdir=$workdir, ssh_public_key=$ssh_public_key, ssh_private_key=$ssh_private_key\n");
- $fehler_count += (system("$echo_bin \"$ssh_private_key\" > $workdir/$CACTUS::FWORCH::ssh_id_basename") != 0);
- if (defined($ssh_public_key) && $ssh_public_key ne "") {
- # only create public key file if the key is defined and not empty
- $fehler_count += (system("$echo_bin \"$ssh_public_key\" > $workdir/$CACTUS::FWORCH::ssh_id_basename.pub") != 0); # only necessary for netscreen
- }
- $fehler_count += (system("$chmod_bin 400 $workdir/$CACTUS::FWORCH::ssh_id_basename") != 0);
- return $fehler_count;
-}
-
-
-sub ruleset_does_not_fit {
- my $rulebasename_to_find = shift;
- my $href_rulesetname = shift;
- my $result = 1;
-
- while ( (my $key, my $value) = each %{$href_rulesetname}) {
- if ($rulebasename_to_find eq $value->{'local_rulebase_name'}) {
- $result=0;
- }
- }
- return $result;
-}
-
-############################################################
-# get_mgm_id_of_import(mgm_id,mgm_name,dev_id,dev_name)
-# liefert wenn herleitbar die mgm_id zurueck, ansonsten undef
-############################################################
-sub get_mgm_id_of_import {
- my $mgm_id = shift;
- my $mgm_name = shift;
- my $dev_id = shift;
- my $dev_name = shift;
-
- if (is_empty($mgm_id)) {
- if (!is_empty($dev_id)) {
- $mgm_id = exec_pgsql_cmd_return_value("SELECT mgm_id FROM device WHERE dev_id=$dev_id");
- } elsif (!is_empty($mgm_name)) {
- $mgm_id = exec_pgsql_cmd_return_value("SELECT mgm_id FROM management WHERE mgm_name='$mgm_name'");
- } elsif (!is_empty($dev_name)) {
- $mgm_id = exec_pgsql_cmd_return_value("SELECT mgm_id FROM device WHERE dev_name='$dev_name'");
- } else { # no info to derive mgm_id from has been passed to sub
- undef($mgm_id);
- }
- }
- return $mgm_id;
-}
-
-############################################################
-# is_import_running()
-# liefert FALSE, wenn kein import aktiv ist
-############################################################
-sub is_import_running {
- my $mgm_id = shift;
- return eval_boolean_sql("SELECT is_import_running($mgm_id)");
-}
-
-##############################
-# is_initial import(MGM-ID)
-# liefert TRUE, wenn noch kein import fuer MGM-ID gelaufen ist
-############################################################
-sub is_initial_import {
- my $mgm_id = shift;
- return (!defined(&exec_pgsql_cmd_return_value("SELECT control_id FROM import_control WHERE mgm_id=\'$mgm_id\' AND successful_import ")));
-}
-############################################################
-# get_matching_import_id()
-# relevanten Import raussuchen (datum und id)
-# = letzter Import fuer das Device vor $time
-# liefert die passende import ID zurueck
-# parameter1: device ID
-# parameter2: gesuchter Zeitpunkt
-############################################################
-sub get_matching_import_id {
- my $dev_id = $_[0];
- my $time = $_[1];
- my ($mgm_id, $dbh, $sth, $relevant_import_id, $err_str, $sqlcode);
-
- $sqlcode = "SELECT mgm_id FROM device WHERE dev_id=$dev_id";
- $dbh = DBI->connect("dbi:Pg:dbname=$CACTUS::FWORCH::fworch_database;host=$CACTUS::FWORCH::fworch_srv_host;port=$CACTUS::FWORCH::fworch_srv_port","$CACTUS::FWORCH::fworch_srv_user","$CACTUS::FWORCH::fworch_srv_pw");
- if ( !defined $dbh ) { die "Cannot connect to database!\n"; }
- $sth = $dbh->prepare( $sqlcode );
- if ( !defined $sth ) { die "Cannot prepare statement: $DBI::errstr\n"; }
- $sth->execute;
- $err_str = $sth->errstr;
- if (defined($err_str) && length($err_str)>0) { error_handler($err_str); }
- ($mgm_id) = $sth->fetchrow();
- $sth->finish;
- if (!defined($mgm_id)) {
- print("Management zum Device $dev_id nicht gefunden.");
- return -1;
- }
- #------
- $sqlcode = "SELECT control_id FROM import_control WHERE mgm_id=$mgm_id AND start_time<='" .
- $time . "' AND successful_import ORDER BY control_id desc LIMIT 1";
- $sth = $dbh->prepare( $sqlcode );
- if ( !defined $sth ) { die "Cannot prepare statement: $DBI::errstr\n"; }
- $sth->execute;
- $err_str = $sth->errstr;
- if (defined($err_str) && length($err_str)>0) { error_handler($err_str); }
- ($relevant_import_id) = $sth->fetchrow();
- $sth->finish;
- $dbh->disconnect;
- if (!defined($relevant_import_id)) {
- print("kein Import gefunden.");
- return -1;
- }
- return $relevant_import_id;
-}
-
-############################################################
-# insert_control_entry(is_initial_import, mgm_id)
-# erzeugt neuen eintrag in import_control und
-# liefert die control_id zurueck
-############################################################
-sub insert_control_entry {
- my $is_initial_import = shift;
- my $mgm_id = shift;
- my ($rc, $dbh, $sth);
- my $current_control_id;
- my $sql_str;
-
- $is_initial_import = (($is_initial_import)?'TRUE':'FALSE');
- $dbh = DBI->connect("dbi:Pg:dbname=$CACTUS::FWORCH::fworch_database;host=$CACTUS::FWORCH::fworch_srv_host;port=$CACTUS::FWORCH::fworch_srv_port",
- "$CACTUS::FWORCH::fworch_srv_user","$CACTUS::FWORCH::fworch_srv_pw");
- if ( !defined $dbh ) { die "Cannot connect to database!\n"; }
- $rc = $dbh->begin_work;
- $sql_str = "INSERT INTO import_control (is_initial_import,mgm_id) VALUES ($is_initial_import,$mgm_id)";
- # print ("\n\nSQL: $sql_str\n\n");
- $sth = $dbh->prepare( $sql_str );
- if ( !defined $sth ) { die "Cannot prepare statement: $DBI::errstr\n"; }
- $sth->execute;
- $sth = $dbh->prepare( "SELECT MAX(control_id) FROM import_control;");
- if ( !defined $sth ) { die "Cannot prepare statement: $DBI::errstr\n"; }
- $sth->execute;
- ($current_control_id) = $sth->fetchrow();
- $rc = $dbh->commit;
- $sth->finish;
- $dbh->disconnect;
- return $current_control_id;
-}
-
-############################################################
-# set_last_change_time($last_change_time_of_config,$current_import_id)
-# liefert die control_id zurueck
-############################################################
-sub set_last_change_time {
- my $last_change_time_of_config = shift;
- my $current_import_id = shift;
-
- if (defined($last_change_time_of_config)) {
- # print("\nlast change_time found: $last_change_time_of_config");
- my $sql_cmd1 =
- "UPDATE import_control SET last_change_in_config='$last_change_time_of_config' " .
- "WHERE control_id=$current_import_id";
- CACTUS::FWORCH::exec_pgsql_cmd_no_result($sql_cmd1);
- }
-}
-############################################################
-# remove_control_entry($current_import_id)
-# setzt Import-Eintrag auf NOT successful_import, wenn Fehler aufgetreten ist
-############################################################
-sub remove_control_entry {
- my $import_id = shift;
- my $sql_str = "UPDATE import_control SET successful_import=FALSE WHERE control_id=$import_id";
- return exec_pgsql_cmd_no_result($sql_str);
-}
-
-############################################################
-# get_import_infos_for_mgm($mgm_id, $fworch_workdir)
-# liefert vielfaeltige Infos zu einem zu importierenden Management zurueck
-############################################################
-sub get_import_infos_for_mgm {
- my $mgm_id = shift;
- my $fworch_workdir = shift;
- my $cfg_dir = shift;
- my ($dbh, $sth, $rc, $sth2);
- my ($err_str, %devices_with_rulebases, $mgm_name, $fields, $tables, $filter, $order, $sqlcode,
- $dev_typ_id,$ssh_hostname,$ssh_user,$ssh_private_key,$ssh_public_key,$hersteller);
- my ($template, $obj_file, $obj_file_base, $user_file, $user_file_base, $rule_file, $rule_file_base,
- $fworch_ctrl, $cmd_str, $is_netscreen, $ssh_port, $config_path_on_mgmt);
- my @result = ();
- my $fehler_cnt = 0;
- my $res = '';
- my $fehler = 0;
- my $res_array_ref;
- my $str_of_config_files = '';
- my $version;
-
- $sqlcode = "SELECT mgm_name,management.dev_typ_id,dev_typ_manufacturer,dev_typ_version,ssh_hostname, " .
- " username as ssh_user,secret, public_key as ssh_public_key,ssh_port,config_path FROM management " .
- " LEFT JOIN import_credential ON (management.import_credential_id=import_credential.id) LEFT JOIN stm_dev_typ USING (dev_typ_id) " .
- " WHERE stm_dev_typ.dev_typ_id=management.dev_typ_id AND mgm_id='$mgm_id'";
- $res_array_ref = exec_pgsql_cmd_return_array_ref($sqlcode, $fehler);
- if (!defined($fehler) || $fehler || !defined($res_array_ref)) {
- if (!defined($fehler)) {
- $fehler = "undefined error";
- }
- $fehler_cnt += 1;
- @result = (1, $fehler);
- return @result;
- }
- ($mgm_name,$dev_typ_id,$hersteller,$version,$ssh_hostname,$ssh_user,$ssh_private_key,$ssh_public_key,$ssh_port,$config_path_on_mgmt) = @$res_array_ref;
-
- $hersteller = lc(remove_space_at_end($hersteller));
- if ($hersteller =~ /netscreen/) { $is_netscreen = 1; $hersteller = 'netscreen'; }
- else { $is_netscreen = 0; }
-
- #if ($hersteller =~ /check\spoint\sr8x/) { $hersteller = 'checkpointR8x'; }
- print ("version: $version, manufacturer: $hersteller, ");
- if ($hersteller =~ /check\spoint/ && $version eq 'R8x') { $hersteller = 'checkpointR8x'; }
- elsif ($hersteller =~ /check/) { $hersteller = 'checkpoint'; }
- if ($hersteller =~ /phion/) { $hersteller = 'phion'; }
-
- my $csv_zone_file = "$fworch_workdir/" . $mgm_name . "_zones" . ".csv";
- my $csv_obj_file = "$fworch_workdir/" . $mgm_name . "_netzobjekte" . ".csv";
- my $csv_svc_file = "$fworch_workdir/" . $mgm_name . "_services" . ".csv";
- my $csv_usr_file = "$fworch_workdir/" . $mgm_name . "_users" . ".csv";
- my $csv_auditlog_file = "$fworch_workdir/" . $mgm_name . "_auditlog" . ".csv";
-
- if ($hersteller eq 'checkpoint') { # checkpoint
- $obj_file_base = "objects_5_0.C";
- $obj_file = $cfg_dir . '/' . $obj_file_base;
- $rule_file_base = "rulebases_5_0.fws";
- $rule_file = $cfg_dir . '/' . $rule_file_base;
- $user_file_base = "fwauth.NDB";
- $user_file = $cfg_dir . '/' . $user_file_base;
- $str_of_config_files = "${obj_file},${rule_file},${user_file}";
- } elsif ($hersteller eq 'checkpointr8x') { # checkpoint R80 ff.
- print ("DEBUG: found hersteller checkpointr8x");
- $obj_file_base = "nw_objects.json";
- $obj_file = $cfg_dir . '/' . $obj_file_base;
- $rule_file_base = "rules.json";
- $rule_file = $cfg_dir . '/' . $rule_file_base;
- # $user_file_base = "fwauth.NDB";
- # $user_file = $cfg_dir . '/' . $user_file_base;
- $str_of_config_files = "${obj_file},${rule_file}";
- } elsif ($hersteller eq 'phion') { # phion
- $obj_file_base = '';
- $obj_file = $cfg_dir . "/" . $obj_file_base;
- $rule_file_base = '';
- $rule_file = $obj_file;
- $user_file_base = $obj_file_base;
- $user_file = $obj_file;
- $str_of_config_files = $cfg_dir . '/' . "iso-phion-config.tgz";
- }
- else { # e.g. hersteller = juniper (JUNOS), cisco (ASA), netscreen (ScreenOS)
- $obj_file_base = $mgm_name . ".cfg";
- $obj_file = $cfg_dir . "/" . $obj_file_base;
- $rule_file_base = $obj_file_base;
- $rule_file = $obj_file;
- $user_file_base = $obj_file_base;
- $user_file = $obj_file;
- $str_of_config_files = $obj_file;
- }
- $rule_file = $cfg_dir . '/' . $rule_file_base;
- @result = (0, "", $mgm_name, $dev_typ_id,
- $obj_file_base,$obj_file,$user_file_base,$user_file,$rule_file_base,$rule_file,
- $csv_zone_file, $csv_obj_file, $csv_svc_file, $csv_usr_file, $csv_auditlog_file,
- $ssh_hostname,$ssh_user,$ssh_private_key,,$ssh_public_key,$hersteller,$is_netscreen,$str_of_config_files,$ssh_port,$config_path_on_mgmt);
- return @result;
-}
-
-############################################################
-# clean_up_fworch_db ($current_import_id)
-# fuehrt vacuum analyze fuer diverse Tabellen durch
-# loescht die Eintraege des aktuellen Imports aus den import_*-Tabellen
-############################################################
-sub clean_up_fworch_db {
- my $current_import_id = shift;
-
- # loeschen der Import-Tabellen und neuordnen
- exec_pgsql_cmd_no_result ("DELETE FROM import_object WHERE control_id=$current_import_id");
- exec_pgsql_cmd_no_result ("DELETE FROM import_service WHERE control_id=$current_import_id");
- exec_pgsql_cmd_no_result ("DELETE FROM import_user WHERE control_id=$current_import_id");
- exec_pgsql_cmd_no_result ("DELETE FROM import_rule WHERE control_id=$current_import_id");
- exec_pgsql_cmd_no_result ("DELETE FROM import_zone WHERE control_id=$current_import_id");
-}
-
-# produktunabhaengige Parse Subroutinen
-#-------------------------------------------------------------------------------------------
-
-#****************************
-# print_debug
-# param1 auszugebender String
-# return keiner
-# Funktion zum Druck von debugging Info, wenn der Schalter entsprechend gesetzt ist.
-#****************************
-
-sub print_debug {
- my $txt = shift;
- my $debug_level = shift;
- my $print_level = shift;
-
- if (!defined ($debug_level)) { $debug_level = 0; }
- if (!defined ($print_level)) { $print_level = 0; }
- if (&is_numeric($print_level) && &is_numeric($debug_level)) {
- if ($print_level < $debug_level) { print "Debug ($debug_level/$print_level): $txt.\n"; }
- } else {
- print "Debug ($debug_level/$print_level): $txt.\n";
- }
-}
-
-#****************************
-# print_verbose
-# param1 auszugebender String
-# return keiner
-# Funktion zum Druck geschwaetziger (verbose) Info, wenn der Schalter entsprechend gesetzt ist.
-#****************************
-sub print_verbose {
- print "Verbose: " if ($debug);
- print "$_[0]" if (($verbose)||($debug));
-}
-
-#****************************
-# d2u - dos 2 unix
-# param1 zu aendernder String
-# return \n fuer jedes \r\n in einem String
-# Funktion zum Wandeln eines DOS in einen UNIX String
-#****************************
-sub d2u {
- return ($_[0]=~s/\r\n/\n/g);
-}
-
-#****************************
-# calc_subnetmask_sub
-# param1 netzmaske in der Form xxx
-# return netzmaske als Bitzahl
-# Unterfunktion zur Berechnung von Teil-Subnetzmasken
-#****************************
-sub calc_subnetmask_sub {
- my $netmask_in = shift;
- my $count = 0;
- my $temp = 0;
-
- if (!defined($netmask_in)) {
- return undef;
- }
- for ( ;($temp < int ($netmask_in));$temp += (2 ** (7 - $count++))) { };
- return $count;
-}
-
-#****************************
-# calc_subnetmask
-# param1 netzmaske in der Form xxx.xxx.xxx.xxx
-# return netzmaske als Bitzahl
-# Funktion zur Berechnung von Subnetzmasken
-#****************************
-sub calc_subnetmask {
- my $netmask_in = shift;
- if (!defined($netmask_in)) {
- return undef;
- }
- $netmask_in =~ /([0-9]+).([0-9]+).([0-9]+).([0-9]+)/;
- my $temp1 = calc_subnetmask_sub($1);
- my $temp2 = calc_subnetmask_sub($2);
- my $temp3 = calc_subnetmask_sub($3);
- my $temp4 = calc_subnetmask_sub($4);
- my $temp0= $temp1 +$temp2 + $temp3 + $temp4;
- print_debug ("\t\t$temp0 = $1 - $temp1\t\t$2 - $temp2\t\t$3 - $temp3\t\t$4 - $temp4\n");
- return $temp0;
-}
-
-#****************************
-# convert_mask_to_dot_notation
-# param1 netzmaske in der Form /xx
-# return netzmaske in dot-Notation (e.g. 255.255.255.248)
-#****************************
-sub convert_mask_to_dot_notation {
- my $bits = shift;
-
- if ($bits =~ /(\d\d?)/) {
- $bits = $1/1;
- } else {
- $bits = 32;
- }
- $bits == 0 && do return "0.0.0.0";
- $bits == 1 && do return "128.0.0.0";
- $bits == 2 && do return "192.0.0.0";
- $bits == 3 && do return "224.0.0.0";
- $bits == 4 && do return "240.0.0.0";
- $bits == 5 && do return "248.0.0.0";
- $bits == 6 && do return "252.0.0.0";
- $bits == 7 && do return "254.0.0.0";
- $bits == 8 && do return "255.0.0.0";
- $bits == 9 && do return "255.128.0.0";
- $bits == 10 && do return "255.192.0.0";
- $bits == 11 && do return "255.224.0.0";
- $bits == 12 && do return "255.240.0.0";
- $bits == 13 && do return "255.248.0.0";
- $bits == 14 && do return "255.252.0.0";
- $bits == 15 && do return "255.254.0.0";
- $bits == 16 && do return "255.255.0.0";
- $bits == 17 && do return "255.255.128.0";
- $bits == 18 && do return "255.255.192.0";
- $bits == 19 && do return "255.255.224.0";
- $bits == 20 && do return "255.255.240.0";
- $bits == 21 && do return "255.255.248.0";
- $bits == 22 && do return "255.255.252.0";
- $bits == 23 && do return "255.255.254.0";
- $bits == 24 && do return "255.255.255.0";
- $bits == 25 && do return "255.255.255.128";
- $bits == 26 && do return "255.255.255.192";
- $bits == 27 && do return "255.255.255.224";
- $bits == 28 && do return "255.255.255.240";
- $bits == 29 && do return "255.255.255.248";
- $bits == 30 && do return "255.255.255.252";
- $bits == 31 && do return "255.255.255.254";
- $bits == 32 && do return "255.255.255.255";
-}
-
-#****************************
-# get_protocol_number
-# param1 string des ip-proto
-# return ip_proto_number
-# Funktion zur Umwandlung von Protonamen in Protonummern
-#****************************
-sub get_proto_number {
- my $proto_in = shift;
- my $proto_out = 'Fehler: proto nicht gefunden';
-
- if (is_numeric($proto_in)) {
- return $proto_in;
- } elsif ($proto_in eq 'tcp') {
- $proto_out = 6;
- } elsif ($proto_in eq 'udp') {
- $proto_out = 17;
- } elsif ($proto_in eq 'icmp') {
- $proto_out = 1;
- }
- if ($proto_out eq 'Fehler: proto nicht gefunden') {
- # print ("error: proto_in not found in import.pm::get_proto_number: $proto_in\n");
- undef ($proto_out);
- }
- return $proto_out;
-}
-
-#****************************
-# print_cell_no_delimiter Output in Dateien
-# return keiner
-# schreibt ein Feld der gefundenen Parserergebnisse in Datei, ohne abschliessenden Delimiter
-#****************************
-sub print_cell_no_delimiter {
- my $file = shift;
- my $cell = shift;
- $cell =~ s/$CACTUS::FWORCH::csv_delimiter/\\$CACTUS::FWORCH::csv_delimiter/g;
- if (defined($cell) && $cell ne '') {
- if ($cell =~ /^\".*?\"$/) { # Zelle schon von doppelten Anfuehrungszeichen eingerahmt
- print $file "$cell";
- } else {
- print $file "\"$cell\"";
- }
- }
- return;
-}
-
-#****************************
-# print_cell Output in Dateien
-# return keiner
-# schreibt ein Feld der gefundenen Parserergebnisse in Datei
-#****************************
-sub print_cell {
- my $file = shift;
- my $cell = shift;
- &print_cell_no_delimiter ($file, $cell);
- print_cell_delimiter($file);
- return;
-}
-
-sub print_cell_delimiter {
- my $file = shift;
- print $file $CACTUS::FWORCH::csv_delimiter;
- return;
-}
-
-
-############################
-# print_results_files_objects
-############################
-sub print_results_files_objects {
- my $out_dir = shift;
- my $mgm_name = shift;
- my $import_id = shift;
- my $out_file;
- my $header_text = "";
- my ($count,$local_schluessel,$schluessel);
-
- # Oeffnen der Ausgabedatei fuer Objekte
- #----------------------------------------
- # print ("beginn csv schreiben\n");
- $out_file = "$out_dir/${mgm_name}_netzobjekte.csv";
- my $fh = new FileHandle;
- if ($fh->open("> $out_file")) {
- foreach $schluessel (sort (@network_objects)) {
- # Ausnahmen fuer die Ausgabe abfragen
- if (!defined($network_objects{"$schluessel.type"})) {
- print ("\nerror key $schluessel, type not defined");
- } else {
- unless ($network_objects{"$schluessel.type"} eq 'sofaware_profiles_security_level'){
- # sollten auch die dynamischen Netzobjekte ausgeblendet werden, ist die folgende Zeile statt der vorhergehenden zu zu aktivieren
- # unless (($network_objects{"$schluessel.type"} eq 'sofaware_profiles_security_level') || ($network_objects{"$schluessel.type"} eq 'dynamic_net_obj')) {
- # Version fuer Nachbearbeitung im Import Modul
- # foreach $local_schluessel (qw ( name type members cp_ver comments color ipaddr ipaddr_last netmask sys location )) {
- # if (defined ( $network_objects{"$schluessel.$local_schluessel"} )) {
- # print FILEOUT_NETOBJ $network_objects{"$schluessel.$local_schluessel"};
- # }
- # Version mit integrierter Nachbearbeitung
- print_cell_no_delimiter($fh, $import_id);
- foreach (@obj_outlist) {
- $local_schluessel = $_;
- print_cell_delimiter($fh);
- # Ist der Wert definiert? > dann ausgeben
- if (defined ( $network_objects{"$schluessel.$local_schluessel"} )) {
- # Objekttypen fuer Ausgabe umsetzen ?
- if (($local_schluessel eq 'type') && (($network_objects{"$schluessel.$local_schluessel"} eq 'gateway_cluster') || ($network_objects{"$schluessel.$local_schluessel"} eq 'cluster_member'))) {
- print_cell_no_delimiter($fh, 'gateway');
- }
- elsif (($local_schluessel eq 'type') && (($network_objects{"$schluessel.$local_schluessel"} eq 'machines_range'
- || $network_objects{"$schluessel.$local_schluessel"} eq 'multicast_address_range'))) {
- print_cell_no_delimiter($fh, 'ip_range');
- }
- elsif (($local_schluessel eq 'type') &&
- (($network_objects{"$schluessel.$local_schluessel"} eq 'dynamic_net_obj' ||
- $network_objects{"$schluessel.$local_schluessel"} eq 'security_zone_obj' ||
- $network_objects{"$schluessel.$local_schluessel"} eq 'ips_sensor' ||
- $network_objects{"$schluessel.$local_schluessel"} eq 'voip_gk' ||
- $network_objects{"$schluessel.$local_schluessel"} eq 'voip_gw' ||
- $network_objects{"$schluessel.$local_schluessel"} eq 'voip_sip' ||
- $network_objects{"$schluessel.$local_schluessel"} eq 'voip_mgcp' ||
- $network_objects{"$schluessel.$local_schluessel"} eq 'voip_skinny'))) {
- print_cell_no_delimiter($fh, 'host');
- }
- elsif (($local_schluessel eq 'type') &&
- (($network_objects{"$schluessel.$local_schluessel"} eq 'group_with_exclusion' ||
- $network_objects{"$schluessel.$local_schluessel"} eq 'gsn_handover_group' ||
- $network_objects{"$schluessel.$local_schluessel"} eq 'domain'))) {
- print_cell_no_delimiter($fh, 'group');
- # da es jetzt eine Aufloesung der Ausnahme-Gruppen in normale Gruppen gibt, ist das jetzt OK
- }
- # IP-adresse?
- elsif (($local_schluessel eq 'ipaddr')||($local_schluessel eq 'ipaddr_last')) {
- # machine ranges immer mit 32 Bit Maske
- if ($network_objects{"$schluessel.type"} eq 'machines_range'){
- if ($network_objects{"$schluessel.$local_schluessel"} ne '') {
- # print_cell_no_delimiter($fh, $network_objects{"$schluessel.$local_schluessel"}."/32");
- print_cell_no_delimiter($fh, $network_objects{"$schluessel.$local_schluessel"});
- }
- } else { # sonst nur die IP-Adresse
- my $maske;
- if (defined($network_objects{"$schluessel.netmask"}) &&
- length($network_objects{"$schluessel.netmask"})>0 &&
- $local_schluessel eq 'ipaddr' &&
- $network_objects{"$schluessel.ipaddr"} !~ /\//) {
- # berechnen
- my $ip2 = CACTUS::FWORCH::remove_space_at_end(remove_quotes($network_objects{"$schluessel.$local_schluessel"}));
- $maske = $network_objects{"$schluessel.netmask"};
- if ($maske =~ /^\d+\.\d+\.\d+\.\d+$/) {
- $maske = calc_subnetmask($maske); # in bit-Notation umwandeln
- } # else: Maske enthaelt keine Punkte --> direkt als Integer uebernehmen
- if (length($ip2)>0) {
- print_cell_no_delimiter($fh, "$ip2/$maske");
- }
- } elsif ($network_objects{"$schluessel.ipaddr"} !~ /\//) {
- # oder fix auf 32 Bit stellen, wenn maske fehlt und Fehlerausgabe
- if (length($network_objects{"$schluessel.$local_schluessel"})>0) {
- if ($network_objects{"$schluessel.$local_schluessel"} ne '') {
- # print_cell_no_delimiter($fh, $network_objects{"$schluessel.$local_schluessel"}."/32");
- print_cell_no_delimiter($fh, $network_objects{"$schluessel.$local_schluessel"});
- }
- }
- } else {
- if ($network_objects{"$schluessel.$local_schluessel"} ne '') {
- print_cell_no_delimiter($fh, $network_objects{"$schluessel.$local_schluessel"});
- }
- }
- }
- }
- # default handling
- else {
- print_cell_no_delimiter($fh, $network_objects{"$schluessel.$local_schluessel"});
- }
- }
- }
- # Datensatztrennzeichen ausgeben
- print $fh "\n";
- }
- }
- }
- $fh->close; # Schliessen der Ausgabedatei fuer NW-Objekte
- } else {
- die "NETOBJ: $out_file konnte nicht geoeffnet werden.\n";
- }
-
- # Oeffnen der Ausgabedatei fuer Services
- #----------------------------------------
- $out_file = "$out_dir/${mgm_name}_services.csv";
- $fh = new FileHandle;
- if ($fh->open("> $out_file")) {
- # Ausgabe der Datensaetze
- foreach $schluessel (sort (@services)) {
- print_cell_no_delimiter ($fh, "$import_id");
- foreach (@srv_outlist) {
- $local_schluessel = $_;
- print_cell_delimiter($fh);
- $local_schluessel = $_;
- # Ist der Wert definiert? > dann ausgeben
- if (defined ( $services{"$schluessel.$local_schluessel"} )) {
- # Serviceport fuer rpc umsetzen ?
- if (defined ($services{"$schluessel.typ"})) {
- unless (($local_schluessel eq 'port') && ($services{"$schluessel.typ"} eq 'rpc')) {
- print_cell_no_delimiter($fh, $services{"$schluessel.$local_schluessel"});
- }
- } else {
- print_cell_no_delimiter($fh, $services{"$schluessel.$local_schluessel"});
- }
- } elsif ($local_schluessel eq 'rpc_port') {
- # Serviceport fuer rpc umsetzten ?
- if (defined ($services{"$schluessel.typ"})) {
- if ($services{"$schluessel.typ"} eq 'rpc') {
- print_cell_no_delimiter($fh, $services{"$schluessel.port"});
- }
- }
- }
- }
- # Datensatztrennzeichen ausgeben
- print $fh "\n";
- }
- $fh->close; # Schliessen der Ausgabedatei fuer Services
- } else {
- die "SVC_OUT: $out_file konnte nicht geoeffnet werden.\n";
- }
-}
-
-#****************************
-# print_results_files_rules Output in Dateien
-# return keiner
-# gibt die gefundenen Parserergebnisse in Dateien aus
-#****************************
-sub print_results_files_rules {
- my $out_dir = shift;
- my $mgm_name = shift;
- my $import_id = shift;
- my $is_first_in_loop;
- my $out_file;
- my $header_text = "";
- my ($count,$local_schluessel,$schluessel,$flat_file);
-
- foreach $schluessel (sort (@rulebases)) {
- # Oeffnen der Ausgabedatei fuer Rulebases
- #----------------------------------------
- # Regeln definiert? > Datei Oeffnen und schreiben
- if ( !defined($rulebases{"$schluessel.rulecount"}) ) {
- print ("\nwarning in import.pm: empty ruleset rulebases($schluessel.rulecount) not defined");
- } elsif ( $rulebases{"$schluessel.rulecount"} > 0 ) {
- if (defined($rulebases{"$schluessel.ruleorder"})) {
- @ruleorder = split (/,/, $rulebases{"$schluessel.ruleorder"});
- } else { # ruleorder ist aufsteigend von 0-n
- @ruleorder = ();
- for (my $i=0; $i<$rulebases{"$schluessel.rulecount"}; ++$i) {
- push @ruleorder, ($i);
- }
- }
-
- # Dateinamen festlegen
- if (!defined($out_dir)) { $out_dir = "."; }
- $flat_file = $schluessel; $flat_file =~ s/\//\_/g;
- $out_file = "$out_dir/${flat_file}_rulebase.csv";
- my $fh = new FileHandle;
- if ($fh->open("> $out_file")) {
- # Ausgabe der Datensaetze
- for ($count = 0; $count < $rulebases{"$schluessel.rulecount"}; $count++) {
- # Regelnummer ausgeben
- print_cell($fh, $import_id); # import_id
- print_cell ($fh, $count); # regel_nummer
- print_cell_no_delimiter ($fh, $schluessel); # rulebase_name
- foreach (@rule_outlist) {
- $local_schluessel = $_;
- print_cell_delimiter($fh);
- # Ist der Wert definiert? > dann ausgeben
- if ( defined ($rulebases{"$schluessel.$ruleorder[$count].$local_schluessel"})) {
- print_cell_no_delimiter($fh, $rulebases{"$schluessel.$ruleorder[$count].$local_schluessel"});
- } else {
- if ($local_schluessel eq 'track') {
- print_cell_no_delimiter($fh, 'none');
- }
- }
- }
- print $fh "\n"; # Datensatztrennzeichen ausgeben
- }
- $fh->close; # Schliessen der Ausgabedatei fuer Rules
- } else {
- die "RULE_OUT: $out_file konnte nicht geoeffnet werden.\n";
- }
- print_verbose ("Output Datei: $out_file wurde geschlossen.\n\n");
- # keine Regeln, keine Regeldatei!
- } else {
- print_verbose ("$schluessel hat keine Regeln.\n");
- print_verbose ("Datei $out_file wurde nicht ausgegeben.\n\n");
- }
- }
-}
-
-#****************************
-# print_results_files_zones Output in Dateien
-# return keiner
-# gibt die gefundenen Parserergebnisse in Dateien aus
-#****************************
-sub print_results_files_zones {
- my $out_dir = shift;
- my $mgm_name = shift;
- my $import_id = shift;
- my ($out_file,$schluessel);
-
- if (!defined($out_dir)) { $out_dir = "."; }
- $out_file = "$out_dir/${mgm_name}_zones.csv";
- my $fh = new FileHandle;
- if ($fh->open("> $out_file")) {
- print_verbose ("Output Datei: $out_file wurde geoeffnet.\n");
- foreach $schluessel (sort (@zones)) {
- print_cell_no_delimiter($fh, $import_id); # import id
- print_cell_delimiter($fh);
- print_cell_no_delimiter($fh, $schluessel); # zone name
- print $fh "\n";
- }
- $fh->close; # Schliessen der Ausgabedatei fuer Zonen
- } else {
- die "ZONE-OUT: $out_file konnte nicht geoeffnet werden.\n";
- }
-}
-
-#****************************
-# print_results_files_audit_log Output in Dateien
-# return keiner
-# gibt die gefundenen Parserergebnisse in Dateien aus
-#****************************
-sub print_results_files_audit_log {
- my $out_dir = shift;
- my $mgm_name = shift;
- my $import_id = shift;
- my ($out_file,$schluessel);
-
- if (!defined($out_dir)) { $out_dir = "."; }
- $out_file = "$out_dir/${mgm_name}_auditlog.csv";
- my $fh = new FileHandle;
- if ($fh->open("> $out_file")) {
- for (my $idx = 0; $idx<$audit_log_count; $idx ++) {
- print_cell ($fh, $import_id);
- print_cell_no_delimiter ($fh, $idx);
- foreach my $schluessel (@auditlog_outlist) {
- print_cell_delimiter($fh);
- if ( defined ($auditlog{"$idx.$schluessel"})) { # Ist der Wert definiert? > dann ausgeben
- print_cell_no_delimiter($fh, $auditlog{"$idx.$schluessel"});
- }
- }
- print $fh "\n"; # EOL
- $fh->close; # Schliessen der Ausgabedatei fuer Auditlog
- }
- } else {
- die "AUDITLOG-OUT: $out_file konnte nicht geoeffnet werden.\n";
- }
-}
-#****************************
-# print_results_files_users Output in Dateien
-# return keiner
-# gibt die gefundenen Parserergebnisse in Dateien aus
-#****************************
-sub print_results_files_users {
- my $out_dir = shift;
- my $mgm_name = shift;
- my $import_id = shift;
- my $s = '';
- my $is_first_in_loop;
- my $schluessel;
- my $out_file;
-
- if (!defined($out_dir)) { $out_dir = "."; }
- $out_file = "$out_dir/${mgm_name}_users.csv";
- my $fh = new FileHandle;
- if ($fh->open("> $out_file")) {
- foreach my $name (sort @user_ar) {
- print_cell ($fh, $import_id);
- print_cell_no_delimiter($fh, $name); # user name
- foreach my $schluessel (@user_outlist) {
- print_cell_delimiter($fh);
- if ( defined ($user{"$name.$schluessel"})) { # Ist der Wert definiert? > dann ausgeben
- print_cell_no_delimiter($fh, $user{"$name.$schluessel"});
- }
- }
- print $fh "\n"; # EOL
- }
- } else {
- die "USER_OUT: $out_file konnte nicht geoeffnet werden.\n";
- }
-}
-
-#****************************
-# print_results_monitor Testoutput auf den Monitor
-# return keiner
-# druckt die gefundenen Parserergebnisse ohne weitere Aufbereitung oder Filterung der Informationen
-#****************************
-sub print_results_monitor {
- my $mode = $_[0];
- my ($schluessel, $local_schluessel, $count);
-
- # if steuert, ob jeweiliges printmodul aktiv => 0 unterdruecken
- # => 1 ausgeben
- SWITCH_parsemode: {
- # nur im Modus "Objekte"
- if ($mode eq 'objects') {
- if ( 1 ) {
- print "\n\nnetwork_objects:\n";
- foreach $schluessel (sort (keys( %network_objects))) {
- if (!defined($network_objects{$schluessel})) {
- print ("\nerror print_results_monitor nwobj $schluessel not defined");
- } else {
- print "$schluessel\t\t\t $network_objects{$schluessel} \n";
- }
- }
-
- }
- if ( 1 ) {
- print "\n\nservices:\n";
- foreach $schluessel (sort (keys( %services ))) {
- print "$schluessel\t\t\t $services{$schluessel} \n";
- }
- }
- if ( 0 ) {
- print "\n\nnetwork_objects:\n";
- foreach $schluessel (sort (@network_objects)) {
- print "\t$schluessel\n";
- foreach $local_schluessel ( @obj_outlist ) {
- print "\t\t$local_schluessel\t";
- if (defined ( $network_objects{"$schluessel.$local_schluessel"} )) {
- print $network_objects{"$schluessel.$local_schluessel"}."\n";
- } else {
- print "\n";
- }
- }
- }
- }
- if ( 0 ) {
- print "\n\nservices:\n";
- foreach $schluessel (sort (@services)) {
- print "\t$schluessel\n";
- foreach $local_schluessel ( @srv_outlist ) {
- print "\t\t$local_schluessel\t";
- if (defined ( $services{"$schluessel.$local_schluessel"} )) {
- print $services{"$schluessel.$local_schluessel"}."\n";
- } else {
- print "\n";
- }
- }
- }
- }
- last SWITCH_parsemode;
- }
- #nur im Modus "Regeln"
-
- if ($mode eq 'rules') {
- if ( 1 ) {
- my $rule_no;
- print "\n\nrulebases:\n";
- foreach $schluessel (sort (@rulebases)) {
- if (defined($rulebases{"$schluessel.rulecount"} )) {
- $rule_no = $rulebases{"$schluessel.rulecount"};
- } else {
- $rule_no = 0;
- }
- print "\t(#regeln: " . $rule_no . ")\t$schluessel\n";
- }
- }
- if ( 0 ) {
- foreach $schluessel (sort (@rulebases)) {
- print "\n\nruleset:\t$schluessel\n";
- foreach $local_schluessel (sort (keys(%rulebases))) {
- print $local_schluessel."\t".$rulebases{"$local_schluessel"}."\n";
- }
- }
- }
- if ( 1 ) {
- print "\n\nrules:\n";
- foreach $schluessel (sort (@rulebases)) {
- print "Regeln des Regelwerks $schluessel:\n";
- if (defined($rulebases{"$schluessel.rulecount"})) {
- @ruleorder = split (/,/, $rulebases{"$schluessel.ruleorder"});
- for ($count = 0; $count < $rulebases{"$schluessel.rulecount"}; $count++) {
- foreach $local_schluessel ( @rule_outlist ) {
- print "$schluessel\t$count\t$local_schluessel\t";
- if (defined ($rulebases{"$schluessel.$ruleorder[$count].$local_schluessel"})){
- print $rulebases{"$schluessel.$ruleorder[$count].$local_schluessel"};
- }
- print "\n";
- }
- }
- print ("rulecount: " . $rulebases{"$schluessel.rulecount"} . "\n");
- if (defined($rulebases{"$schluessel.ruleorder"})) {
- print ("ruleorder: " . $rulebases{"$schluessel.ruleorder"} . "\n");
- }
- }
- print "\n";
- }
- }
- last SWITCH_parsemode;
- }
- }
-}
-
-############################################################
-# fill_import_tables_from_csv ($sqldatafile, $fehler_count, $dev_typ_id,
-# $csv_obj_file, $csv_svc_file, $csv_usr_file, $csv_rule_file)
-# fuellt die import_tabellen
-############################################################
-sub fill_import_tables_from_csv {
- my $fehler_count = 0;
- my $dev_typ_id = shift;
- my $csv_zone_file = shift;
- my $csv_obj_file = shift;
- my $csv_svc_file = shift;
- my $csv_usr_file = shift;
- my $rulebases = shift; # ref to hash of rulebase-infos
- my $fworch_workdir = shift;
- my $csv_audit_log_file = shift;
- my ($csv_rule_file,$fehler, $fields, $sqlcode, $psql_cmd, $start_time);
-
- $start_time = time();
- if (file_exists($csv_zone_file)) { # optional (netscreen, fortigate)
- $fields = "(" . join(',',@zone_import_fields) . ")";
- $sqlcode = "COPY import_zone $fields FROM STDIN DELIMITER '$CACTUS::FWORCH::csv_delimiter' CSV";
- $fehler = CACTUS::FWORCH::copy_file_to_db($sqlcode,$csv_zone_file);
- }
- if (defined($csv_audit_log_file) && file_exists($csv_audit_log_file)) { # optional if audit log exists
- $fields = "(" . join(',',@auditlog_import_fields) . ")";
- $sqlcode = "COPY import_changelog $fields FROM STDIN DELIMITER '$CACTUS::FWORCH::csv_delimiter' CSV";
- $fehler = CACTUS::FWORCH::copy_file_to_db($sqlcode,$csv_audit_log_file);
- }
- $fields = "(" . join(',',@obj_import_fields) . ")";
- $sqlcode = "COPY import_object $fields FROM STDIN DELIMITER '$CACTUS::FWORCH::csv_delimiter' CSV";
- if ($fehler = CACTUS::FWORCH::copy_file_to_db($sqlcode,$csv_obj_file)) {
- print_error("dbimport: $fehler"); print_linebreak(); $fehler_count += 1;
- }
- $fields = "(" . join(',',@svc_import_fields) . ")";
- $sqlcode = "copy import_service $fields FROM STDIN DELIMITER '$CACTUS::FWORCH::csv_delimiter' CSV";
- if ($fehler = CACTUS::FWORCH::copy_file_to_db($sqlcode,$csv_svc_file)) {
- print_error("dbimport: $fehler"); print_linebreak(); $fehler_count += 1;
- }
- if (file_exists($csv_usr_file)) {
- $fields = "(" . join(',',@user_import_fields) . ")";
- $sqlcode = "COPY import_user $fields FROM STDIN DELIMITER '$CACTUS::FWORCH::csv_delimiter' CSV";
- if ($fehler = CACTUS::FWORCH::copy_file_to_db($sqlcode,$csv_usr_file)) { }
- }
- $fields = "(" . join(',',@rule_import_fields) . ")";
- my @rulebase_ar = ();
- foreach my $d (keys %{$rulebases}) {
- my $rb = $rulebases->{$d}->{'local_rulebase_name'};
- my $rulebase_name_sanitized = join('__', split /\//, $rb);
- if ( !grep( /^$rulebase_name_sanitized$/, @rulebase_ar ) ) {
- @rulebase_ar = (@rulebase_ar, $rulebase_name_sanitized);
- # print ("rulebase_name_sanitized: $rulebase_name_sanitized\n");
- $csv_rule_file = $fworch_workdir . '/' . $rulebase_name_sanitized . '_rulebase.csv';
- print ("rulebase found: $rulebase_name_sanitized, rule_file: $csv_rule_file, device: $d\n");
- $sqlcode = "COPY import_rule $fields FROM STDIN DELIMITER '$CACTUS::FWORCH::csv_delimiter' CSV";
- if ($fehler = CACTUS::FWORCH::copy_file_to_db($sqlcode,$csv_rule_file)) {
- print_error("dbimport: $fehler"); print_linebreak(); $fehler_count += 1;
- }
- } else {
- print ("ignoring another device ($d) with rulebase $rb\n");
- }
- } print_bold("Database Import ... done in " . sprintf("%.2f",(time() - $start_time)) . " seconds"); print_linebreak();
- return $fehler_count;
-}
-
-############################################################
-# fill_import_tables_from_csv_with_sql ($dev_typ_id,
-# $sql_obj_file, $sql_svc_file, $sql_usr_file, $sql_rule_file)
-# fuellt die import_tabellen mit einzelen sql-befehlen
-# nur zum Debuggen verwendet, wenn der CSV-Import schiefgeht
-############################################################
-sub fill_import_tables_from_csv_with_sql {
- my $fehler_count = 0;
- my $dev_typ_id = shift;
- my $csv_zone_file = shift;
- my $csv_obj_file = shift;
- my $csv_svc_file = shift;
- my $csv_usr_file = shift;
- my $rulebases = shift; # ref to hash of rulebase-infos
- my $fworch_workdir = shift;
- my $csv_audit_log_file = shift;
- my ($csv_rule_file,$fehler, $fields, $sqlcode, $psql_cmd, $start_time);
-
- sub build_and_exec_sql_statements { # baut aus csv-Files sql statements und fuehrt diese einzeln aus
- my $target_table = shift;
- my $str_of_fields = shift;
- my $csv_file = shift;
- my $input_line;
- my $sqlcode;
- my @values = ();
- my $fehler;
- my $fehler_count=0;
- my $delimiter;
-
- sub str_to_sql_value {
- my $input_str = remove_quotes(shift);
-
- if ($input_str eq '') {
- return 'NULL';
- } else {
- $input_str =~ s/\'/\\\'/g;
- $input_str =~ s/^"//;
- $input_str =~ s/"$//;
- return "'$input_str'";
- }
- }
-
- my $CSVhdl = new IO::File ("< $csv_file") or $fehler = "Cannot open file $csv_file for reading: $!";
- if ($fehler) {
- print_error("FEHLER: $fehler");
- print_linebreak();
- $fehler_count += 1;
- return $fehler_count;
- }
- $delimiter = qr/$CACTUS::FWORCH::csv_delimiter/;
-
- while ($input_line = <$CSVhdl>) {
- $sqlcode = "INSERT INTO $target_table ($str_of_fields) VALUES (";
- $input_line =~ s/\n$//;
-
- $input_line =~ s/\\$delimiter/AbDdia679roe4711/g;
- @values = split (/$delimiter/, $input_line);
- my @values2 = @values;
- @values = ();
- foreach my $val (@values2) {
- $val =~ s/AbDdia679roe4711/$delimiter/g;
- @values = (@values, $val);
- }
- while ($input_line =~ /$delimiter$/) { # line ends with ; --> add NULL-value for last column
- push @values, "";
- $input_line =~ s/$delimiter$//;
- }
- $sqlcode .= str_to_sql_value($values[0]);
- my $i = 1;
- while ($i<=$#values) {
- $sqlcode .= "," . str_to_sql_value($values[$i++]);
- }
- $sqlcode .= ");";
- # print ("sql_code: $sqlcode\n");
- if ($fehler = CACTUS::FWORCH::exec_pgsql_cmd_no_result($sqlcode)) {
- output_txt($fehler . "; $csv_file",3);
- $fehler_count += 1;
- }
- }
- $CSVhdl->close;
- return $fehler_count;
- }
-
- print_bold(" - Database Import (non-bulk)");
- print_linebreak(); print_txt("- writing data into import tables ... ");
- $start_time = time();
-
- if (file_exists($csv_zone_file)) { # optional nur fuer Netscreen
- $fehler = build_and_exec_sql_statements('import_zone', join(',',@zone_import_fields), $csv_zone_file);
- if ($fehler) { $fehler_count ++; }
- }
- if (defined($csv_audit_log_file) && file_exists($csv_audit_log_file)) { # optional nur wenn auditlog existiert
- $fehler = build_and_exec_sql_statements('import_changelog', join(',',@auditlog_import_fields), $csv_audit_log_file);
- if ($fehler) { $fehler_count ++; }
- }
- $fields = join(',', @obj_import_fields);
- $fehler = build_and_exec_sql_statements('import_object',$fields, $csv_obj_file);
- if ($fehler) { $fehler_count ++; }
-
- $fields = join(',',@svc_import_fields);
- $fehler = build_and_exec_sql_statements('import_service',$fields, $csv_svc_file);
- if ($fehler) { $fehler_count ++; }
-
- if (file_exists($csv_usr_file)) {
- $fields = join(',',@user_import_fields);
- $fehler = build_and_exec_sql_statements('import_user',$fields, $csv_usr_file);
- }
- $fields = join(',',@rule_import_fields);
- foreach my $d (keys %{$rulebases}) {
- my $rb = $rulebases->{$d}->{'local_rulebase_name'};
- $csv_rule_file = $fworch_workdir . '/' . $rb . '_rulebase.csv';
- $fehler = build_and_exec_sql_statements('import_rule',$fields, $csv_rule_file);
- if ($fehler) { $fehler_count ++; }
- }
- print_bold ("done in " . sprintf("%.2f",(time() - $start_time)) . " seconds"); print_linebreak();
- return $fehler_count;
-}
-
-#####################################################################################
-
-1;
-__END__
-
-=head1 NAME
-
-FWORCH::import - Perl extension for fworch Import
-
-=head1 SYNOPSIS
-
- use CACTUS::FWORCH::import;
-
-=head1 DESCRIPTION
-
-fworch Perl Module support for importing configs into fworch Database
-
-=head2 EXPORT
-
- global variables
-
- DB functions
-
-=head1 SEE ALSO
-
- behind the door
-
-
-=head1 AUTHOR
-
- Cactus eSecurity, tmp@cactus.de
-
-=cut
diff --git a/roles/importer/files/importer/CACTUS/FWORCH/import/checkpoint.pm b/roles/importer/files/importer/CACTUS/FWORCH/import/checkpoint.pm
deleted file mode 100644
index a3ff30d049..0000000000
--- a/roles/importer/files/importer/CACTUS/FWORCH/import/checkpoint.pm
+++ /dev/null
@@ -1,2438 +0,0 @@
-package CACTUS::FWORCH::import::parser;
-
-use strict;
-use warnings;
-use IO::File;
-use Getopt::Long;
-use File::Basename;
-use Time::HiRes qw(time); # fuer hundertstelsekundengenaue Messung der Ausfuehrdauer
-use Net::CIDR;
-use CACTUS::FWORCH;
-use CACTUS::FWORCH::import;
-use Date::Calc qw(Add_Delta_DHMS);
-
-require Exporter;
-our @ISA = qw(Exporter);
-
-our %EXPORT_TAGS = ( 'basic' => [ qw( ©_config_from_mgm_to_iso &parse_config ) ] );
-
-our @EXPORT = ( @{ $EXPORT_TAGS{'basic'} } );
-our $VERSION = '0.3';
-
-# variblendefinition check point parser - global
-# -------------------------------------------------------------------------------------------
-my $GROUPSEP = $CACTUS::FWORCH::group_delimiter;
-
-my $UID = "UID"; # globale konstante UID
-
-# Stati und anderes des Objektparsers
-my $ln = 0;
-my $line = '';
-our $parse_obj_state = 0; # moegliche Werte 0 kein status
- # 1 objectclass gestartet
- # 2 object gestartet
- # 3...7 diverse attribute & werte (kontextsensitiv)
- # >10 innerhalb einer Gruppe
-our $parse_obj_type; # State 1 - aktuelle Objektklasse
-our $parse_obj_name; # State 2 - name des aktuellen objektes
-our $old_parse_obj_name; # State 2 - name des letzten objektes
-our $parse_obj_attr; # State 3 - Attribut
-our $parse_obj_attr_value; # State 3 - Wert des Attributes
-our $parse_obj_attr_ext; # State 4 - Attributerweiterung (kontextsensitiv)
-our $parse_obj_attr_ext_value
- ; # State 4 - Wert des erweiterten Attributes (kontextsensitiv)
-our $parse_obj_attr_ext2; # State 5 - Attributerweiterung (kontextsensitiv)
-our $parse_obj_attr_ext2_value
- ; # State 5 - Wert des erweiterten Attributes (kontextsensitiv)
-our $parse_obj_attr_ext3; # State 6 - Attributerweiterung (kontextsensitiv)
-our $parse_obj_attr_ext3_value
- ; # State 6 - Wert des erweiterten Attributes (kontextsensitiv)
-our $parse_obj_attr_ext4; # State 7 - Attributerweiterung (kontextsensitiv)
-our $parse_obj_attr_ext4_value
- ; # State 7 - Wert des erweiterten Attributes (kontextsensitiv)
-our $parse_obj_groupmember; # string-array fuer gruppenmitglieder
-our $parse_obj_groupmember_refs; # string-array fuer gruppenmitglieder
-our $group_with_exclusion_marker; # gibt an, ob die aktuelle gruppe eine normale (undefined),
- # positiv- (base), oder negativ-gruppe (exclusion) ist
-
-# Stati und anderes des Rulesparser
-our $parserule_state = 0; # status des Regelparsers
- # 0 kein Regelwerk
- # 1 Regelwerk
- # 2 Regel
- # 14 Gruppe
- # 15 Mitgliederliste der Gruppe
- # 16 Mitglied einer Compound Gruppe
-our $parserule_in_rule_base = 0; # wenn innerhalb eines Regelwerkes
-our $parserule_in_rule = 0; # wenn innerhalb einer Regeldefinition
-our $parserule_rulenum = -1; # aktuelle Regelnummer
-our $parserule_rulebasename; # aktuellen Regelwerkes
-our $parserule_ruleparameter; # Bezeichnung des aktuellen Parameter
-our $parserule_groupmember; # Regelgruppenmitglieder
-our $parserule_groupmember_refs; # String mit Uids
-our $parserule_ruleparameter_ext; # Attributerweiterung (kontextsensitiv)
-our $parserule_ruleparameter_ext_value
- ; # Wert des erweiterten Attributes (kontextsensitiv)
-our $parserule_ruleuser
- ; # user, fuer die die Regel gilt (alle user aus der Quellspalte)
-our %usergroup;
-
-#####################################################################################
-# Start Check Point Parser
-#####################################################################################
-
-sub parse_config {
- my $object_file = shift;
- my $rulebase_file = shift;
- my $user_db_file = shift;
- my $rulebase_name = shift;
- my $output_dir = shift;
- my $verbose = shift;
- my $mgm_name = shift;
- my $config_dir = shift;
- my $import_id = shift;
- my $audit_log_file= shift;
- my $prev_import_time= shift;
- my $parse_full_audit_log = shift;
- my $debug_level = shift;
- my $result;
-
- if ($result = cp_parse_main( $object_file, $output_dir, '', $verbose )) { return $result; }
-
- undef($parse_obj_state);
- undef($parse_obj_type);
- undef($old_parse_obj_name);
- undef($parse_obj_attr);
- undef($parse_obj_attr_value);
- undef($parse_obj_attr_ext);
- undef($parse_obj_attr_ext_value);
- undef($parse_obj_attr_ext2);
- undef($parse_obj_attr_ext2_value);
- undef($parse_obj_groupmember);
- undef($parse_obj_groupmember_refs);
-
- if ($result = cp_parse_main( $rulebase_file, $output_dir, $rulebase_name, $verbose )) { return $result; }
-
- if ($result = &cp_parse_users_main($user_db_file, $output_dir, $verbose)) { return $result; }
- if ($result = &cp_parse_users_from_rulebase($rulebase_file)) { return $result; }
-
- if ( -e $audit_log_file ) { # if audit.log exists, process it
- &parse_audit_log($audit_log_file, $prev_import_time, $parse_full_audit_log);
- }
-
- &print_results_files_objects ( $output_dir, $mgm_name, $import_id );
- &print_results_files_rules ( $output_dir, $mgm_name, $import_id );
- &print_results_files_users ( $output_dir, $mgm_name, $import_id );
- &print_results_files_audit_log ( $output_dir, $mgm_name, $import_id );
- return 0; # done without errors
-}
-
-############################################################
-# gen_uid (geparste uid)
-# wandelt geparste uid in fworch-kompatible UID um
-# derzeit ist lediglich ein upercase notwendig
-############################################################
-sub gen_uid {
- return ( uc(shift) );
-}
-
-sub add_delta_db_time {
- my $db_time = shift;
- my $diff_in_seconds = shift;
-
- my ($year, $month, $day, $hour, $min, $sec) = $db_time =~ m/(\d+)\-(\d+)\-(\d+)\s+(\d+)\:(\d+)\:(\d+)/;
- ($year, $month, $day, $hour, $min, $sec) = Add_Delta_DHMS( $year, $month, $day, $hour, $min, $sec, 0, 0, 0, $diff_in_seconds );
- return sprintf("%04d-%02d-%02d %02d:%02d:%02d", $year, $month, $day, $hour, $min, $sec);
-}
-
-############################################################
-# convert_checkpoint_to_db_date (string)
-# konvertiert von Format 'Sun Jul 21 12:24:13 2002'
-# oder '21Jul2007 12:24:13'
-# oder '21-jul-2007'
-# zu Format '2002-07-21 12:24:13'
-############################################################
-sub convert_checkpoint_to_db_date {
- my $month = 0;
- my $sec = 0;
- my ($result, $day, $year, $hour, $min);
-
-# '[Sun ]Jul 21 12:24:13 2007'
- if ( $_[0] =~ /^\w+\s+(\w+)\s+(\d+)\s+(\d+)\:(\d+)\:?(\d+)?\s+(\d+)$/ ) {
- $month = $1;
- $day = $2;
- $hour = $3;
- $min = $4;
- if (defined($5)) { $sec = $5; }
- $year = $6;
- }
-# '21Jul2007 12:24:13'
- if ( $_[0] =~ /^(\d+)\s?([a-zA-Z]{3})(\d{4})\s+(\d+)\:(\d+)\:?(\d+)?$/ ) {
- $month = $2;
- $day = $1;
- $hour = $4;
- $min = $5;
- if (defined($6)) { $sec = $6; }
- $year = $3;
- }
-
-# '21-jul-2007'
- if ( $_[0] =~ /^(\d+)\-([a-zA-Z]{3})\-(\d{4})$/ ) {
- $month = $2;
- $day = $1;
- $year = $3;
- }
- SWITCH: {
- if ($month =~ /jan/i) { $month = "01"; last SWITCH; }
- if ($month =~ /feb/i) { $month = "02"; last SWITCH; }
- if ($month =~ /mar/i) { $month = "03"; last SWITCH; }
- if ($month =~ /apr/i) { $month = "04"; last SWITCH; }
- if ($month =~ /may/i) { $month = "05"; last SWITCH; }
- if ($month =~ /jun/i) { $month = "06"; last SWITCH; }
- if ($month =~ /jul/i) { $month = "07"; last SWITCH; }
- if ($month =~ /aug/i) { $month = "08"; last SWITCH; }
- if ($month =~ /sep/i) { $month = "09"; last SWITCH; }
- if ($month =~ /oct/i) { $month = "10"; last SWITCH; }
- if ($month =~ /nov/i) { $month = "11"; last SWITCH; }
- if ($month =~ /dec/i) { $month = "12"; last SWITCH; }
- $month = 0;
- }
- if ( $month ne 0 ) {
- if (defined ($year) && defined($month) && defined($day) && defined($hour) && defined($min) ) {
- $result = sprintf("%04d-%02d-%02d %02d:%02d:%02d", $year, $month, $day, $hour, $min, $sec);
- } elsif (defined ($year) && defined($month) && defined($day) ) {
- $result = sprintf("%04d-%02d-%02d", $year, $month, $day);
- } else {
- print ("WARNING: convert_cp_to_db_date: " . $_[0] . " undefined value: $year-$month-$day $hour:$min:$sec\n");
- }
- }
- return $result;
-}
-
-############################################################
-# convert_db_to_checkpoint_date (string)
-# konvertiert von Format '2002-07-21 12:24:13'
-# zu Format 'Jul 21 12:24:13 2002'
-############################################################
-sub convert_db_to_checkpoint_date {
- my $result;
- my $month;
- my $day;
- my $year;
- my $rest;
- my $time = ' ';
-
- if ( $_[0] =~ /^(\d+)\-(\d+)\-(\d+)(.*)$/ ) {
- $month = $2;
- $day = $3;
- $year = $1;
- $rest = $4;
- } else {
- return '';
- }
- my @months = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
- $month = $months[$month-1];
-
- if ( $rest =~ /(\d+)\:(\d+)\:?(\d+)?/ ) {
- $time = " $1:$2";
- if (defined($3)) {
- $time .= ":$3";
- }
- $time .= ' ';
- }
- return "$month $day$time$year";
-}
-
-############################################################
-# copy_config_from_mgm_to_iso($ssh_private_key, $ssh_user, $ssh_hostname, $management_name, $obj_file_base, $cfg_dir, $rule_file_base)
-# Kopieren der Config-Daten vom Management-System zum ITSecorg-Server
-############################################################
-sub copy_config_from_mgm_to_iso {
- my $ssh_user = shift;
- my $ssh_hostname = shift;
- my $management_name = shift; # not used
- my $obj_file_base = shift;
- my $cfg_dir = shift;
- my $rule_file_base = shift;
- my $workdir = shift;
- my $auditlog = shift;
- my $prev_import_time= shift;
- my $ssh_port = shift;
- my $config_path_on_mgmt = shift;
- my $debug_level = shift;
- my $user_db_file = 'fwauth.NDB';
- my $cmd;
- my $return_code;
- my $fehler_count = 0;
-
- if (!defined($config_path_on_mgmt) || $config_path_on_mgmt eq '') {
- $config_path_on_mgmt = "";
- }
- if (!defined($ssh_port) || $ssh_port eq '') {
- $ssh_port = "22";
- }
- my $tar_archive = 'cp_config.tar.gz'; # compress files for bandwidth optimization
- my $tar_cmd = "cd $config_path_on_mgmt; tar chf $tar_archive $obj_file_base $rule_file_base $user_db_file";
- $cmd = "$ssh_bin -i $workdir/$CACTUS::FWORCH::ssh_id_basename $ssh_user\@$ssh_hostname \"$tar_cmd\"";
-# print("DEBUG - tar_cmd = $cmd\n");
- $return_code = system($cmd); if ( $return_code != 0 ) { $fehler_count++; }
-
- $cmd = "$scp_bin $scp_batch_mode_switch -P $ssh_port -i $workdir/$CACTUS::FWORCH::ssh_id_basename $ssh_user\@$ssh_hostname:$config_path_on_mgmt$tar_archive $cfg_dir";
-# print("DEBUG - copy_cmd = $cmd\n");
- $return_code = system($cmd); if ( $return_code != 0 ) { $fehler_count++; }
-
- $cmd = "$ssh_bin -i $workdir/$CACTUS::FWORCH::ssh_id_basename $ssh_user\@$ssh_hostname \"rm $tar_archive\""; # cleanup
-# print("DEBUG - tar_cmd = $cmd\n");
-
- # gunzip -c xx.tar.gz | tar tvf -
-# $tar_cmd = "cd $cfg_dir; gunzip -c $tar_archive | tar xf -";
- $tar_cmd = "cd $cfg_dir; tar xf $tar_archive";
- $return_code = system($tar_cmd); if ( $return_code != 0 ) { $fehler_count++; }
-
- if ( ! $fehler_count ) {
- &remove_literal_carriage_return("$cfg_dir/$obj_file_base");
- &remove_literal_carriage_return("$cfg_dir/$rule_file_base");
- }
- # do not return name of auditlog file to avoid testing for md5sum
- return ( $fehler_count, "$cfg_dir/$obj_file_base,$cfg_dir/fwauth.NDB,$cfg_dir/$rule_file_base" );
-}
-
-#****************************
-# parser fuer audit log von check point
-# insbesondere fuer die Erkennung von deletes (user et al.) zu verwenden
-#****************************
-# syntaxbeispiele auditlog:
-# 1Oct2007 12:18:22 accept sting < ObjectName: iso-test-user-1; ObjectType: user; ObjectTable: users; Operation: Create Object; Uid: {69907560-5FF5-4120-A62D-D373654D2C15}; Administrator: tim; Machine: stingray; Subject: Object Manipulation; Operation Number: 0; product: SmartDashboard;
-# 2Oct2007 9:52:55 accept sting < ObjectName: iso-test-user-1; ObjectType: user; ObjectTable: users; Operation: Delete Object; Uid: {69907560-5FF5-4120-A62D-D373654D2C15}; Administrator: tim; Machine: stingray; Subject: Object Manipulation; Operation Number: 3; product: SmartDashboard;
-# 2Oct2007 11:07:26 accept sting < ObjectName: isotstusr2; ObjectType: user; ObjectTable: users; Operation: Create Object; Uid: {9ACAF401-9450-4B27-9B4C-D158FC7D2F10}; Administrator: tim; Machine: stingray; Subject: Object Manipulation; Operation Number: 0; product: SmartDashboard;
-# 4Oct2007 10:47:44 accept sting < ObjectName: cactus; ObjectType: firewall_policy; ObjectTable: fw_policies; Operation: Modify Object; Uid: {4DFA246F-BCB1-4283-B3D6-685FBF8DB89B}; Administrator: tim; Machine: stingray; FieldsChanges: Rule 3 Service: removed 'a-250' ;; Subject: Object Manipulation; Operation Number: 1; product: SmartDashboard;
-# 4Oct2007 15:35:07 accept fw1 < Operation: Log In; Administrator: tufin1; Machine: localhost.localdomain; Subject: Administrator Login; Additional Info: Authentication method: Certificate; Operation Number: 10; product: CPMI Client;
-# 4Oct2007 15:35:35 accept fw1 < Operation: Log Out; Administrator: tufin1; Machine: localhost.localdomain; Subject: Administrator Login; Operation Number: 12; product: CPMI Client;
-sub parse_audit_log {
- my $in_file = shift;
- my $prev_import_time = shift;
- my $process_whole_auditlog = shift;
- my $line;
- my ($db_date, $found_line, $found_basic_line, $date, $time, $action, $admin, $management, $obj_name, $obj_type, $obj_table, $operation, $uid);
- my $prev_import_date_minus_5_minutes = &add_delta_db_time($prev_import_time,-600);
-
- open( IN, $in_file ) || die "$in_file konnte nicht geoeffnet werden.\n";
-
- while () {
- $line = $_; # Zeileninhalt merken
- $line =~ s/\x0D/\\r/g; # literal carriage return entfernen
- $line =~ s/\r\n/\n/g; # handle cr,nl (d2u ohne Unterprogrammaufruf)
- chomp($line);
- undef $date; undef $time; undef $action; undef $admin, undef $management;
- undef $obj_name; undef $obj_type; undef $obj_table; undef $operation; undef $uid;
- $found_line = 0;
- $found_basic_line = 0;
-
- # Basisdaten sammeln (Datum, Zeit, ...)
- if ( $line =~ /\s*(\d+\w+\d+)\s+(\d+\:\d+\:\d+)\s+(\w+)\s+(.*?)\s+\<\s+/ ) {
- $date = $1; $time = $2; $action = $3; $management = $4; $found_basic_line = 1;
- $db_date = &convert_checkpoint_to_db_date("$date $time");
- print (" date from auditlog: $db_date; date from prev_import-5minues: $prev_import_date_minus_5_minutes");
- }
- if ($found_basic_line && ((defined($process_whole_auditlog) && $process_whole_auditlog) || $db_date gt $prev_import_date_minus_5_minutes)) {
- # changelog eintrag mit uid
- print ("processing ...\n");
- if ( $found_basic_line && $line =~
- /\s+\<\s+ObjectName\:\s([\w\_\-\s]+)\;\s+ObjectType\:\s+([\w\_\-\s]+)\;\s+ObjectTable\:\s+([\w\_\-\s]+)\;\s+Operation\:\s+([\w\_\-\s]+)\;\s+Uid\:\s+\{([\w\_\-\s]+)\}\;\s+Administrator\:\s+([\w\_\-\s]+)\;/ ) {
- $obj_name = $1; $obj_type = $2; $obj_table = $3; $operation = $4; $uid = $5; $admin = $6; $found_line = 1;
- }
-
- # changelog eintrag ohne uid
- if ( $found_basic_line && $line =~
- /\s+\<\s+ObjectName\:\s([\w\_\-\s]+)\;\s+ObjectType\:\s+([\w\_\-\s]+)\;\s+ObjectTable\:\s+([\w\_\-\s]+)\;\s+Operation\:\s+([\w\_\-\s]+)\;\s+Administrator\:\s+([\w\_\-\s]+)\;/ ) {
- $obj_name = $1; $obj_type = $2; $obj_table = $3; $operation = $4; $admin = $5; $found_line = 1;
- }
- # user login/logout
- if ( $found_basic_line && $line =~ /\s+\<\s+Operation\:\s+(Log\s(In|Out))\;\s+Administrator\:\s+(.*?)\;/ ) {
- $operation = $1; $admin = $3; $found_line = 1;
- }
- if ($found_line) {
- $auditlog{"$audit_log_count.change_time"} = $db_date;
-
- if (defined($operation)) {
- if ( $operation =~ /Create Object/ ) { $operation = 'I'; }
- if ( $operation =~ /Modify Object/ ) { $operation = 'C'; }
- if ( $operation =~ /Delete Object/ ) { $operation = 'D'; }
- $auditlog{"$audit_log_count.change_action"} = $operation;
- }
- $auditlog{"$audit_log_count.management_name"} = $management;
- if (defined($admin)) { $auditlog{"$audit_log_count.change_admin"} = $admin; }
- if (defined($uid)) { $auditlog{"$audit_log_count.changed_object_uid"} = $uid; }
- if (defined($obj_name)) { $auditlog{"$audit_log_count.changed_object_name"} = $obj_name; }
- if (defined($obj_type)) {
- if ( $obj_type =~ /\_protocol/ || $obj_type =~ /\_service/ ) {
- $obj_type = 'service';
- }
- if ( $obj_type =~ /network/ || $obj_type =~ /host\_plain/ || $obj_type =~ /address\_range/ ) {
- $obj_type = 'network_object';
- }
- if ( $obj_type =~ /user/ ) {
- $obj_type = 'user';
- }
- if ( $obj_type =~ /policies\_collection/ || $obj_type =~ /firewall_policy/ ) {
- $obj_type = 'rule';
- }
- $auditlog{"$audit_log_count.changed_object_type"} = $obj_type;
- }
- $audit_log_count ++;
-
- # debugging:
-# print ("parse_audit_log found \@ $date $time for management $management: operation=$operation, admin=$admin");
- if ($operation !~ /^Log/ ) {
- print (", obj_type=$obj_type, obj_name=$obj_name");
- if (defined($uid)) { print (", uid=$uid"); }
- }
- print ("\n");
- }
- }
- }
- close IN;
-}
-
-#****************************
-# Verarbeitung / Aufbereitung der identifizierten Parameter und Values (kontextsesitiv)
-#****************************
-# result_handler_obj
-# obj_state_3
-sub result_handler_obj {
- my $local_name; # Variable als Zwischenspeicher in lokalen Schleifen
- my $found; # flag, true falls Eintrag bekannt
-
- if ( $parse_obj_state >= 3 ) {
-
- # die Attribute von Anfuehrungszeichen befreien
- if ( defined($parse_obj_attr_value) ) {
-
- # Test auf Primary Management
- if ( ( $parse_obj_attr eq "primary_management" )
- && ( $parse_obj_attr_value eq "true" ) )
- {
- $mgm_name = $parse_obj_name; # setting mgm_name defined in CACTUS::FWORCH::import.pm
- }
-
- # Attribute in lowercase wandeln
- if ( $parse_obj_attr eq "type" ) {
- $parse_obj_attr_value = lc($parse_obj_attr_value);
- }
- }
-
- # objekte nach typen sortiert in arrays sammeln
- SWITCH_OBJ_TYPE: {
- if (defined($parse_obj_name)) {
- # globals any obj
- if ( $parse_obj_type eq "globals"
- && $parse_obj_name eq 'Any'
- && $parse_obj_attr eq 'AdminInfo' )
- {
- @network_objects = ( @network_objects, $parse_obj_name );
- $network_objects{"$parse_obj_name.name"} = $parse_obj_name;
- $network_objects{"$parse_obj_name.UID"} =
- &gen_uid($parse_obj_attr_ext_value);
- $network_objects{"$parse_obj_name.ipaddr"} = '0.0.0.0';
- $network_objects{"$parse_obj_name.comments"} =
- '"implied any network object from objects.C global section"';
- $network_objects{"$parse_obj_name.type"} = 'network';
- $network_objects{"$parse_obj_name.netmask"} = '0.0.0.0';
- $network_objects{"$parse_obj_name.location"} = 'internal';
- @services = ( @services, $parse_obj_name );
- $services{"$parse_obj_name.name"} = $parse_obj_name;
- $services{"$parse_obj_name.UID"} =
- &gen_uid($parse_obj_attr_ext_value);
- $services{"$parse_obj_name.comments"} =
- '"any service from objects.C global section"';
- $services{"$parse_obj_name.typ"} = 'simple';
- $services{"$parse_obj_name.type"} = 'simple';
- last SWITCH_OBJ_TYPE;
- }
- # dynamic zone objects
- if ( $parse_obj_name eq 'Internet_Zone' || $parse_obj_name eq 'Trusted_Zone')
- {
- @network_objects = ( @network_objects, $parse_obj_name );
- $network_objects{"$parse_obj_name.name"} = $parse_obj_name;
- $network_objects{"$parse_obj_name.UID"} =
- &gen_uid($parse_obj_attr_ext_value);
- $network_objects{"$parse_obj_name.ipaddr"} = '0.0.0.0';
- $network_objects{"$parse_obj_name.comments"} =
- '"checkpoint implied zone object, filled dynamically"';
- $network_objects{"$parse_obj_name.type"} = 'network';
- $network_objects{"$parse_obj_name.netmask"} = '0.0.0.0';
- $network_objects{"$parse_obj_name.location"} = 'internal';
- last SWITCH_OBJ_TYPE;
- }
- }
-
- # netzobjekte
- if ( $parse_obj_type eq "network_objects" ) {
-
- # schon ein bekanntes network_object?
- if ( !defined( $network_objects{"$parse_obj_name.name"} ) ) {
- @network_objects = ( @network_objects, $parse_obj_name );
- $network_objects{"$parse_obj_name.name"} = $parse_obj_name;
- }
-
- # Daten im Hash ergaenzen
- if ( $parse_obj_state > 10 )
- { # group member alle eingesammelt, abspeichern und zurueck
- if ( defined($parse_obj_groupmember)
- && defined($parse_obj_groupmember_refs) )
- {
-
- # group member alle eingesammelt, abspeichern und zurueck
- $network_objects{"$parse_obj_name.members"} =
- $parse_obj_groupmember;
- $network_objects{"$parse_obj_name.member_refs"} =
- $parse_obj_groupmember_refs;
- $network_objects{"$parse_obj_name.typ"} = 'group';
- }
- $parse_obj_state -= 10;
- }
- if ( defined($parse_obj_attr_value) ) {
-
- # Attribute aufarbeiten
- SWITCH_OBJ_ATTR: {
- # start ipv6
- if ( $parse_obj_attr eq 'ipv6_prefix') { # IPV6 netmask
- $parse_obj_attr_value = CACTUS::FWORCH::remove_space_at_end($parse_obj_attr_value);
- $network_objects{"$parse_obj_name.netmask"} = $parse_obj_attr_value;
- last SWITCH_OBJ_ATTR;
- }
- if ( $parse_obj_attr eq 'ipv6_address') { # IPV6 ip addr
- $parse_obj_attr_value = CACTUS::FWORCH::remove_space_at_end($parse_obj_attr_value);
- $network_objects{"$parse_obj_name.ipaddr"} = $parse_obj_attr_value;
- last SWITCH_OBJ_ATTR;
- }
- if ( $parse_obj_attr eq 'ipv6_type') { # IPV6 ip addr
- $parse_obj_attr_value = CACTUS::FWORCH::remove_space_at_end($parse_obj_attr_value);
- $network_objects{"$parse_obj_name.type"} = $parse_obj_attr_value;
- last SWITCH_OBJ_ATTR;
- }
- if ( $parse_obj_attr eq 'type' && $parse_obj_attr_value eq 'ipv6_object') { # ignore v6 type info
- last SWITCH_OBJ_ATTR;
- }
- # end ipv6
- if ( $parse_obj_attr eq 'ipaddr_first' || $parse_obj_attr eq 'bogus_ip') { # R75 new feature zone objects without ip_addr
- $parse_obj_attr_value =
- CACTUS::FWORCH::remove_space_at_end($parse_obj_attr_value);
- $network_objects{"$parse_obj_name.ipaddr"} = $parse_obj_attr_value;
- last SWITCH_OBJ_ATTR;
- }
- if ( $parse_obj_attr eq 'AdminInfo' ) {
- $network_objects{"$parse_obj_name.UID"} =
- &gen_uid($parse_obj_attr_ext_value);
- last SWITCH_OBJ_ATTR;
- }
- if ( $parse_obj_attr eq 'By' ) {
- $network_objects{
- "$parse_obj_name.last_change_admin"} =
- $parse_obj_attr_value;
- last SWITCH_OBJ_ATTR;
- }
- if ( $parse_obj_attr eq 'Time' ) {
- $network_objects{"$parse_obj_name.last_change_time"}
- = $parse_obj_attr_value;
-
-# print("debug: setting last_change_time for nwobject $parse_obj_name to $parse_obj_attr_value\n");
- last SWITCH_OBJ_ATTR;
- }
- $network_objects{"$parse_obj_name.$parse_obj_attr"} =
- $parse_obj_attr_value;
- }
- }
- last SWITCH_OBJ_TYPE;
- }
-
- # services & resources
- if ( $parse_obj_type eq "services" || $parse_obj_type eq "resources") {
-
- # schon ein bekanntes network_object?
- if ( !defined( $services{"$parse_obj_name.name"} ) ) {
- @services = ( @services, $parse_obj_name );
- $services{"$parse_obj_name.name"} = $parse_obj_name;
- }
-
- # Daten im Hash ergaenzen
- if ( $parse_obj_state > 10 ) {
- if ( defined($parse_obj_groupmember)
- && defined($parse_obj_groupmember_refs) )
- {
-
- # group member alle eingesammelt, abspeichern und zurueck
- @services{"$parse_obj_name.members"} =
- $parse_obj_groupmember;
- @services{"$parse_obj_name.member_refs"} =
- $parse_obj_groupmember_refs;
- @services{"$parse_obj_name.typ"} = 'group';
- }
- $parse_obj_state -= 10;
- }
- if ( defined($parse_obj_attr_value) ) {
-
- # Attribute aufarbeiten
- SWITCH_OBJ_ATTR: {
- if ( $parse_obj_attr eq 'AdminInfo' ) {
- $services{"$parse_obj_name.UID"} =
- &gen_uid($parse_obj_attr_ext_value);
- last SWITCH_OBJ_ATTR;
- }
- if ( $parse_obj_attr eq 'By' ) {
- $services{"$parse_obj_name.last_change_admin"} =
- $parse_obj_attr_value;
- last SWITCH_OBJ_ATTR;
- }
- if ( $parse_obj_attr eq 'Time' ) {
- $services{"$parse_obj_name.last_change_time"} =
- $parse_obj_attr_value;
-
-# print("debug: setting last_change_time for service $parse_obj_name to $parse_obj_attr_value\n");
- last SWITCH_OBJ_ATTR;
- }
- if ( $parse_obj_attr eq 'port' ) {
-
- # >portnummer identifizieren
- if ( $parse_obj_attr_value =~ (/"\>(\d+)/) ) {
- $services{"$parse_obj_name.port"} = $1 + 1;
- $services{"$parse_obj_name.port_last"} = 65535;
- }
-
- # portnummer identifizieren SRC
- if ( $parse_obj_attr_value =~ (/"\>(\d+)/) ) {
- $services{"$parse_obj_name.src_port"} = $1 + 1;
- $services{"$parse_obj_name.src_port_last"} =
- 65535;
- }
-
- # ) {
- $line = $_; # Zeileninhalt merken
- $last_line = $line; # fuer end of line check
- $line =~ s/\x0D/\\r/g; # literal carriage return entfernen
- $line =~ s/\r\n/\n/g; # handle cr,nl (d2u ohne Unterprogrammaufruf)
- chomp($line);
- $ln++; # Zeilennummer merken
-
- SWITCH_parsemode: {
-
- # objects oder rules parsen?
- if ( $mode eq 'objects' ) {
-
- # group member collection
- if ( $parse_obj_state > 10 ) {
-
- #kein neuer Parameter, d.h. ein Gruppenmitglied (Member) gefunden
- # Pattern <=> Einzugtiefe, Doppelpunkt, 'Zeichenkette'
- if (/^\t{3}:(\w+)/)
- { # Ende der Member-Definition, weiter aber Member noch nicht wegschreiben
- $parse_obj_state -= 10;
- }
- else { # Member anfuegen
- # Pattern <=> Einruecktiefe, Doppelpunkt, "Name", Leerzeichen, 'Namensstring'
- my $member_prefix = '';
- if (defined($group_with_exclusion_marker) &&
- ($group_with_exclusion_marker eq 'base' || $group_with_exclusion_marker eq 'exception'))
- {
- $member_prefix = "{$group_with_exclusion_marker}";
- undef ($group_with_exclusion_marker);
- }
- if (/^\t{4}:Name\s\(([\w\-\.\_]+)\)/) {
- if ( defined($parse_obj_groupmember) ) {
- $parse_obj_groupmember =
- $parse_obj_groupmember . $GROUPSEP . $member_prefix . $1;
- }
- else {
- $parse_obj_groupmember = $member_prefix . $1;
- }
- }
- elsif (/^\t{4}:Uid\s\(\"\{([\w\-]+)\}\"\)/) {
- if ( defined($parse_obj_groupmember_refs) ) {
- $parse_obj_groupmember_refs =
- $parse_obj_groupmember_refs
- . $GROUPSEP
- . &gen_uid($1);
- }
- else {
- $parse_obj_groupmember_refs = &gen_uid($1);
- }
- }
- }
- }
-
- # globaler Aufbau der folgenden Abschnitte:
- # -> Suche nach einem Muster in einen Status => Status heraufsetzen
- # Pattern <=> Einzugtiefe, ':', ...weitere
- # -> optionale Bloecke
- # -> Suche nach einem Muster, das den Status beendet => Status herabsetzen
- # Pattern <=> Einzugtiefe, Klammerzu
-
- # state 1 type ohne Config bzw. einzeilig, z.B. :serverobj (serverobj)
- if (/^\t:([\w\-\.\_]+)\s\(.*?\)$/) {
- }
- elsif (/^\t:([\w\-\.\_]+) */) {
-
- # anfang parse obj state 1
- # Pattern <=> Einzugtiefe, ':', obj_typ
- print_debug("up $parse_obj_state => 1 at $ln\n");
- $parse_obj_state = 1;
- $parse_obj_type = $1;
- }
- elsif (/^\t\)$/) {
-
- # Ende des state 1
- # Pattern <=> Einzugtiefe, Klammerzu
- $parse_obj_state = 0;
- undef($parse_obj_type);
- }
-
- # parse obj state 2 - objektnamen setzen
- # Pattern <=> Einzugtiefe, ':', 'Leerzeichen', obj_name, 'Leerzeichen'
- if ( (/^\t{2}:\s\(([\"\w\-\.\_]+)\s*/)
- && ( ( $parse_obj_state == 2 )
- || ( $parse_obj_state == 1 ) ) )
- {
- print_debug("up $parse_obj_state => 2 at $ln\n");
- $parse_obj_state = 2;
- $parse_obj_name = $1;
- undef($parse_obj_groupmember); # Gruppe initialisieren
- undef($parse_obj_groupmember_refs); # Gruppe initialisieren
- }
-
- # Ende des state 2
- # Pattern <=> Einzugtiefe, Klammerzu
- if ( (/^\t{2}\)/) ) { # Ende der Objekt-Definition
- # (alter status - neuer status) darf nicht groesser als eine Ebene sein, sonst ist die Struktur falsch interpretiert worden
- $parse_obj_state = 1;
- if ( defined($parse_obj_groupmember) )
- { # wenn group-member gefunden, diese jetzt wegschreiben
- $parse_obj_state += 10;
- result_handler_obj();
- }
- undef($parse_obj_name);
- }
-
-# parse obj state 3
-# in diesem und den hoeheren Stati werden die Objekteigenschaften festgelegt.
-# Pattern <=> Einzugtiefe, ':', parameter, 'Leerzeichen', 'Klammerauf', value-wenn definiert, 'Klammerzu',
- if ( (/^\t{3}:(\w+)\s\((.*)/)
- && ( ( $parse_obj_state == 3 )
- || ( $parse_obj_state == 2 ) ) )
- {
- print_debug("up $parse_obj_state => 3 at $ln\n");
- $parse_obj_state = 3;
- $parse_obj_attr =
- &remove_space_at_end($1); # TMP, remove space at end of attribute (eg. ip)
- if ( defined($2) ) { # Klammer nicht leer
- # value zwischenspeichern und untersuchen
- my $local_line = $2;
- print_debug("local_line: $local_line at $ln\n"); #TMP
- # Ist der Wert in einer Zeile mit dem Parameter angegeben?
- # Pattern <=> value, 'Klammerzu'
- if ( $local_line =~ (/^(.*)\)$/) ) {
- undef($parse_obj_attr_value);
- if ( defined($1) ) {
- $parse_obj_attr_value =
- &remove_space_at_end($1)
- ; # TMP, remove space at end of attribute (eg. ip)
- }
-
- # derzeit nicht interpretierte Parameter ueberspringen
- unless ( ( $parse_obj_attr eq 'members_query' )
- || ( $parse_obj_attr eq 'show_in_menus' )
- || ( $parse_obj_attr eq 'reload_proof' )
- || ( $parse_obj_attr eq 'etm_enabled' )
-# || ( $parse_obj_attr eq 'track' )
-# || ( $parse_obj_attr eq 'Wiznum' )
- || ( $parse_obj_attr eq 'include_in_any' ) )
- {
-
- # den Rest im Result Handler aufbereiten
- print_debug("call result handler at $ln\n");
- result_handler_obj();
- }
- print_debug(
- " auto down $parse_obj_state => 2 at $ln\n");
-
-# Keine Gruppe/Liste geoeffnt, daher direkt den parse_obj_state auf den Level 2 zuruecksetzen
- $parse_obj_state = 2;
- $parse_obj_attr = "";
- }
- }
- }
-
-# Gruppenmitglieder zu service oder nwobject (wichtig, nur Eintraege ohne 'members_query'!!!)
-# Setzt nur den Status um >10 --> Gruppe
-# Pattern <=> Einzugtiefe, ':', 'Leerzeichen', 'Klammerauf', 'ReferenceObject', 'Klammerzu',
- if ( (/^\t{3}:\s\(ReferenceObject/) ) {
- print_debug("up $parse_obj_state => 3 at $ln\n");
- $parse_obj_state = 3;
-
- # Gruppenstatus setzten
- $parse_obj_state += 10;
- }
-# Behandlung von "group_with_exclusion" Objekten
- if ( (/^\t{3}:base\s\(ReferenceObject/) ) {
- $group_with_exclusion_marker = 'base';
- $parse_obj_state = 13;
- }
- if ( (/^\t{3}:exception\s\(ReferenceObject/) ) {
- $group_with_exclusion_marker = 'exception';
- $parse_obj_state = 13;
- }
-
- # Ende des state 3
- # Pattern <=> Einzugtiefe, Klammerzu
- if ( (/^\t{3}\)/) ) {
-
-# (alter status - neuer status) darf nicht groesser als eine Ebene sein, sonst ist die Struktur falsch interpretiert worden
- $parse_obj_state = 2;
- $parse_obj_attr = "";
- }
-
-# parse obj state 4
-# Pattern <=> Einzugtiefe, ':', parameter, 'Leerzeichen', 'Klammerauf', value-wenn definiert, 'Klammerzu',
- if (
- (/^\t{4}:(\w+)\s\((.*?)\)/)
- && ( $parse_obj_state == 4
- || $parse_obj_state == 3
- || $parse_obj_state == 5 )
- )
- {
- print_debug("up $parse_obj_state => 4 at $ln\n");
- $parse_obj_state = 4;
- $parse_obj_attr_ext = $1;
- if ( defined($2) ) {
- $parse_obj_attr_ext_value = $2;
- print_debug(
- " auto down $parse_obj_state => 3 at $ln\n");
- if ( defined($parse_obj_attr_ext_value) ) {
- $parse_obj_attr_ext_value =
- &remove_space_at_end(
- $parse_obj_attr_ext_value)
- ; # TMP, remove space at end of attribute (eg. ip)
- # Suche nach \t\t\t\t:chkpf_uid ("{9BD86F11-908B-4723-B7E9-31252A17B034}")
- if ( $parse_obj_attr_ext eq 'chkpf_uid' ) {
-
- # Aufbereiten der UID, Pattern "{ entfernen
- $parse_obj_attr_ext_value =~ s/\"\{//g;
-
- # Pattern }" entfernen
- $parse_obj_attr_ext_value =~ s/\}\"//g;
- $parse_obj_attr_ext_value =
- $parse_obj_attr_ext_value;
-
- # Abspeichern der UID
- result_handler_obj();
- undef($parse_obj_attr_ext_value);
- }
- }
- $parse_obj_attr_ext = "";
- }
- }
-
- # Ende des state 4
- # Pattern <=> Einzugtiefe, Klammerzu
- if ( (/^\t{4}\)/) && ( $parse_obj_state < 10 ) ) {
-
-# (alter status - neuer status) darf nicht groesser als eine Ebene sein, sonst ist die Struktur falsch interpretiert worden
- $parse_obj_state = 3;
- undef($parse_obj_attr_ext);
- undef($parse_obj_attr_ext_value);
- }
-
-# parse obj state 5
-# Pattern <=> Einzugtiefe, ':', parameter, 'Leerzeichen', 'Klammerauf', value-wenn definiert, 'Klammerzu',
- if (
- (/^\t{5}:(\w+)\s\((.*)/) && (
- $parse_obj_state == 5
- || $parse_obj_state == 4
- )
- )
- {
- print_debug("up $parse_obj_state => 5 at $ln\n");
- $parse_obj_state = 5;
- if ( defined($2) ) {
- my $local_line = $2;
- $parse_obj_attr = $1;
- if ( $parse_obj_attr eq 'By' ) {
-
- # der attr_ext_value steht in einer Zeile mit dem attr_ext
- # Pattern <=> 'beliebige Zeichen des values', Klammerzu
- if ( $local_line =~ (/^(.*)\)$/) ) {
- $parse_obj_attr_value = $1;
-
- # Abspeichern des last_change_admin
- result_handler_obj();
- }
- }
- if ( $parse_obj_attr eq 'Time' ) {
- my $timestamp;
- if ( $local_line =~ (/^\"(.*)\"\)$/) ) {
- $timestamp = convert_checkpoint_to_db_date($1);
- if ( defined($timestamp) ) {
- $parse_obj_attr_value = $timestamp;
- result_handler_obj();
- if ( !defined($last_change_time_of_config)
- || $last_change_time_of_config
- lt $timestamp )
- {
-
- # das gefundene Aenderungsdatum ist neuer als das bisherige
- $last_change_time_of_config =
- $timestamp;
- }
- }
- }
- }
- }
-
- # $parse_obj_state = 4;
- }
-
- # Ende des state 5
- # Pattern <=> Einzugtiefe, Klammerzu
- if ( (/^\t{5}\)/) && ( $parse_obj_state < 10 ) ) {
-
-# (alter status - neuer status) darf nicht groesser als eine Ebene sein, sonst ist die Struktur falsch interpretiert worden
- $parse_obj_state = 4;
- undef($parse_obj_attr_ext2);
- undef($parse_obj_attr_ext2_value);
- }
-
-# parse obj state 6
-# Pattern <=> Einzugtiefe, ':', parameter, 'Leerzeichen', 'Klammerauf', value-wenn definiert, 'Klammerzu',
- if ( (/^\t{6}:(\w+)\s\((.*)/)
- && ( ( $parse_obj_state == 6 )
- || ( $parse_obj_state == 5 ) ) )
- {
- print_debug("up $parse_obj_state => 6 at $ln\n");
- $parse_obj_state = 6;
- $parse_obj_attr_ext3 =
- &remove_space_at_end($1)
- ; # TMP, remove space at end of attribute (eg. ip)
- if ( defined($2) ) {
- my $local_line = $2;
-
- # der attr_ext_value steht in einer Zeile mit dem attr_ext
- # Pattern <=> 'beliebe Zeichen des values', Klammerzu
- if ( $local_line =~ (/^(.*)\)$/) ) {
- print_debug(
- " auto down $parse_obj_state => 6 at $ln\n");
- if ( defined($1) ) {
- $parse_obj_attr_ext3_value =
- &remove_space_at_end($1); # TMP, remove space at end of attribute
- }
-
- #// insert parsresult handler call //#
- #// wenn weitere Auswertung notwendig //#
- $parse_obj_state = 5;
- undef($parse_obj_attr_ext3);
- undef($parse_obj_attr_ext3_value);
-
- # komplexes Attribut, ggf. im Resulthandler auszuwerten
- }
- }
- }
-
- # Ende des state 6
- # Pattern <=> Einzugtiefe, Klammerzu
- if ( (/^\t{6}\)/) && ( $parse_obj_state < 10 ) ) {
-
-# (alter status - neuer status) darf nicht groesser als eine Ebene sein, sonst ist die Struktur falsch interpretiert worden
-# print_debug("down $parse_obj_state => 5 at $ln\n") || (($parse_obj_state - 4) >1);
- $parse_obj_state = 5;
- undef($parse_obj_attr_ext3);
- undef($parse_obj_attr_ext3_value);
- }
-
-# parse obj state 7
-# Pattern <=> Einzugtiefe, ':', parameter, 'Leerzeichen', 'Klammerauf', value-wenn definiert, 'Klammerzu',
- if ( (/^\t{7}:(\w+)\s\((.*)/)
- && ( ( $parse_obj_state == 7 )
- || ( $parse_obj_state == 6 ) ) )
- {
- print_debug("up $parse_obj_state => 7 at $ln\n");
- $parse_obj_state = 7;
- $parse_obj_attr_ext4 =
- &remove_space_at_end($1)
- ; # TMP, remove space at end of attribute
- if ( defined($2) ) {
- my $local_line = $2;
- if ( $local_line =~ (/^(.*)\)$/) ) {
- print_debug(
- " auto down $parse_obj_state => 7 at $ln\n");
- if ( defined($1) ) {
- $parse_obj_attr_ext4_value =
- &remove_space_at_end($1)
- ; # TMP, remove space at end of attribute
- }
-
- #// insert parsresult handler call //#
- #// wenn weitere Auswertung notwendig //#
- $parse_obj_state = 6;
- undef($parse_obj_attr_ext4);
- undef($parse_obj_attr_ext4_value);
-
- # komplexes Attribut, ggf. im Resulthandler auszuwerten
- # $parse_obj_attr = "";
- }
- }
- }
-
- # Pattern <=> Einzugtiefe, Klammerzu
- if ( (/^\t{7}\)/) && ( $parse_obj_state < 10 ) ) {
-
-# (alter status - neuer status) darf nicht groesser als eine Ebene sein, sonst ist die Struktur falsch interpretiert worden
-# print_debug("down $parse_obj_state => 6 at $ln\n") || (($parse_obj_state - 6) >1);
- $parse_obj_state = 6;
- undef($parse_obj_attr_ext4);
- undef($parse_obj_attr_ext4_value);
- }
-
- #// insert parsresult handler call //#
- #// wenn weitere Auswertung notwendig //#
- last SWITCH_parsemode;
- }
-
- # Rules parsen
- if ( $mode eq 'rules' ) {
-
- # rule member collection
- # sammelt mitglieder einer Gruppe (entspricht hier einer Spalte)
- if ( $parserule_state > 10 ) {
-
- #kein neuer Parameter - keine schliessende Klammer
- # Pattern <=> Einzugtiefe, Klammerzu
- unless (/^\t\t\t\)/) {
-
-# innerhalb der Gruppe Parameter suchen
-# Pattern <=> Einzugtiefe, ':',Eigenschaft der Gruppe, Leerzeichen Klammerauf, value-wenn definiert, Klammerzu
- if (/^\t{4}:(op)\s\((.*)\)/) {
- $parserule_ruleparameter_ext = $1;
- if ( defined($2) ) {
- if ( $2 eq '"not in"' ) {
- $parserule_ruleparameter_ext_value = "true";
- }
- else {
- $parserule_ruleparameter_ext_value = "false";
- }
- $rulebases{"$parserule_rulebasename.$parserule_rulenum.$parserule_ruleparameter.$parserule_ruleparameter_ext"} = $parserule_ruleparameter_ext_value;
- }
- }
-
-# compound gruppenmitglieder, die Gruppe faengt hier an
-# Pattern <=> Einzugtiefe, Doppelpunkt, "compound", Leerzeichen, Klammerauf, 'zeichenkette'
- if (/^\t{4}:(compound)\s\((.*)/) {
-
- # kein Wert angegeben
- # Pattern <=> Klammerzu
- unless ( $2 =~ m/\)$/ ) {
- $parserule_state = 16;
- }
-
- # Werte werden zwischengespeichert, aber nicht verwendet
- $parserule_ruleparameter_ext = $1;
- $parserule_ruleparameter_ext_value = $2;
- }
-
-# einfaches gruppenmitglied faengt hier an
-# Pattern <=> Einzugtiefe, ':', Leerzeichen, Klammerauf, "ReferenceObject", Klammerzu
- if (/^\t{4}:\s\(ReferenceObject/) {
- $parserule_state = 15;
- }
-
- # die eigentliche Gruppenliste hoert auf
- # Pattern <=> Einzugtiefe, Klammerzu
- elsif (/^\t{4}\)/) {
- $parserule_state = 14;
- }
-
-# einfaches Member anfuegen
-# Pattern <=> Einzugtiefe, Doppelpunkt, "Name", Leezeichen, 'objektname', Klammerzu
-# bei Name-Pattern auch Leerzeichen und optionale Hochkommata-Klammerung hinzugefuegt (fuer UserDefined 3)
- if ( (/^\t{5}:Table\s\(([\w\-\.\_\s]+)\)/)
- && ( $parserule_state == 15 ) )
- {
- if ($1 =~ /^identity\_roles$/) {
- # now not adding source network_objects but identity awareness user groups
- $parse_obj_type = 'identity_roles';
- if ($prev_line =~ /^\t{5}:Name\s\(\"?([\w\-\.\_\s]+)\"?\)/) {
- $parserule_ruleuser = $1;
- } else {
- print ("id:ERROR: found identity_roles Usergroup but no name in previous line=$prev_line\n");
- }
- }
- }
- elsif ( (/^\t{5}:Name\s\(\"?([\w\-\.\_\s]+)\"?\)/)
- && ( $parserule_state == 15 ) )
- {
- if ( defined($parserule_groupmember) ) {
- $parserule_groupmember =
- $parserule_groupmember . $GROUPSEP . $1;
- }
- else {
- $parserule_groupmember = $1;
- }
- }
- elsif ((/^\t{5}:Uid\s\(\"\{([\w\-\.\_\s]+)\}\"\)/)
- && ( $parserule_state == 15 ) )
- {
-####### start identity_roles
- if (defined($parse_obj_type) && $parse_obj_type eq 'identity_roles') {
- # set identity role as user group @ any
- # assuming always any source nw obj for identity rule objects
- # might additionally parse identity_roles.C for specific_identity_entities (both locations and machines)
-=cut
- :locations (
- :restrict_to (Specific)
- :specific_identity_entities (
- : (ReferenceObject
- :Uid ("{BF1C6029-8D5D-4A05-A3AD-4B67E6E7A7AA}")
- :Name (Cactus-100)
- :Table (network_objects)
- )
- : (ReferenceObject
- :Uid ("{ABBA18CB-625D-4231-BF5A-6DBF646A155C}")
- :Name (CACTUS-WLAN-Clients_client_auth)
- :Table (network_objects)
- )
- )
- )
- :machines (
- :type (machines_restriction)
- :restrict_to (Specific)
- :specific_identity_entities (
- : (ReferenceObject
- :Uid ("{7D67B970-ABDE-4A97-8CEB-0EE8F6703750}")
- :Name (ad_machine_Calamus)
- :Table (ad_machines)
- )
- : (ReferenceObject
- :Uid ("{DDF26071-6C14-4DC1-8040-605C9042321E}")
- :Name (ad_machine_TICK)
- :Table (ad_machines)
- )
- )
- )
-=cut
- if ( defined($parserule_groupmember_refs) ) {
- $parserule_groupmember_refs = $parserule_groupmember_refs . $GROUPSEP . $parserule_ruleuser . "@" .
- &gen_uid($network_objects{"Any.UID"});
- $parserule_groupmember = $parserule_groupmember . $GROUPSEP . $parserule_ruleuser . '@Any';
- }
- else {
- $parserule_groupmember = $parserule_ruleuser . '@Any'; # assuming always any source nw obj for identity rule objects
- $parserule_groupmember_refs = "$parserule_ruleuser" . "@" . &gen_uid($network_objects{"Any.UID"});
- }
-# print ("id:found id_roles Usergroup User::$parserule_ruleuser, parserule_groupmember_refs: $parserule_groupmember_refs\n");
-####### end identity_roles
- } else {
- if ( defined($parserule_groupmember_refs) ) {
- $parserule_groupmember_refs =
- $parserule_groupmember_refs
- . $GROUPSEP
- . &gen_uid($1);
- }
- else {
- $parserule_groupmember_refs = &gen_uid($1);
- }
- }
- }
-
- # compound Member anfuegen
- # Pattern <=> Einzugtiefe, Doppelpunkt, Klammerauf, 'Gruppenmitglied'
- if ( (/^\t{5}:\s\((.*)/)
- && ( $parserule_state == 16 ) )
- {
- my $compound_name = $1;
- if ( $compound_name =~
- /\w+\-\>\"?([\w\-\.\_\s]+)\"?/ )
- { # strip of resource-suffix (eg. http->)
- # INFO: check point resources koennen in CP NGX und davor immer nur einzeln in Regeln auftauchen
- $compound_name = $1;
- $parserule_state = 18; # 18 = resource
- }
- if ( defined($parserule_groupmember) ) {
- $parserule_groupmember =
- $parserule_groupmember
- . $GROUPSEP
- . $compound_name;
- }
- else {
- $parserule_groupmember = $compound_name;
- }
-
- # Userinfo pro Regel ausfiltern
- # user(-gruppen) stehen vor dem @ zeichen, gefolgt vom netz(-objekt)
- if ( $compound_name =~ m/(.*)\@(.*)/ ) {
- $current_user = $1;
- $current_user =~ s/"//g;
- if ( defined($parserule_ruleuser) ) {
- $parserule_ruleuser =
- $parserule_ruleuser
- . $GROUPSEP
- . $current_user;
- }
- else {
- $parserule_ruleuser = $current_user;
- }
- }
- }
-
- # resource
- if ( (/^\t{6}:resource\s\(ReferenceObject$/) && ( $parserule_state == 18 ) )
- {
- $parserule_state = 19; # 19 = uid einer Resource
- }
-
- if ( (/^\t{7}:Uid\s\(\"\{(.*?)\}\"\)$/) && ( $parserule_state == 19 ) )
- {
- $parserule_groupmember_refs = $1;
-# $rulebases{"$parserule_rulebasename.$parserule_rulenum.services.refs"} = $1;
-# $parserule_state = 14;
- }
- # end resource
-
- if ( (/^\t{6}:at\s\(ReferenceObject$/) && ( $parserule_state == 16 ) )
- {
- $parserule_state = 17;
- }
-
- # die Referenz auf das nwobjekt hinzufuegen
- if ( (/^\t{7}:Uid\s\(\"\{(.*)\}\"\)$/) && ( $parserule_state == 17 ) )
- {
- my $user_string = '';
- my $nwobj_uid = $1;
-
- if ( defined($current_user) ) {
- $user_string = "$current_user@";
- undef($current_user);
- }
- if ( defined($parserule_groupmember_refs) ) {
- $parserule_groupmember_refs = "$user_string" . &gen_uid($nwobj_uid) . "$GROUPSEP$parserule_groupmember_refs";
- }
- else {
- $parserule_groupmember_refs = "$user_string" . &gen_uid($nwobj_uid);
- }
- $parserule_state = 16;
- }
-
- # alle Member gefunden, daher wegschreiben
- }
- else {
- if ( defined($parserule_groupmember) ) {
- $rulebases{"$parserule_rulebasename.$parserule_rulenum.$parserule_ruleparameter"} = $parserule_groupmember;
- $rulebases{"$parserule_rulebasename.$parserule_rulenum.$parserule_ruleparameter.refs"} = $parserule_groupmember_refs;
- }
-
- $parserule_state -= 10;
- undef($parse_obj_type);
- undef($parserule_groupmember);
- undef($parserule_groupmember_refs);
- undef($parserule_ruleuser);
- }
- }
-
- # parserule state 1 - Regelwerk faengt an
- # Pattern <=> Einzugtiefe, ':rule-base'
- if (/^\t:rule-base/) {
- $parserule_in_rule_base = 1;
- $parserule_rulenum = -1;
- $parserule_state = 1;
-
-# den Namen identifizieren
-# Pattern <=> beliebiger Anfang, Anfuehrungszeichen, Doppelkreuz, Doppelkreuz, 'name des Regelwerks', Anfuehrungszeichen
- if (/.*\"##(.*)\"/) {
- $parserule_rulebasename = $1;
- if ( defined($rulesetname) && ruleset_does_not_fit($parserule_rulebasename, $rulesetname) )
- { # Nicht gesuchten Regelsatz uebergehen, $rulesetname = hashref
- IGNORE_RULESET: while () {
- $last_line = $_;
- if ( $last_line =~ /^\t\)/ ) {
- last IGNORE_RULESET;
- }
- }
- }
- else {
- print_debug("Regelwerk $1 wird geparst\n");
- unless (defined($rulebases{"$parserule_rulebasename.name"}))
- {
- @rulebases = ( @rulebases, $parserule_rulebasename );
- $rulebases{"$parserule_rulebasename.name"} = $parserule_rulebasename;
- $rulebases{"$parserule_rulebasename.rulecount"} = 0;
- }
-
- # LastChangeAdmin und LastSaveTime fuer das Regelwerk identifizieren
- IGNORE_ADM_INFO: while ( $line = )
- { # move to end of AdminInfo
- $last_line = $_;
- if ( $line =~ /^\t\t\)/ ) {
- last IGNORE_ADM_INFO;
- }
- if ( $line =~ /^\t\t\t\t:By\s\((.+?)\)/ ) {
- $rulebases{"$parserule_rulebasename.last_change_admin"} = $1;
- }
- if ( $line =~ /^\t\t\t\t:Time\s\(\"(.+?)\"\)/ )
- {
- my $timestamp = convert_checkpoint_to_db_date($1);
- if ( defined($timestamp) ) {
- $rulebases{"$parserule_rulebasename.last_save_time"} = $timestamp;
- if (!defined($last_change_time_of_config) || $last_change_time_of_config lt $timestamp)
- { # das gefundene Aenderungsdatum ist neuer als das bisherige
- $last_change_time_of_config = $timestamp;
- }
- }
- }
- }
- }
- }
- }
-
- # parserule state 2 - Access Regel faengt an
- # Pattern <=> Einzugtiefe, ':rule '
- if (/^\t{2}:rule\s/) {
- $parserule_in_rule = 1;
- $parserule_rulenum++;
- print_debug(
- "switching form state $parserule_state to 2.\n");
- $parserule_state = 2;
- $rulebases{"$parserule_rulebasename.rulecount"} =
- $parserule_rulenum + 1;
-
-# setzen des last_change_admin (bei CP leider global fuer das gesamte Regelwerk)
- $rulebases{"$parserule_rulebasename.$parserule_rulenum.last_change_admin"} =
- $rulebases{"$parserule_rulebasename.last_change_admin"};
- $ruleorder[$parserule_rulenum] = $parserule_rulenum; # Regeln sind einfach von 1-n durchnummeriert
- # neu: Wegschreiben der Regelreihenfolge
- $rulebases{"$parserule_rulebasename.ruleorder"} =
- join( ',', @ruleorder );
- print_debug(
- "rule: $parserule_in_rule\t$parserule_rulenum\n");
- }
- if (/^\t{2}:rule_adtr\s/)
- { # eingefuegt, um keine comments von AdrTrans-Regeln hineinzubekommen
- print_debug("ignoring address translation rule\n");
- IGNORE_ADDR_TRANS: while () {
- $last_line = $_;
- if ( $last_line =~ /^\t\t\)/ ) {
- last IGNORE_ADDR_TRANS;
- }
- } # lese Zeilen bis zur Ende-Klammerung der rule_adtr
- }
-
- # parserule state 3 - Regeldetails fangen an
- # Pattern <=> Einzugtiefe, ':', beliebiges dahinter
- if ( ( /^\t{3}:(.*)/ && $parserule_state > 1 ) ) {
- $parserule_state = 3;
- }
-
- # Behandlung der Regeldetails
- if ( $parserule_state >= 3 && $parserule_state < 10 ) {
-
- #Einzelfaelle behandeln
- switch_rulepart: {
-
-# value in der gleichen Zeile
-# Pattern <=> Einzugtiefe, ':', einer der parameter, Leerzeichen, Klammerauf, Wert-wenn definiert, Klammerzu
- if (/^\t\t\t:(name|disabled|global_location|comments|header_text)\s\((.*)\)/)
- {
- $parserule_state = 4;
- $parserule_ruleparameter = $1;
- if ( defined($2) ) {
- my $parserule_ruleparameter_value = $2;
- $rulebases{"$parserule_rulebasename.$parserule_rulenum.$parserule_ruleparameter"} = $parserule_ruleparameter_value;
- if (( $parserule_ruleparameter eq 'comments' ) && ( $parserule_ruleparameter_value =~ m/\@A(\d+)\@/ ) )
- { # customer specific customizing
- $rulebases{"$parserule_rulebasename.$parserule_rulenum.rule_id"} = $1;
- }
- }
- last switch_rulepart;
- }
-
- # Value eine Zeile tiefer
- # Pattern <=> Einzugtiefe, ':', action, Leerzeichen, Klammerauf
- if (/^\t{3}:(action)\s\(/) {
- $parserule_state = 4;
- $parserule_ruleparameter = $1;
-
- #neue Zeile einlesen
- $_ = ;
- $line = $_;
- $last_line = $line;
- $ln++;
- s/\r\n/\n/g
- ; # handle cr,nl (d2u ohne Unterprogrammaufruf)
- # Pattern <=> bel. Leerzeichen, Klammerauf, value
- /\s*:\s\((.*)/;
-
- if ( defined($1) ) {
- my $parserule_ruleparameter_value = $1;
- $rulebases{"$parserule_rulebasename.$parserule_rulenum.$parserule_ruleparameter"} = $parserule_ruleparameter_value;
- }
- last switch_rulepart;
- }
-
- # Value ist eine Auflistung
- # Pattern <=> Einzugtiefe, ':', eine der Aktionen, Leerzeichen, Klammerauf
- if (/^\t{3}:(src|dst|services|install|track|time|through)\s\(/)
- {
- $parserule_state = 14;
- $parserule_ruleparameter = $1;
- last switch_rulepart;
- }
-
- # Value ist Rule AdminInfo
- # Pattern <=> Einzugtiefe, ':', "AdminInfo", Leerzeichen, Klammerauf
- if (/^\t{3}:(AdminInfo)\s\(/) {
- # Zeilen lesen, bis UID gefunden
- while () {
- $line = $_;
- $ln++;
- $last_line = $line;
- s/\r\n/\n/g
- ; # handle cr,nl (d2u ohne Unterprogrammaufruf)
- # Pattern <=> Einzugtiefe, ':', "chkpf_uid", Leerzeichen, Klammer auf, Doppeltes Hochkomma, geschw. Klammer auf, 'UID', geschw. Klammer zu, Doppeltes Hochkomma
- if (/^\t\t\t\t:chkpf_uid\s\(\"\{(.*)\}\"\)/) {
- my $rule_uid = &gen_uid($1);
-
-# folgenden Praefix eingefuegt wg. Bug bei CP (zwei Regeln eines Mgmts mit derselben UID)
- $rulebases{"$parserule_rulebasename.$parserule_rulenum.UID"} = $parserule_rulebasename . '__uid__' . $rule_uid;
- last;
- }
- if (/^\t{4}:ClassName\s\(security_header_rule\)/)
- {
- # ignore this rule except for :header_text
- $header_FLAG = 1;
- }
- }
- }
- last switch_rulepart;
- }
- }
- last SWITCH_parsemode;
- }
- }
- $prev_line = $line;
- }
-
- # Interpretation des Inputs ist abgeschlossen,
- # Aufraeumen dateihandle etc.
- #----------------------------------------
- #print "\t\t ... parsing abgeschlossen\n\n";
- close(IN);
- print_debug("$in_file closed.\n");
-
- # check auf Vollstaendigkeit des Config-Files:
- if ( $last_line =~ m/^\)(\n)?$/ ) { return 0; }
- else {
- return "last line of config-file $in_file not correct: <$last_line>";
- }
-}
-
-####################################
-# cp_parse_main
-# Main-Routine zum Parsen von CP-Configs
-# Ausgabe: CSV-Dateien im output-directory
-# param1: input-filename
-# param2: output-directory
-# param3: verbose_flag (0/1)
-####################################
-sub cp_parse_main {
- my $in_file = $_[0];
- my $out_dir = $_[1];
- my $rulesetname = $_[2];
- my $verbose = $_[3];
- my $mode;
-
-# Setzen des Parsermodes
-# umschalten des Betriebsmodus anhand der Dateiextension des uebergebenen Konfigurationsfiles
- if ( $in_file =~ /\.fws/ ) { # eine *.fws datei => Regelwerke
- $mode = 'rules';
- }
- elsif ( $in_file =~ /\_5_0.C/ ) { # eine _5_0.C Datei => Objekte
- $mode = 'objects';
- }
- else { # etwas unbekanntes
- die "unknown File extension in $in_file - exiting!\n";
- }
-
- # print_verbose ("using mode $mode\n");
- print "parsing config file: $in_file\n";
-
- # print ("in_file: $in_file, mode: $mode, rulsetname: $rulesetname\n");
- my $result = parse_cp2( $in_file, $mode, $rulesetname );
-
- if ($mode eq 'objects') {
- &cp_resolve_groups_with_exception();
- }
-
-# &print_results_monitor($mode);
- return $result;
-}
-
-sub cp_parse_users_main {
- my $user_db_file = shift;
- my $output_dir = shift;
- my $verbose = shift;
- my $result = 0;
- my $line = '';
-
- # first check if it is the original DB format or an exported version of the group and user data (using fw dbexport)
- if ( -T $user_db_file ) { # -T: is the file a text only file without binary chars?
- my $INFILE = new IO::File("< $user_db_file") or die "cannot open file $user_db_file\n";
- $line = <$INFILE>; $INFILE->close;
- if (defined($line)) {
- if ( $line =~ /^name;\tgroups;\tcolor;\tcomments;\tis_administrator_group;/ ) { # start of user group definitions dbexport file
- print "parsing users from csv dbexport: $user_db_file\n";
- &cp_parse_users_csv_db_exported($user_db_file, $output_dir, $verbose);
- }
- if ($line =~ /\<\?xml/) {
- print "parsing users from xml: $user_db_file\n";
- &cp_parse_users_xml_exported($user_db_file, $output_dir, $verbose);
- }
- }
- } else { # binary/original user file
- print "NOTICE: currently not parsing Check Point users from binary config file $user_db_file\n";
- # $result = &cp_parse_users_original_format( $user_db_file, $output_dir, $verbose );
- }
- &cp_parse_users_add_special_all_users_group();
- return $result;
-}
-
-sub cp_parse_users_add_special_all_users_group {
- my $name = 'All Users'; # adding special predefined user group "All Users" if it does not exist yet
- if (!defined($user{"$name.type"})) {
- push @user_ar, $name;
- $user{"$name.type"} = 'group';
- $user{"$name.uid"} = $name;
- $user{"$name.comments"} = 'special Check Point predefined usergroup containing all users';
- }
-}
-
-sub cp_parse_users_add_member_to_group {
- my $gruppe = shift;
- my $member = shift;
- my $debug = shift;
- my $oldmembers = "";
- my @group_ar = ();
-
- if ( defined( $usergroup{"$gruppe"} ) ) {
- $oldmembers = $usergroup{"$gruppe"} . $usergroupdelimiter;
- @group_ar = split /[$usergroupdelimiter]/, $oldmembers;
- }
- if ($debug) { print (" cp_parse_users_add_member_to_group:: found member $member for group $gruppe; members so far: $oldmembers\n"); }
- if ( !grep( /^${member}$/, @group_ar ) && $member ne $gruppe )
- { # doppelte Member und Rekursion vermeiden
- $usergroup{"$gruppe"} = $oldmembers . $member;
- }
- return;
-}
-
-sub cp_parse_users_add_groupmembers_final {
- # bei CP sind Namen und UIDs der User identisch, da uid (derzeit) nicht aus fwauth.NDB ausgelesen werden kann
- foreach my $gruppe ( keys %usergroup ) {
- $user{"$gruppe.member_refs"} = $usergroup{"$gruppe"};
- $user{"$gruppe.members"} = $usergroup{"$gruppe"};
- $user{"$gruppe.type"} = "group";
- if ( !grep( /^${gruppe}$/, @user_ar ) ) {
- push @user_ar, $gruppe;
- }
- }
- return;
-}
-
-sub cp_parse_users_xml_exported {
-
-=cut
-syntax start: xxx
-userdefinition:
-
-xxx
-user|user_group|external_group
-user_group: user-in-group
-xxx
-
-
-ende:
-=cut
-
- my $in_file_main = shift;
- my $fworch_workdir = shift;
- my $debuglevel_main = shift;
- my $line = '';
- my $last_line = '';
- my $parse_error = 0;
- my $line_no = 0;
- my $INFILE = new IO::File("< $in_file_main") or die "cannot open file $in_file_main\n";
- my $debug_lines = '';
- my $debug = 0;
- my $start = 1;
- my ($name, $type, $color, $comments, $is_admin, $uid, $expdate, $group_string, $rest_of_line);
-
- LINE: while ( $line = <$INFILE> ) {
- $last_line = $line;
- $line_no++;
- if ($start && $line !~ /\/) { next LINE; } else { $start = 0; }
- if (!$start && $line =~ /\/) {
- if ( $line =~ /\\(.+?)\<\/Name\>\(.+?)\<\/Class\_Name\>(.+)$/ ) { # start of user definition
- $name = $1; $type = $2; $uid = $name; $rest_of_line = $3;
- } else {
- print ("debug: no match in line: $line\n");
- }
- }
- if ($line =~ /\\s*(.*?)\s*<\/groups\>\s*\/ ) { # group membership definition
- my $group_string = $1;
- while ($group_string ne '') {
-# print ("DEBUG: group_string=$group_string\n");
- if ($group_string =~ /^\\s*\\s*(.+?)\s*\<\/Name\>\s*\\s*users\s*\<\/Table\>\s*\<\/groups\>(.*)/ ) { # group membership definition
- my $group = $1;
- $group_string = $2;
- &cp_parse_users_add_member_to_group ($group, $name, 0);
-# print ("DEBUG: found group for user $name=$group\n");
- } else {
-# print ("DEBUG warning: found no match in group_string $group_string - would result in endless looping\n");
- $group_string = '';
- }
- }
- }
- if ($line =~ /\<\/user\>/) { # end of user definition
- if ($type eq 'external_group' || $type eq 'user_group') { $type = 'group'; }
- if ($type eq 'user') { $type = 'simple'; }
- if ($type eq'simple' || $type eq 'group') {
- push @user_ar, $name;
- $user{"$name.type"} = $type;
- $user{"$name.uid"} = $uid;
- if ( defined($color) ) { $user{"$name.color"} = $color; }
- if ( defined($comments) ) { $user{"$name.comments"} = $comments; }
- if ( defined($expdate) ) { $user{"$name.expdate"} = $expdate; }
- }
- undef($name); undef($type); undef($color); undef($comments); undef($is_admin); undef($uid); undef($expdate); undef($group_string);
- }
- }
- $INFILE->close;
- &cp_parse_users_add_groupmembers_final;
- # check auf Vollstaendigkeit des Config-Files:
- if ( $last_line =~ /\<\/users\>/ && !$parse_error) { return 0; }
- else {
- print ("error while parsing exported userdata, last_line: <$last_line>");
- return "error while parsing exported userdata, last_line: <$last_line>";
- }
-}
-
-sub cp_parse_users_csv_db_exported {
-
-=cut
-
-limitations:
-
-fw dbexport of user data does not export IDs (UIDs) so the UID field will be filled with the user name
-
-syntax groups:
-
-name; groups; color; comments; is_administrator_group;
-group1; ; red; ; ;
-group2; ; red; ; ;
-group3; ; red; Kommentar3; ;
-group4; ; orange; Kommentar4; ;
-
-
-syntax users:
-
-name; color; groups; destinations; sources; auth_method; fromhour; tohour; expiration_date; days; accept_track; internal_password; SKEY_seed; SKEY_number; SKEY_passwd; SKEY_gateway; comments; radius_server; vlan_auth_list; userc; expire; isakmp.transform; isakmp.data.integrity; isakmp.encryption; isakmp.methods; isakmp.encmethods; isakmp.hashmethods; isakmp.authmethods; isakmp.shared.secret; tacacs_server; SKEY_method; administrator; administrator_profile;
-user1; red; {group1,group4}; {Any}; {Any}; Internal Password; 00:00; 23:59; 20-Sep-1999; {MON,TUE,WED,THU,FRI,SAT,SUN}; None; 5bV0ZOoq921Bs; ; ; ; ; Kommentar1; Any; {}; {DES,DES,MD5}; 60; ; ; ; ; ; ; ; ; Any; ; false; ;
-user2; orange; {group2,group3,group1}; {Any}; {Any}; Internal Password; 00:00; 23:59; 15-feb-2001; {MON,TUE,WED,THU,FRI,SAT,SUN}; AuthAlert; nb.NZXrB2qyxA; ; ; ; ; Testuser; Any; {}; {,,None}; ; ESP; SHA1; 3DES; ; {DES,3DES}; {MD5,SHA1}; {signatures}; ; Any; ; false; ;
-user3; orange; {group2}; {Any}; {Any}; Internal Password; 08:00; 18:00; 27-Sep-1999; {MON,TUE,WED,THU,FRI,SAT,SUN}; None; 7b.SEuf5N.YV2; ; ; ; ; Testkennung f. acde; Any; {}; {DES,DES,MD5}; 60; ; ; ; ; ; ; ; ; Any; ; false; ;
-
-=cut
-
- my $in_file_main = shift;
- my $fworch_workdir = shift;
- my $debuglevel_main = shift;
- my $line = '';
- my $last_line = '';
- my $parse_error = 0;
- my $line_no = 0;
- my $INFILE = new IO::File("< $in_file_main") or die "cannot open file $in_file_main\n";
- my $debug_lines = '';
- my $debug = 0;
- my $type;
-
- while ( $line = <$INFILE> ) {
- $line_no++;
- if ( $line =~ /^name;\tgroups;\tcolor;\tcomments;\tis_administrator_group;/ ) { # start of user group definitions
- $type = 'group';
- } elsif ( $line =~ /^name;\tcolor;\tgroups;\tdestinations;\tsources;/ ) { # start of user definitions
- $type = 'simple';
- } else { # no header - plain user or group data from here on
- my $name;
- my $color;
- my $comments;
- my $is_admin;
- my $uid;
- my $expdate;
- my $group_string;
-
- if ($type eq 'group' && $line =~ /^(.*?);\t(.*?);\t(.*?);\t(.*?);\t(.*?);/ ) {
- $name = $1;
- $group_string = $2;
- $color = lc($3);
- $comments = $4;
- } elsif ($type eq 'simple' && $line =~ /^(.*?);\t(.*?);\t(.*?);\t(.*?);\t(.*?)\t(.*?);\t(.*?);\t(.*?);\t(.*?);\t(.*?);\t(.*?);\t(.*?);\t(.*?);\t(.*?);\t(.*?);\t(.*?);\t(.*?);\t(.*?);\t(.*?);\t(.*?);\t(.*?);\t(.*?);\t(.*?);\t(.*?);\t(.*?);\t(.*?);\t(.*?);\t(.*?);\t(.*?);\t(.*?);\t(.*?);\t(.*?);\t(.*?);/ ) {
- $name = $1;
- $color = lc($2);
- $group_string = $3;
- $comments = $17;
- $expdate = &convert_checkpoint_to_db_date($9);
- } else { # line not parsable
- $parse_error = 1;
- print (" $line_no: this line was not parsable: $line\n");
- }
- $uid = $name;
- if ($group_string ne '') {
- my $group_string_without_brackets;
- if ( $group_string =~ /\{(.*?)\}/ ) { $group_string_without_brackets = $1; }
- my @groups_this_user_belongs_to_ar = split /,/, $group_string_without_brackets;
- foreach my $gruppe (@groups_this_user_belongs_to_ar) {
-# print (" adding user $name to group $gruppe\n");
- if ( $gruppe ne 'ReferenceObject' ) {
-# TODO: Fehler fuer toten Link ausgeben
- &cp_parse_users_add_member_to_group ($gruppe, $name, $debug);
- }
- }
- }
- push @user_ar, $name;
-# print (" $line_no: adding new user $name\n");
- $user{"$name.type"} = $type;
- $user{"$name.uid"} = $uid;
- if ( defined($color) ) { $user{"$name.color"} = $color; }
- if ( defined($comments) ) { $user{"$name.comments"} = $comments; }
- if ( defined($expdate) ) { $user{"$name.expdate"} = $expdate; }
- }
- $last_line = $line;
- }
- $INFILE->close;
- &cp_parse_users_add_groupmembers_final;
- # check auf Vollstaendigkeit des Config-Files:
- if ( $last_line =~ m/^.+?\;(.*?\;){31}/ && !$parse_error) { return 0; }
- else {
- print ("error while parsing exported userdata, last_line: <$last_line>");
- return "error while parsing exported userdata, last_line: <$last_line>";
- }
-}
-# parse user groups that are externally defined (e.g. LDAP) from rules in rulebase file
-# does not work for individual users - all these groups will be empty
-
-sub cp_parse_users_from_rulebase { # ($rulebase_file)
- my $in_file_main = shift;
- my $line = '';
- my $last_line;
- my $INFILE = new IO::File("< $in_file_main") or die "cannot open file $in_file_main\n";
- my $type;
- my $name;
- my $comments;
- my $uid;
-
- print "parsing users from rulebase file: $in_file_main\n";
- LINE: while ( $line = <$INFILE> ) {
- chomp($line);
- if ($line =~ /^\t\t\t\t\t\: \(\"?(.+?)\@.+?$/) { # externally defined (e.g. ldap) user groups
- $name = $1;
- $comments = "";
- $type = 'group';
- $uid = $name;
- if (!defined($user{"$name.type"})) {
- push @user_ar, $name;
- $user{"$name.type"} = 'group';
- $user{"$name.uid"} = $name;
- $user{"$name.comments"} = '';
- }
- }
- if ($line =~ /\t+\:Table \(identity\_roles\)$/) { # identity awareness user groups
- if ($last_line =~ /\t+\:Name \((.+)\)/) {
- $name = $1;
- $comments = "identity awareness user group";
- $type = 'group';
- $line = <$INFILE>;
- if ($line =~ /\t+\:Uid \(\"\{(.+)\}\"\)$/) {
- $uid = $1;
- if (!defined($user{"$name.type"})) {
- push @user_ar, $name;
- $user{"$name.type"} = 'group';
-# $user{"$name.uid"} = $uid;
- $user{"$name.uid"} = $name;
- $user{"$name.comments"} = $comments;
- }
- }
- }
- }
- $last_line = $line;
- }
- $INFILE->close;
- return 0;
-}
-
-sub cp_parse_users_original_format {
- sub cp_parse_users_remove_nonprintable_chars {
- sub cp_parse_user_remove_bin_data {
- my $in_file_main = shift;
- my $out_file_main = shift;
- my $line = '';
-
- my $INFILE = new IO::File("< $in_file_main") or die "cannot open file $in_file_main\n";
- binmode($INFILE);
- my $OUTFILE = new IO::File("> $out_file_main") or die "cannot open file $out_file_main\n";
- binmode($OUTFILE);
-# while ( sysread( $INFILE, $line, 256 ) ) { print $OUTFILE substr( $line, 3 ); }
- while ( sysread( $INFILE, $line, 256 ) ) { print $OUTFILE substr( $line, 0 ); }
- $INFILE->close; $OUTFILE->close;
- return;
- }
-
- my $in_file = shift;
- my $out_file = shift;
- my $line = '';
- my $orig_line = '';
-
- &cp_parse_user_remove_bin_data( $in_file, "${in_file}_non_bin" );
-# my $INFILE = new IO::File("< ${in_file}")
- my $INFILE = new IO::File("< ${in_file}_non_bin")
- or die "cannot open file $in_file\n";
- my $OUTFILE = new IO::File("> $out_file")
- or die "cannot open file $in_file\n";
- binmode($INFILE);
-
- while ( $line = <$INFILE> ) {
- $orig_line = $line;
- $line =~ s/\n/%EOL%/g; # preserve important non-printable chars
- $line =~ s/\t/%TAB%/g;
- if ( $line =~ /[^[:print:]]/ )
- { # replace all special characters with EOL
- $line =~ s/[^[:print:]]+/%EOL%%EOL%/g; # zwei EOL, um eine leere Zeile zu erhalten
- }
- $line =~ s/%EOL%/\n/g;
- $line =~ s/%TAB%/\t/g;
- print $OUTFILE $line;
- }
- $INFILE->close;
- $OUTFILE->close;
- }
-
- my $in_file_main = shift;
- my $fworch_workdir = shift;
- my $debuglevel_main = shift;
- my $line = '';
- my $name;
- my $type;
- my $color;
- my $comments;
- my $uid;
- my $last_change_admin;
- my $expdate;
- my $user_def_type = 'simple';
- my $line_no = 0;
- my $userparse_state = 0; # 0 = no user, 1 = in user definition, 2 = in group definition
- my $debug_lines = '';
- my $debug = 0;
- my $printable_usr_file = $in_file_main . '_printable';
-
- &cp_parse_users_remove_nonprintable_chars( $in_file_main, $printable_usr_file );
- my $INFILE = new IO::File("< $printable_usr_file") or die "cannot open file $printable_usr_file\n";
-
- while ( $line = <$INFILE> ) {
- $line_no++;
- if ( $line =~ /^([\w0-9\-_\s\.]+)\(\"?\1\"?$/ ) { # only take lines "username(username" as user definitions
- $userparse_state = 1; # found start of user definition (username)
- undef($type); undef($color); undef($uid); undef($comments); undef($last_change_admin); undef($expdate);
- $name = $1;
- if ($debug) { print ("$line_no: processing user $name\n"); }
- }
- if ( $userparse_state && ($name =~ /[0-9a-fA-F]{16}/ || $name =~ /[0-9a-fA-F]{24}/ || $name eq 'ALL_EXTUSRGROUPS' || $name eq 'ALL_TEMPLATES' ||
- $name eq 'ALL_GROUPS' || $name eq 'GROUPS_AND_EXTGROUPS' || $name eq 'ALL_KEYH' || $name eq 'Default' ))
- { # no real user
- $userparse_state = 0; undef($name);
- }
- if ( $userparse_state && $line =~ /^\)?$/ ) { # end of user def (empty lines as well as closing brackets)
- if (defined($type) && defined($name) && $type ne 'rsacacertkey' && $type ne 'keyh' && $type ne 'template' )
- { # wenn Benutzer noch nicht existiert: anlegen
- if ( !grep( /^${name}$/, @user_ar ) ) {
- push @user_ar, $name;
- if ($debug) { print (" $line_no: adding new user $name\n") }
- }
- $user{"$name.type"} = $type;
- if ( defined($color) ) { $user{"$name.color"} = $color; }
- if ( defined($comments) ) { $user{"$name.comments"} = $comments; }
- $user{"$name.uid"} = $name; # nicht schoen, aber in den CP-Regeln sind fuer die User keine Uids!?, daher dient der Username als UID
- if ( defined($last_change_admin) ) { $user{"$name.last_change_admin"} = $last_change_admin; }
- if ( defined($expdate) ) { $user{"$name.expdate"} = $expdate; }
- }
- else {
- if ($debug) { print ("undefined type; final line: $line; name: $name, type: $type.\n"); }
- }
- $userparse_state = 0;
- }
- if ( $userparse_state && ($line =~ /^\t\t:ClassName\s\(([\w0-9\-_\.]+?)\)/ || $line =~ /^\t:type \((\w+)\)$/ ) )
- {
- if ($1 eq 'template' || $1 eq 'user_template') { # ignore all template users
- $userparse_state = 0;
- }
- if (defined($type)) {
- if ($debug) { print (" $line_no :: found type $1, but type already defined as $type, line: $line"); }
- } else {
- $type = $1;
- if ($debug) { print (" $line_no :: found type $type, line: $line"); }
- if ($type eq 'usrgroup' || $type eq 'extusrgroup' || $type eq 'external_group')
- { $type = 'group'; }
- else { $type = 'simple'; }
- }
- }
- if ( $userparse_state) { $debug_lines .= $line_no . $line; }
- if ( $userparse_state && $line =~ /^\t\t:chkpf_uid \("\{([\w0-9\-_\.]+?)\}"\)$/ ) { $uid = &gen_uid($1); }
- if ( $userparse_state && $line =~ /^\t\t\t:By \(([\w0-9\-_\.]+)\)$/ ) { $last_change_admin = $1; }
- if ( $userparse_state && $line =~ /^\t:Uid \("\{([\w0-9\-_\.]+?)\}"\)$/ ) { $uid = &gen_uid($1); }
-
-# usergroup processing
- if ( $line =~ /:groups/ ) { # start of a group statement
- if ($debug) { print (" start of group statement in $line_no, userparse_state = $userparse_state: $line"); }
- }
- if ( $userparse_state && $line =~ /^\t:groups \(\)$/ ) { # no group memberships
- if ($debug) { print (" $line_no: user $name does not belong to any groups: $line"); }
- }
- if ( $userparse_state && $line =~ /^\t\:groups \($/ ) {
- $userparse_state = 2;
- if ($debug) { print (" $line_no: user $name belongs to groups: "); }
- }
- if ( $userparse_state == 2 && ($line =~ /^$/ || $line =~ /^\t\t\)$/) ) # || $line =~ /^\t\t:\s\(/ || $line =~ /^\t?\)/ ))
- { # (premature) end of group-statement
- $userparse_state = 1;
- }
- if ( $userparse_state == 2 && $line =~ /^\s+: \(([\w0-9\-_\.]+)$/ ) {
- if ($debug) { print (" $line_no: entering cp_parse_users_add_member_to_group, line: $line"); }
- &cp_parse_users_add_member_to_group( $1, $name, $debug );
- }
- }
- $INFILE->close;
- if ($debug) { print $debug_lines; }
- &cp_parse_users_add_groupmembers_final;
-}
-
-sub cp_resolve_groups_with_exception {
- sub is_group {
- my $nwobj = shift;
- if (defined ($network_objects{"$nwobj.type"})) {
- return ($network_objects{"$nwobj.type"} eq 'group');
- } else {
- print ("WARNING: type of $nwobj not defined\n");
- return 0;
- }
- }
- sub is_non_empty_group {
- my $nwobj = shift;
- if (is_group($nwobj) && defined($network_objects{"$nwobj.members"}) && $network_objects{"$nwobj.members"} ne '') {
- return 1;
- } else {
- return 0;
- }
- }
- sub is_group_with_exclusion {
- my $nwobj = shift;
- return ($network_objects{"$nwobj.type"} eq 'group_with_exclusion');
- }
- sub is_simple_ip {
- my $obj_in = shift;
- return ($network_objects{"$obj_in.type"} eq 'host' || $network_objects{"$obj_in.type"} eq 'gateway');
- }
- sub is_network {
- my $obj_in = shift;
- return ($network_objects{"$obj_in.type"} eq 'network');
- }
- sub is_ip_range {
- my $obj_in = shift;
- return ($network_objects{"$obj_in.type"} eq 'machines_range');
- }
- sub to_cidr {
- my $obj_in = shift;
- my $obj;
- if (is_network($obj_in)) {
- $obj = Net::CIDR::addrandmask2cidr($network_objects{"$obj_in.ipaddr"}, $network_objects{"$obj_in.netmask"});
- } elsif (is_simple_ip($obj_in)) {
- $obj = $network_objects{"$obj_in.ipaddr"} . "/32";
- } elsif (is_ip_range($obj_in)) {
- $obj = $network_objects{"$obj_in.ipaddr"} . "-" . $network_objects{"$obj_in.ipaddr_last"} ;
- } else {
- print ("WARNING: $obj_in is neither network nor host nor range, but " . $network_objects{"$obj_in.type"}. " - empty?\n");
- if (defined($network_objects{"$obj_in.ipaddr"})) {
- $obj = $network_objects{"$obj_in.ipaddr"} . "/32";
- } else {
- undef $obj;
- }
- }
- return $obj;
- }
- sub list_to_cidr {
- my $obj_ar_in = shift; # ref to array
- my @obj_ar_out = ();
-
- foreach my $obj (@{$obj_ar_in}) {
- my $obj_out = &to_cidr($obj);
- if (defined($obj_out)) {
- @obj_ar_out = (@obj_ar_out, $obj_out);
- }
- }
- return @obj_ar_out;
- }
- sub ip_overlaps_with_list {
- my $obj1_in = shift;
- my $obj_array_ref_in = shift;
-
- my $obj1 = &to_cidr($obj1_in);
- my @cidr_array = ();
- foreach my $obj (@{$obj_array_ref_in}) {
- @cidr_array = (@cidr_array, &to_cidr($obj));
- }
- my $result = &Net::CIDR::cidrlookup($obj1, @cidr_array);
- return ($result);
- }
- sub ips_overlap {
- my $obj1_in = shift;
- my $obj2_in = shift;
-
- my $obj1 = &to_cidr($obj1_in);
- my $obj2 = &to_cidr($obj2_in);
- my $result = &Net::CIDR::cidrlookup($obj1, $obj2);
- return ($result);
- }
- sub flatten {
- my $group_in = shift;
- if (defined($group_in) && defined($network_objects{"$group_in.members"}) && defined($network_objects{"$group_in.member_refs"})) {
- my $member_string = $network_objects{"$group_in.members"};
- my $member_ref_string = $network_objects{"$group_in.member_refs"};
- my @member_ar = split (/[$GROUPSEP]/, $member_string);
- my @member_ref_ar = split (/[$GROUPSEP]/, $member_ref_string);
- my $i = 0;
- while ($i < scalar @member_ar) {
- my $m = $member_ar[$i];
- if (&is_non_empty_group ($m) && defined($m) && defined($GROUPSEP) && defined($network_objects{"$m.members"})) {
- splice(@member_ar, $i, 1, split(/[$GROUPSEP]/, $network_objects{"$m.members"}));
- $member_string = join ("$GROUPSEP", @member_ar);
- splice(@member_ref_ar,$i,1, split(/[$GROUPSEP]/, $network_objects{"$m.member_refs"}));
- $member_ref_string = join ("$GROUPSEP", @member_ref_ar);
- } else {
- $i++;
- }
- @member_ar = split (/[$GROUPSEP]/, $member_string);
- @member_ref_ar = split (/[$GROUPSEP]/, $member_ref_string);
- }
- $network_objects{"$group_in.members"} = $member_string;
- $network_objects{"$group_in.member_refs"} = $member_ref_string;
- # print ("flatten_result: $member_string\n");
- }
- return;
- }
- sub reduce_ip_by_one {
- my $ip_in = shift;
- my $result;
-
- if ($ip_in =~ /^0.0.0.0$/) { return undef; }
- if ($ip_in =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/ ) {
- if ($4 ne '0') { $result = "$1.$2.$3." . ($4-1);
- } elsif ($3 ne '0') { $result = "$1.$2." . ($3-1) . ".255";
- } elsif ($2 ne '0') { $result = "$1." . ($2-1) . ".255.255";
- } else { $result = ($1-1) . ".255.255.255";
- }
- return $result;
- } else {
- print ("WARNING: ip not well-formed: $ip_in\n");
- }
- }
- sub increase_ip_by_one {
- my $ip_in = shift;
- my $result;
-
- if ($ip_in =~ /^255.255.255.255$/) { return undef; }
- if ($ip_in =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/ ) {
- if ($4 ne '255') { $result = "$1.$2.$3." . ($4+1);
- } elsif ($3 ne '255') { $result = "$1.$2." . ($3+1) . ".0";
- } elsif ($2 ne '255') { $result = "$1." . ($2+1) . ".0.0";
- } else { $result = ($1+1) . ".0.0.0";
- }
- return $result;
- } else {
- print ("WARNING: ip not well-formed: $ip_in\n");
- }
- }
- sub pad_ip { # used only for correct sorting
- my $ip = shift;
- my $result;
- if ($ip =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/ ) {
- $result = sprintf("%03d.%03d.%03d.%03d", $1, $2, $3, $4);
- } else {
- print ("WARNING: ip not well-formed: $ip\n");
- return undef;
- }
- return $result;
- }
- sub calculate_non_overlapping_group {
- my $pos_ip = shift;
- my $neg_ip = shift;
- my $group_name = shift;
-
- my ($pos_ip_range) = Net::CIDR::cidr2range($pos_ip);
- my ($neg_ip_range) = Net::CIDR::cidr2range($neg_ip);
- my ($pos_start, $pos_end) = split(/\-/, $pos_ip_range);
- my ($neg_start, $neg_end) = split(/\-/, $neg_ip_range);
- my @new_objs;
-
- if (&pad_ip($pos_start) lt &pad_ip($neg_start) && &pad_ip($pos_end) gt &pad_ip($neg_end)) {
- # neg is fully contained in pos
- # pos: 1.0.0.0-1.1.255.255, neg 1.1.2.3-1.1.2.3
- @new_objs = Net::CIDR::range2cidr(split(/,/, "$pos_start-" .
- &reduce_ip_by_one($neg_start) .
- "," . &increase_ip_by_one($neg_end) . "-" . $pos_end));
- } elsif (&pad_ip($neg_start) le &pad_ip($pos_start) && &pad_ip($neg_end) lt &pad_ip($pos_end)) {
- # neg cuts off the beginning of pos
- # pos: 1.6.0.0-1.7.255.255, neg 1.5.255.254-1.6.0.2
- @new_objs = Net::CIDR::range2cidr(&increase_ip_by_one($neg_end) . "-$pos_end");
- } elsif (&pad_ip($pos_start) lt &pad_ip($neg_start) && &pad_ip($neg_end) ge &pad_ip($pos_end)) {
- # neg cuts off the end of pos --> pos = old_pos_start to start_of_neg - 1
- # pos: 1.6.0.0-1.7.255.255, neg 1.7.255.254-1.8.0.2
- @new_objs = Net::CIDR::range2cidr("$pos_start-" . &reduce_ip_by_one($neg_start));
- } else {
- # neg fully contains pos --> result is empty, do nothing
- @new_objs = ();
- }
- return join ("$GROUPSEP", @new_objs);
- }
- sub obj_is_created {
- my $cidr_obj = shift;
-
- # currently we do not keep any original objects in pos group (assuming only object "any" in pos group anyway)
- return 1;
- }
- sub create_new_obj {
- my $new_obj = shift;
- my $group_name = shift;
- my $obj_name = "${group_name}_${new_obj}_isogrpexcl";
- my $obj_ref = "$obj_name.ref";
-
- @network_objects = (@network_objects, $obj_name);
- $network_objects{"$obj_name.name"} = $obj_name;
- $network_objects{"$obj_name.UID"} = $obj_ref;
-
- if ($new_obj =~ /\// && $new_obj !~ /\/32$/ ) { # network
- $network_objects{"$obj_name.type"} = 'network';
- $network_objects{"$obj_name.ipaddr"} = $new_obj;
- } elsif ($new_obj =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/ || $new_obj =~ /\/32$/) { # simple host
- $network_objects{"$obj_name.type"} = 'host';
- $network_objects{"$obj_name.ipaddr"} = $new_obj;
- } elsif ($new_obj =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)-(\d+)\.(\d+)\.(\d+)\.(\d+)$/) { # range
- $network_objects{"$obj_name.type"} = 'machines_range';
- print ("WARNING: $new_obj is range, but this should never occur.\n");
- } else {
- print ("WARNING: $new_obj is neither network nor host nor range.\n");
- }
- return ($obj_name, $obj_ref);
- }
- sub add_obj_to_group {
- my $obj_name = shift;
- my $obj_ref = shift;
- my $group_name = shift;
-
- if (defined($network_objects{"$group_name.members"}) && $network_objects{"$group_name.members"} ne '') {
- $network_objects{"$group_name.members"} .= "${GROUPSEP}$obj_name";
- $network_objects{"$group_name.member_refs"} .= "${GROUPSEP}$obj_ref";
- } else {
- $network_objects{"$group_name.members"} = $obj_name;
- $network_objects{"$group_name.member_refs"} = $obj_ref;
- }
- }
- sub remove_obj_from_group {
- my $obj_name = shift;
- my $group_name = shift;
-
- my $idx = 0;
- my @member_ar = split (/[$GROUPSEP]/, $network_objects{"$group_name.members"});
- my @member_ref_ar = split (/[$GROUPSEP]/, $network_objects{"$group_name.member_refs"});
- while ($idx < scalar (@member_ar)) {
- if ($member_ar[$idx] eq $obj_name) {
- splice (@member_ar, $idx, 1);
- splice (@member_ref_ar, $idx, 1);
- }
- $idx++;
- }
- $network_objects{"$group_name.members"} = join ("$GROUPSEP", @member_ar);
- $network_objects{"$group_name.member_refs"} = join ("$GROUPSEP", @member_ref_ar);
- }
- sub is_obj_the_any_obj {
- my $obj_name_in = shift;
- return (lc($network_objects{"$obj_name_in.name"}) eq 'any');
- }
-
- foreach my $nwobj ( @network_objects ) {
- if (&is_group_with_exclusion($nwobj)) {
-# print ("processing group_with_exclusion: $nwobj\n");
- my $members = $network_objects{"$nwobj.members"};;
-
- # remove pos_group from $nwobj and add its content instead
- my ($positiv_gruppe, $negativ_gruppe, $pos_idx, $neg_idx, $pos_grp_ref, $neg_grp_ref);
- my $idx = 0;
- foreach my $member (split (/[$GROUPSEP]/, $network_objects{"$nwobj.members"})) {
- if ($member =~ /^\{base\}(.+)$/) { $positiv_gruppe = $1; $pos_idx = $idx; }
- if ($member =~ /^\{exception\}(.+)$/) { $negativ_gruppe = $1; $neg_idx = $idx; }
- }
- my @pos_member_list;
- if (is_obj_the_any_obj($positiv_gruppe)) {
- @pos_member_list = ("$positiv_gruppe");
- } else {
- &flatten($positiv_gruppe); # flatten = untergruppen aufloesen
- @pos_member_list = split (/[$GROUPSEP]/, $network_objects{"$positiv_gruppe.members"});
- }
- &flatten($negativ_gruppe);
- my @neg_member_list;
- if (defined($negativ_gruppe) && defined($network_objects{"$negativ_gruppe.members"})) {
- @neg_member_list = split (/[$GROUPSEP]/, $network_objects{"$negativ_gruppe.members"});
- } else {
- @neg_member_list = ();
- }
- # convert all list members to cidr notation
- @pos_member_list = &list_to_cidr(\@pos_member_list);
- @neg_member_list = &list_to_cidr(\@neg_member_list);
-
- # ab hier ist @pos_member_list massgeblich (keine Veraenderung von $network_objects)
- foreach my $negmember (@neg_member_list) {
- if (&Net::CIDR::cidrlookup($negmember, @pos_member_list)) { # overlapping?
- # am Ende dieses ifs wird die Liste @pos_member_list komplett neu definiert fuer den naechsten Schleifendurchlauf
- my $i = 0;
- while ($i < scalar @pos_member_list) {
- my $posmember = $pos_member_list[$i];
- if (&Net::CIDR::cidrlookup ($negmember, $posmember)) {
- my $new_obj_str = &calculate_non_overlapping_group ($posmember, $negmember);
- my @new_objs = split (/[$GROUPSEP]/, $new_obj_str);
- splice(@pos_member_list, $i, 1, @new_objs);
- # for optimizing only:
- $i += scalar @new_objs;
- } else {
- $i++;
- }
- }
- }
- }
- # pos gruppe normieren/zusammenfassen/vereinfachen
- my @new_non_redundant_objects = ();
- foreach my $new_obj (@pos_member_list) {
- @new_non_redundant_objects = Net::CIDR::cidradd($new_obj, @new_non_redundant_objects);
- }
- # jetzt werden die $network_objects wieder angefasst
- # redefine members of group to empty group
- $network_objects{"$nwobj.members"} = '';
- $network_objects{"$nwobj.member_refs"} = '';
- foreach my $obj (@new_non_redundant_objects) {
- my ($obj_name, $obj_ref);
- if (obj_is_created($obj)) {
- ($obj_name, $obj_ref) = &create_new_obj ($obj, $nwobj);
- } else { #
-# ($obj_name, $obj_ref) = lookup_nwobj_data ($obj);
- }
- &add_obj_to_group ($obj_name, $obj_ref, $nwobj);
- }
- }
- }
- return;
-}
-
-1;
-
-__END__
-
-=head1 NAME
-
-CACTUS::FWORCH::parser - Perl extension for fworch check point parser
-
-=head1 SYNOPSIS
-
- use CACTUS::FWORCH::import::checkpoint;
-
-=head1 DESCRIPTION
-
-IT Security Organizer Perl Module
-support for importing configs into fworch Database
-
-=head2 EXPORT
-
- global variables
-
-=head1 SEE ALSO
-
- behind the door
-
-=head1 AUTHOR
-
- Cactus eSecurity, tmp@cactus.de
-
-=cut
diff --git a/roles/importer/files/importer/CACTUS/FWORCH/import/checkpointR8x.pm b/roles/importer/files/importer/CACTUS/FWORCH/import/checkpointR8x.pm
deleted file mode 100644
index 05a632bea6..0000000000
--- a/roles/importer/files/importer/CACTUS/FWORCH/import/checkpointR8x.pm
+++ /dev/null
@@ -1,205 +0,0 @@
-package CACTUS::FWORCH::import::parser;
-
-use strict;
-use warnings;
-use IO::File;
-use Getopt::Long;
-use File::Basename;
-use CACTUS::FWORCH;
-use CACTUS::FWORCH::import;
-
-require Exporter;
-our @ISA = qw(Exporter);
-
-our %EXPORT_TAGS = ( 'basic' => [ qw( ©_config_from_mgm_to_iso &parse_config ) ] );
-
-our @EXPORT = ( @{ $EXPORT_TAGS{'basic'} } );
-our $VERSION = '0.3';
-
-#####################################################################################
-# Start Check Point Parser
-#####################################################################################
-
-sub parse_config {
- my $object_file = shift;
- my $rulebase_file = shift;
- my $user_db_file = shift;
- my $rulebase_name = shift;
- my $output_dir = shift;
- my $verbose = shift;
- my $mgm_name = shift;
- my $config_dir = shift;
- my $import_id = shift;
- my $audit_log_file= shift;
- my $prev_import_time= shift;
- my $parse_full_audit_log = shift;
- my $debug_level = shift;
- my $result;
- my $cmd;
- my $return_code = 0;
- # my $parser_py = "/usr/bin/python3 ./fworch_parse_config_cp_r8x_api.py";
- my $parser_py = "/usr/bin/python3 ./checkpointR8x/parse_config.py";
- my $users_csv = "$output_dir/${mgm_name}_users.csv";
- my $users_delimiter = "%"; # value is defined in parser_py = ./fworch_parse_config_cp_r8x_api.py !!!
-
-
-# parsing rulebases
- my $local_rulebase_names = get_local_ruleset_name_list($rulebase_name);
- my $global_rulebase_names = get_global_ruleset_name_list($rulebase_name);
- my @local_rulebase_name_ar = split /,/, $local_rulebase_names;
- my @global_rulebase_name_ar = split /,/, $global_rulebase_names;
- my $rulebase_with_slash;
- my $rulebase_name_sanitized;
- for (my $i=0; $i \"$output_dir/${rulebase_name_sanitized}_rulebase.csv\"";
-# print("DEBUG - cmd = $cmd\n");
- $return_code = system($cmd);
- if ( $return_code != 0 ) { print("ERROR in parse_config found: $return_code\n") }
-
- }
-# foreach my $rulebase (@local_rulebase_name_ar) {
-# my $rulebase_name_sanitized = join('__', split /\//, $rulebase);
-# $cmd = "$parser_py -m $mgm_name -i $import_id -r \"$rulebase\" -f \"$object_file\" -d $debug_level > \"$output_dir/${rulebase_name_sanitized}_rulebase.csv\"";
-# # print("DEBUG - cmd = $cmd\n");
-# $return_code = system($cmd);
-# if ( $return_code != 0 ) { print("ERROR in parse_config found: $return_code\n") }
-# }
-# parsing users
- $cmd = "$parser_py -m $mgm_name -i $import_id -u -f \"$object_file\" -d $debug_level > \"$output_dir/${mgm_name}_users.csv\"";
-# print("DEBUG - cmd = $cmd\n");
- $return_code = system($cmd);
- # system("ls -l $output_dir");
- if ( $return_code != 0 ) { print("ERROR in parse_config::users found: $return_code\n") }
-
- # in case of no users being returned, remove users_csv file
- if (-r $users_csv) {
- my $empty_flag = 0;
- open FH, $users_csv;
- my $firstline = ;
- if (defined($firstline)) {
- # print ("firstline=$firstline###\n");
- if(index($firstline,$users_delimiter)==-1) {
- #print ("test: empty_flag=$empty_flag\n");
- $empty_flag = 1;
- }
- }
- close FH;
- if ($empty_flag == 1){
- # print ("unlinking users_csv file $users_csv\n");
- unlink $users_csv;
- }
- }
-
-# parsing svc objects
- $cmd = "$parser_py -m $mgm_name -i $import_id -s -f \"$object_file\" -d $debug_level > \"$output_dir/${mgm_name}_services.csv\"";
-# print("DEBUG - cmd = $cmd\n");
- $return_code = system($cmd);
- if ( $return_code != 0 ) { print("ERROR in parse_config::services found: $return_code\n") }
-# parsing nw objects
- $cmd = "$parser_py -m $mgm_name -i $import_id -n -f \"$object_file\" -d $debug_level > \"$output_dir/${mgm_name}_netzobjekte.csv\"";
-# print("DEBUG - cmd = $cmd\n");
- $return_code = system($cmd);
- if ( $return_code != 0 ) { print("ERROR in parse_config::network_objects found: $return_code\n") }
- return $return_code;
-}
-
-############################################################
-# copy_config_from_mgm_to_iso($ssh_private_key, $ssh_user, $ssh_hostname, $management_name, $obj_file_base, $cfg_dir, $rule_file_base)
-# Kopieren der Config-Daten vom Management-System zum ITSecorg-Server
-############################################################
-sub copy_config_from_mgm_to_iso {
- my $api_user = shift;
- my $api_hostname = shift;
- my $management_name = shift; # not used
- my $obj_file_base = shift;
- my $cfg_dir = shift;
- my $layer_name = shift;
- my $workdir = shift;
- my $auditlog = shift;
- my $prev_import_time= shift;
- my $api_port = shift;
- my $config_path_on_mgmt = shift;
- my $rulebase_names_hash_ref = shift;
- my $debug_level = shift;
- my $return_code;
- my $fehler_count = 0;
- my $domain_setting = "";
- my $api_port_setting = "";
- my $ssl_verify = "";
- my $python_bin = "/usr/bin/python3";
- my $base_path = "/usr/local/fworch/importer";
- my $lib_path;
- my $get_config_bin;
- my $enrich_config_bin;
- my $get_cmd;
- my $enrich_cmd;
-
- my $rulebase_names = get_local_ruleset_name_list($rulebase_names_hash_ref);
- if ( ${^CHILD_ERROR_NATIVE} ) { $fehler_count++; }
-
- if ( -r "$workdir/${CACTUS::FWORCH::ssh_id_basename}.pub" ) {
- $ssl_verify = "-s $workdir/${CACTUS::FWORCH::ssh_id_basename}.pub";
- }
- if (defined($config_path_on_mgmt) && $config_path_on_mgmt ne '') {
- $domain_setting = "-D " . $config_path_on_mgmt;
- }
- if (defined($api_port) && $api_port ne '') {
- $api_port_setting = "-p $api_port";
- }
-
- $lib_path = "$base_path/checkpointR8x";
- $get_config_bin = "$lib_path/get_basic_config.py";
- $enrich_config_bin = "$lib_path/enrich_config.py";
- $get_cmd = "$python_bin $get_config_bin -a $api_hostname -w '$workdir/$CACTUS::FWORCH::ssh_id_basename' -l '$rulebase_names' -u $api_user $api_port_setting $ssl_verify $domain_setting -o '$cfg_dir/$obj_file_base' -d $debug_level";
- $enrich_cmd = "$python_bin $enrich_config_bin -a $api_hostname -w '$workdir/$CACTUS::FWORCH::ssh_id_basename' -l '$rulebase_names' -u $api_user $api_port_setting $ssl_verify $domain_setting -c '$cfg_dir/$obj_file_base' -d $debug_level";
-
- if ($debug_level>0) {
- print("getting config with command: $get_cmd\n");
- }
- $return_code = system($get_cmd); if ( $return_code != 0 ) { $fehler_count++; }
- if ($debug_level>0) {
- print("enriching config with command: $enrich_cmd\n");
- }
- $return_code = system($enrich_cmd); if ( $return_code != 0 ) { $fehler_count++; }
- return ( $fehler_count, "$cfg_dir/$obj_file_base,$cfg_dir/$layer_name");
-}
-
-
-1;
-
-__END__
-
-=head1 NAME
-
-parser - Perl extension for check point R8x API get and parse config
-
-=head1 SYNOPSIS
-
- use CACTUS::FWORCH::import::checkpointR8x;
-
-=head1 DESCRIPTION
-
-Perl Module support for importing configs into database - in this case only empty shell calling python code
-
-=head2 EXPORT
-
- global variables
-
-=head1 SEE ALSO
-
- behind the door
-
-=head1 AUTHOR
-
- Cactus eSecurity, tmp@cactus.de
-
-=cut
diff --git a/roles/importer/files/importer/CACTUS/FWORCH/import/cisco.pm b/roles/importer/files/importer/CACTUS/FWORCH/import/cisco.pm
deleted file mode 100644
index 6108adb867..0000000000
--- a/roles/importer/files/importer/CACTUS/FWORCH/import/cisco.pm
+++ /dev/null
@@ -1,983 +0,0 @@
-package CACTUS::FWORCH::import::parser;
-
-use strict;
-use warnings;
-use Time::HiRes qw(time); # fuer hundertstelsekundengenaue Messung der Ausfuehrdauer
-use CACTUS::FWORCH;
-use CACTUS::FWORCH::import;
-use CACTUS::read_config;
-
-require Exporter;
-our @ISA = qw(Exporter);
-
-our %EXPORT_TAGS = ( 'basic' => [ qw( ©_config_from_mgm_to_iso &parse_config ) ] );
-
-our @EXPORT = ( @{ $EXPORT_TAGS{'basic'} } );
-our $VERSION = '0.3';
-
-our @config_lines; # array der Zeilen des Config-Files im Originalzustand (kein d2u etc.)
-our $parser_mode; # Typ des Config-Formates (basic, data)
-our $rule_order = 0; # Reihenfolge der Rules im Configfile
-our $rulebase_name;
-our %junos_uuids = ();
-
-## parse_audit_log Funktion fuer ASA (noch) nicht implementiert
-sub parse_audit_log { }
-
-#####################################
-# add_nw_obj
-# param1: obj_name
-# param2: obj_ip (with netmask)
-# param3: obj zone
-# param4: debuglevel [0-?]
-#####################################
-sub add_nw_obj {
- my $act_obj_name = shift;
- my $act_obj_ipaddr = shift;
- my $act_obj_mask = shift;
- my $debuglevel = shift;
- my $act_obj_ipaddr_last = '';
- my $act_obj_comm = '';
- my $act_obj_type = '';
- my $act_obj_loc = '';
- my $act_obj_color = 'black';
- my $act_obj_sys = '';
- my $act_obj_uid = '';
-
- if (!defined($act_obj_mask) || $act_obj_mask eq '') { $act_obj_mask = "32"; }
- elsif ($act_obj_mask =~ /\./) { $act_obj_mask = &calc_subnetmask($act_obj_mask); if ($act_obj_mask ne "32") { $act_obj_name = $act_obj_name . '_mask_' . $act_obj_mask; } }
- # otherwise leave mask as is (assuming int between 0 and 32)
- print_debug("add_nw_obj called with name=$act_obj_name, ip=$act_obj_ipaddr", $debuglevel, 5);
- if (!defined ($network_objects{"$act_obj_name.name"})) {
- @network_objects = (@network_objects, $act_obj_name);
- $network_objects{"$act_obj_name.name"} = $act_obj_name;
- $network_objects{"$act_obj_name.UID"} = $act_obj_name;
- if ($act_obj_mask==32) { $network_objects{"$act_obj_name.type"} = 'host' };
- if ($act_obj_mask<32) { $network_objects{"$act_obj_name.type"} = 'network' };
- $network_objects{"$act_obj_name.netmask"} = $act_obj_mask ;
- $network_objects{"$act_obj_name.ipaddr"} = $act_obj_ipaddr;
- $network_objects{"$act_obj_name.ipaddr_last"} = $act_obj_ipaddr_last;
- $network_objects{"$act_obj_name.color"} = $act_obj_color;
- $network_objects{"$act_obj_name.comments"} = $act_obj_comm;
- $network_objects{"$act_obj_name.location"} = $act_obj_loc;
- $network_objects{"$act_obj_name.sys"} = $act_obj_sys;
- } else {
- print_debug ("found duplicate object definition for network object $act_obj_name", $debuglevel, 6); # no error with cisco configs
- }
-}
-
-#####################################
-# add_nw_obj_group_member
-# param1: input-line
-# param2: debuglevel [0-?]
-#####################################
-sub add_nw_obj_group_member{
- my $group_name = shift;
- my $member_name = shift;
- my $debuglevel = shift;
-
- my $act_obj_name = '';
- my $act_obj_mbr = '';
- my $act_obj_fkt = '';
- my $act_obj_color = 'black';
- my $act_obj_comm = '';
- my $mbrlst = '';
- my $mbr_ref_lst = '';
-
- if (!defined ($network_objects{"$group_name.name"})) {
- @network_objects = (@network_objects, $group_name);
- $network_objects{"$group_name.name"} = $group_name;
- $network_objects{"$group_name.UID"} = $group_name;
- print_debug ("added group $group_name", $debuglevel, 5);
- $network_objects{"$group_name.type"} = 'group';
- $network_objects{"$group_name.color"} = $act_obj_color;
- }
-# die ("reference to undefined network object $member_name found in group $group_name, zone: $act_obj_zone");
- if (defined($network_objects{"$group_name.members"})) {
- $mbrlst = $network_objects{"$group_name.members"};
- $mbr_ref_lst = $network_objects{"$group_name.member_refs"};
- }
- if ( $mbrlst eq '' ) {
- $mbrlst = $member_name;
- $mbr_ref_lst = $member_name;
- }
- else {
- $mbrlst = "$mbrlst|$member_name";
- $mbr_ref_lst = "$mbr_ref_lst|$member_name";
- }
- $network_objects{"$group_name.members"} = $mbrlst;
- $network_objects{"$group_name.member_refs"} = $mbr_ref_lst;
- return;
-}
-
-#####################################
-# add_nw_service_obj
-# param1: input-line
-# param2: debuglevel [0-?]
-#####################################
-sub add_nw_service_obj { # ($application_name, $proto, $source_port, $destination_port, $uuid, $rpc, $icmp_art, $icmp_nummer, $debug_level_main)
- my $act_obj_name = shift;
- my $act_obj_proto = shift;
- my $act_obj_src = shift;
- my $act_obj_dst = shift;
- my $act_obj_uid = shift;
- my $act_obj_rpc = shift;
- my $icmp_art = shift;
- my $icmp_nummer = shift;
- my $debuglevel = shift
- my @range;
- my $act_obj_typ = 'simple';
- my $act_obj_type = '';
- my $act_obj_src_last = '';
- my $act_obj_dst_last = '';
- my $act_obj_comm = '';
- my $act_obj_time = '';
- my $act_obj_time_std = '';
- my $act_obj_color = 'black';
-
- if (!defined ($services{"$act_obj_name.name"})) {
- @services = (@services, $act_obj_name);
- $services{"$act_obj_name.name"} = $act_obj_name;
- if (defined($act_obj_src)) {
- @range = split ( /-/, $act_obj_src);
- $services{"$act_obj_name.src_port"} = $range[0];
- if (defined($range[1])) {
- $services{"$act_obj_name.src_port_last"} = $range[1];
- } else {
- $services{"$act_obj_name.src_port_last"} = '';
- }
- }
- if (defined($act_obj_dst)) {
- @range = split ( /-/, $act_obj_dst);
- $services{"$act_obj_name.port"} = $range[0];
- if (defined($range[1])) {
- $services{"$act_obj_name.port_last"} = $range[1];
- } else {
- $services{"$act_obj_name.port_last"} = '';
- }
- }
- if (defined($act_obj_proto)) {
- $services{"$act_obj_name.ip_proto"} = get_proto_number($act_obj_proto)
- } else {
- $services{"$act_obj_name.ip_proto"} = '';
- }
- $services{"$act_obj_name.timeout"} = $act_obj_time;
- $services{"$act_obj_name.color"} = $act_obj_color;
- $services{"$act_obj_name.comments"} = $act_obj_comm;
- $services{"$act_obj_name.typ"} = $act_obj_typ;
- $services{"$act_obj_name.type"} = $act_obj_type;
- if (defined($act_obj_rpc)) {
- $services{"$act_obj_name.rpc_port"} = $act_obj_rpc;
- } else {
- $services{"$act_obj_name.rpc_port"} = '';
- }
- $services{"$act_obj_name.UID"} = $act_obj_name;
- if (defined($act_obj_uid) && $act_obj_uid ne '') { $junos_uuids{"$act_obj_uid"} = $act_obj_name; } # collect uid refs
- print_debug("add_nw_service_obj: added application $act_obj_name", $debuglevel, 4);
- } else {
- print_debug("add_nw_service_obj: warning duplicate defintion of service $act_obj_name", $debuglevel, 1);
- }
- return;
-}
-
-#####################################
-# add_nw_service_obj_grp
-# param1: input-line
-# param2: debuglevel [0-?]
-#####################################
-sub add_nw_service_obj_grp { # ($application_name, $members, $members_proto, $members_uid, $debuglevel_main);
- my $act_obj_name = shift;
- my $act_obj_members = shift;
- my $debuglevel = shift;
- my $act_obj_typ = 'group';
- my $act_obj_type = '';
- my $act_obj_proto = '';
- my $act_obj_src_last = '';
- my $act_obj_dst_last = '';
- my $act_obj_comm = '';
- my $act_obj_time = '';
- my $act_obj_time_std = '';
- my $act_obj_color = 'black';
- my $act_obj_rpc = '';
- my $mbrlst = '';
-
- if (!defined ($services{"$act_obj_name.name"})) {
- @services = (@services, $act_obj_name);
- $services{"$act_obj_name.name"} = $act_obj_name;
- print_debug("adding service group $act_obj_name", $debuglevel, 5);
- } else {
- print_debug("re-defining service group $act_obj_name", $debuglevel, 5);
- }
- if (defined($act_obj_members) && $act_obj_members ne '') {
- $services{"$act_obj_name.members"} = $act_obj_members; # simple group case
- $services{"$act_obj_name.member_refs"} = $act_obj_members; # simple group case
- print_debug("adding service group $act_obj_name with members $act_obj_members", $debuglevel, 5);
- } else {
- print_debug("no members defined", $debuglevel, 1);
- }
- $services{"$act_obj_name.ip_proto"} = $act_obj_proto;
- $services{"$act_obj_name.timeout"} = $act_obj_time;
- $services{"$act_obj_name.color"} = $act_obj_color;
- $services{"$act_obj_name.comments"} = $act_obj_comm;
- $services{"$act_obj_name.typ"} = $act_obj_typ;
- $services{"$act_obj_name.type"} = $act_obj_type;
- $services{"$act_obj_name.rpc_port"} = $act_obj_rpc;
- $services{"$act_obj_name.UID"} = $act_obj_name;
-}
-
-sub junos_split_list { # param1 = list of objects (network or service)
- my $list = shift;
- my $debug_level = shift;
- my $orig_list = $list;
-
- if ($list =~ /^\[\s([\w\-\_\/\.\d\s]+?)\s\]$/) { # standard list format: [ x1 x2 x3 ]
- $list = $1;
- $list = join('|', split(/\s/, $list));
- } elsif ($list =~ /[\w\-\_\/\.\d]+/) {
- # found single object, no changes necessary
- } else {
- print_debug("warning in junos_split_list: orig_list=$orig_list; found no match for object list", $debug_level, 1);
- }
-# print_debug("junos_split_list: orig_list=$orig_list, result=$list", $debug_level, 5);
- return $list;
-}
-
-#####################################
-# add_rule
-# param1:
-# debuglevel [integer]
-#####################################
-sub add_rule { # ($rule_no, $from_zone, $to_zone, $policy_name, $disabled, $source, $destination, $application, $action, $track, $debuglevel_main)
- my $rule_no = shift;
- my $from_zone = shift;
- my $to_zone = shift;
- my $policy_name = shift;
- my $disabled = shift;
- my $source = shift;
- my $destination = shift;
- my $service = shift;
- my $action = shift;
- my $track = shift;
- my $debuglevel = shift;
- my $rule_id;
-
-# print_debug ("add_rule: rulebase_name=$rulebase_name, rulecount=" . $rulebases{"$rulebase_name.rulecount"}, $debuglevel, 4);
- $rulebases{"$rulebase_name.rulecount"} = $rule_no + 1; # Anzahl der Regeln wird sukzessive hochgesetzt
- $rule_id = "from_zone__$from_zone" . "__to_zone__$to_zone" . "__$policy_name";
- $ruleorder[$rule_no] = $rule_no;
-
- if (!defined($track) || $track eq '') { $track = 'none'; }
- if (length($track)<3) { print_debug ("warning, short track: <$track>", $debuglevel, 1); }
-
- $rulebases{"$rulebase_name.$rule_no.src"} = '';
- foreach my $src (split(/\|/, &junos_split_list($source, $debuglevel))) {
- if ($rulebases{"$rulebase_name.$rule_no.src"} ne '') {
- $rulebases{"$rulebase_name.$rule_no.src"} .= '|';
- $rulebases{"$rulebase_name.$rule_no.src.refs"} .= '|';
- }
- $rulebases{"$rulebase_name.$rule_no.src"} .= "$src";
- $rulebases{"$rulebase_name.$rule_no.src.refs"} .= ("$src" . "__zone__$from_zone");
- }
- $rulebases{"$rulebase_name.$rule_no.dst"} = '';
- foreach my $dst (split(/\|/, &junos_split_list($destination, $debuglevel))) {
- if ($rulebases{"$rulebase_name.$rule_no.dst"} ne '') {
- $rulebases{"$rulebase_name.$rule_no.dst"} .= '|';
- $rulebases{"$rulebase_name.$rule_no.dst.refs"} .= '|';
- }
- $rulebases{"$rulebase_name.$rule_no.dst"} .= "$dst";
- $rulebases{"$rulebase_name.$rule_no.dst.refs"} .= ("$dst" . "__zone__$to_zone");
- }
-
- $rulebases{"$rulebase_name.$rule_no.services"} = '';
- foreach my $svc (split(/\|/, &junos_split_list($service, $debuglevel))) {
- if ($rulebases{"$rulebase_name.$rule_no.services"} ne '') {
- $rulebases{"$rulebase_name.$rule_no.services"} .= '|';
- $rulebases{"$rulebase_name.$rule_no.services.refs"} .= '|';
- }
- $rulebases{"$rulebase_name.$rule_no.services"} .= "$svc";
- $rulebases{"$rulebase_name.$rule_no.services.refs"} .= "$svc";
- }
-
- $rulebases{"$rulebase_name.$rule_no.id"} = $rule_id;
- $rulebases{"$rulebase_name.$rule_no.ruleid"} = $rule_id;
- $rulebases{"$rulebase_name.$rule_no.order"} = $rule_no;
- if ($disabled eq 'inactive') { $rulebases{"$rulebase_name.$rule_no.disabled"} = '1'; }
- else { $rulebases{"$rulebase_name.$rule_no.disabled"} = '0'; }
- $rulebases{"$rulebase_name.$rule_no.src.zone"} = $from_zone;
- $rulebases{"$rulebase_name.$rule_no.dst.zone"} = $to_zone;
- $rulebases{"$rulebase_name.$rule_no.services.op"} = '0';
- $rulebases{"$rulebase_name.$rule_no.src.op"} = '0';
- $rulebases{"$rulebase_name.$rule_no.dst.op"} = '0';
- $rulebases{"$rulebase_name.$rule_no.action"} = $action;
- $rulebases{"$rulebase_name.$rule_no.track"} = $track;
- $rulebases{"$rulebase_name.$rule_no.install"} = ''; # set hostname verwenden ?
- $rulebases{"$rulebase_name.$rule_no.name"} = ''; # kein Aequivalent zu CP rule_name
- $rulebases{"$rulebase_name.$rule_no.time"} = '';
- $rulebases{"$rulebase_name.$rule_no.comments"} = $policy_name;
- $rulebases{"$rulebase_name.$rule_no.UID"} = $rule_id;
- $rulebases{"$rulebase_name.$rule_no.header_text"} = '';
- return $rule_no+1;
-}
-
-############################################################
-# add_zone ($new_zone)
-############################################################
-sub add_zone {
- my $new_zone = shift;
- my $debug = shift;
- my $is_there = 0;
- foreach my $elt (@zones) { if ($elt eq $new_zone) { $is_there = 1; last; } }
- if (!$is_there) { push @zones, $new_zone; &print_debug("adding new zone: $new_zone", $debug, 1); }
-}
-
-############################################################
-# object_address_add (name, ip, mask, zone, comment)
-############################################################
-sub object_address_add {
- my $act_obj_name = $_[0];
- my $act_obj_ipaddr = $_[1];
- my $act_obj_mask = $_[2];
- my $act_obj_zone = $_[3];
- my $act_obj_comm = $_[4];
- my @params;
- my $act_obj_nameZ = '';
- my $act_obj_ipaddr_last = '';
- my $act_obj_type = 'simple';
- my $act_obj_loc = '';
- my $act_obj_color = 'black';
- my $act_obj_sys = '';
- my $act_obj_uid = '';
-
- $act_obj_nameZ = "${act_obj_name}__zone__$act_obj_zone"; # Zone in Feldindex mit aufgenommen
- if (!defined ($network_objects{"$act_obj_nameZ.name"})) {
- @network_objects = (@network_objects, $act_obj_nameZ);
- $network_objects{"$act_obj_nameZ.name"} = $act_obj_name;
- } elsif (defined ($network_objects{"$act_obj_nameZ.name"})) {
- print "sub ns_object_address NET_OBJECT: $act_obj_nameZ ist bereits definiert.\n";
- } else {
- print "sub ns_object_address NET_OBJECT: $act_obj_nameZ ist undefiniert.\n";
- }
- my $subnetbits = calc_subnetmask ($act_obj_mask);
- if ($subnetbits==32) { $network_objects{"$act_obj_nameZ.type"} = 'host' };
- if ($subnetbits<32) { $network_objects{"$act_obj_nameZ.type"} = 'network' };
- $network_objects{"$act_obj_nameZ.netmask"} = $act_obj_mask ;
- $network_objects{"$act_obj_nameZ.zone"} = $act_obj_zone; # neues Feld fuer Zone
- $network_objects{"$act_obj_nameZ.ipaddr"} = $act_obj_ipaddr;
- $network_objects{"$act_obj_nameZ.ipaddr_last"} = $act_obj_ipaddr_last;
- $network_objects{"$act_obj_nameZ.color"} = $act_obj_color;
- $network_objects{"$act_obj_nameZ.comments"} = $act_obj_comm;
- $network_objects{"$act_obj_nameZ.location"} = $act_obj_loc;
- $network_objects{"$act_obj_nameZ.sys"} = $act_obj_sys;
- $network_objects{"$act_obj_nameZ.UID"} = $act_obj_nameZ;
-}
-
-############################################################
-# read_predefined_services(
-############################################################
-sub read_predefined_services {
- my $device_type = shift;
-# my $predefined_service_string = shift;
- my $debug_level = shift;
- my $predef_svc;
- my ($svc_name,$ip_proto,$port,$port_end,$timeout,$comment,$typ,$group_members);
-
- $predef_svc = exec_pgsql_cmd_return_value ("SELECT dev_typ_predef_svc FROM stm_dev_typ WHERE dev_typ_id=$device_type");
- my @predef_svc = split /\n/, $predef_svc;
-
- &print_debug("inserting pre-defined services for junos: $predef_svc",$debug_level,8);
-
- foreach my $svc_line (@predef_svc) {
- ($svc_name,$ip_proto,$port,$port_end,$timeout,$comment,$typ,$group_members) = split /;/, $svc_line;
- $services{"$svc_name.name"} = $svc_name;
- $services{"$svc_name.port"} = $port;
- $services{"$svc_name.port_last"} = $port_end;
- $services{"$svc_name.ip_proto"} = $ip_proto;
- $services{"$svc_name.timeout"} = $timeout;
- $services{"$svc_name.color"} = "black";
-# $services{"$svc_name.comments"} = "$predefined_service_string, $comment";
- $services{"$svc_name.comments"} = $comment;
- $services{"$svc_name.typ"} = $typ;
- $services{"$svc_name.type"} = "";
- $services{"$svc_name.rpc_port"} = "";
- $services{"$svc_name.UID"} = $svc_name;
- $services{"$svc_name.members"} = $group_members;
- $services{"$svc_name.member_refs"} = $group_members;
- push @services, $svc_name;
- }
- return;
-}
-
-sub resolve_service_uuid_references { # ($debuglevel_main);
- my $debug = shift;
-
- foreach my $nw_svc (@services) {
- &print_debug("resolve_service_uuid_references: checking service $nw_svc, typ=" . $services{"$nw_svc.typ"} . "type=" . $services{"$nw_svc.type"}, $debug, 5);
- if ($services{"$nw_svc.typ"} eq 'group') {
- &print_debug("resolve_service_uuid_references: checking service group $nw_svc", $debug, 5);
- my @members = split (/\|/, $services{"$nw_svc.member_refs"});
- my $member_string = '';
- my $change_flag = 0;
- foreach my $member (@members) {
- &print_debug("resolve_service_uuid_references: checking member $member", $debug, 5);
- if ($member =~ /^[a-f0-9]\-[a-f0-9]\-[a-f0-9]\-[a-f0-9]\-[a-f0-9]$/) {
- my $old_member = $member;
- $change_flag = 1;
- $member = $junos_uuids{"$member"};
- &print_debug("resolve_service_uuid_references: replacing uuid $old_member with $member", $debug, 5);
- }
- if ($member_string ne '') { $member_string .= '|'; }
- $member_string .= $member;
- }
- if ($change_flag) {
- $services{"$nw_svc.members"} = $member_string;
- $services{"$nw_svc.member_refs"} = $member_string;
- }
- }
- }
- return;
-}
-
-############################################################
-# copy_config_from_mgm_to_iso($ssh_private_key, $ssh_user, $ssh_hostname, $management_name,
-# $obj_file_base, $cfg_dir, $rule_file_base)
-# Kopieren der Config-Daten vom Management-System zum ITSecorg-Server
-############################################################
-
-sub copy_config_from_mgm_to_iso {
- my $ssh_user = shift;
- my $ssh_hostname = shift;
- my $management_name = shift;
- my $obj_file_base = shift;
- my $cfg_dir = shift;
- my $rule_file_base = shift;
- my $workdir = shift;
- my $debug_level = shift;
- my $cmd;
- my $fehler_count = 0;
- my $result;
-
- $cmd = "$scp_bin $scp_batch_mode_switch -i $workdir/$CACTUS::FWORCH::ssh_id_basename $ssh_user\@$ssh_hostname:asa_config $cfg_dir/$obj_file_base"; # dummy
-# $cmd = "$ssh_bin -i $workdir/$CACTUS::FWORCH::ssh_id_basename $ssh_user\@$ssh_hostname show config > $cfg_dir/$obj_file_base"; # Cisco ASA - noch offen
- if (system ($cmd)) { $fehler_count++; }
- return ($fehler_count, "$cfg_dir/$obj_file_base" );
-}
-
-sub sort_rules_and_add_zone_headers {
- my $anzahl_regeln;
- my $count;
- my $zone_string;
- my @rule_zones = ();
-
- # Nachbereitung Regeln: Sortierung nach a) Zonen b) $ruleorder
- if (!defined($rulebases{"$rulebase_name.rulecount"})) {
- $anzahl_regeln = 0;
- } else {
- $anzahl_regeln = $rulebases{"$rulebase_name.rulecount"};
- }
- for ($count=0; $count<$anzahl_regeln; $count++) {
- $zone_string = $rulebases{"$rulebase_name.$ruleorder[$count].src.zone"};
- $zone_string .= " : ";
- $zone_string .= $rulebases{"$rulebase_name.$ruleorder[$count].dst.zone"};
- push @rule_zones, $zone_string
- }
- my @idx = ();
- my $item;
- for (@rule_zones) {
- ($item) = $_;
- push @idx, $item;
- }
-
- @ruleorder = @ruleorder[ sort { $idx[$a] cmp $idx[$b] } 0 .. $anzahl_regeln-1 ];
- @rule_zones = @rule_zones[ sort { $idx[$a] cmp $idx[$b] } 0 .. $anzahl_regeln-1 ];
-
- # Nachbereitung Regeln: Header-Regeln vor Zonenwechsel einfuegen
- my $new_zone_string;
- my $old_zone_string = "";
- my $rule_header_count = 1;
- my $rule_header_offset = &CACTUS::read_config::read_config('rule_header_offset') * 1;
- my $new_rule_id;
- for ($count = 0; $count < $anzahl_regeln; $count++) {
- $new_zone_string = $rule_zones[$count];
- if ($new_zone_string ne $old_zone_string) { # insert header rule
- $new_rule_id = $rule_header_offset+$rule_header_count++;
- (my $src_zone, my $dst_zone) = split / : /, $new_zone_string;
- splice(@ruleorder,$count,0,$new_rule_id); # fuegt neue Regel ein
- splice(@rule_zones,$count,0,$new_zone_string);
- $anzahl_regeln++;
- $rulebases{"$rulebase_name.rulecount"} = $anzahl_regeln;
- $rulebases{"$rulebase_name.$ruleorder[$count].id"} = $new_rule_id;
- $rulebases{"$rulebase_name.$ruleorder[$count].header_text"} = $new_zone_string;
- $rulebases{"$rulebase_name.$ruleorder[$count].UID"} = $new_rule_id;
- $rulebases{"$rulebase_name.$ruleorder[$count].src"} = "any";
- $rulebases{"$rulebase_name.$ruleorder[$count].dst"} = "any";
- $rulebases{"$rulebase_name.$ruleorder[$count].services"} = "any";
- $rulebases{"$rulebase_name.$ruleorder[$count].action"} = "deny";
- $rulebases{"$rulebase_name.$ruleorder[$count].src.zone"} = $src_zone;
- $rulebases{"$rulebase_name.$ruleorder[$count].dst.zone"} = $dst_zone;
- $rulebases{"$rulebase_name.$ruleorder[$count].disabled"} = '0';
- $rulebases{"$rulebase_name.$ruleorder[$count].src.op"} = '0';
- $rulebases{"$rulebase_name.$ruleorder[$count].dst.op"} = '0';
- $rulebases{"$rulebase_name.$ruleorder[$count].services.op"} = '0';
- }
- $old_zone_string = $new_zone_string;
- }
-}
-
-sub parse_mgm_name { # ($obj_file, $fworch_workdir, $debug_level, $mgm_name, $config_dir, $import_id)
- my $in_file_main = shift;
- my $fworch_workdir = shift;
- my $debuglevel_main = shift;
- my $mgm_name = shift;
- my $config_dir = shift;
- my $import_id = shift;
- my $line = '';
- my $context = '';
- my @nodes= ();
-
- &print_debug("entering parse_mgm_name",$debuglevel_main,2);
- NEW_LINE: foreach $line (@config_lines) {
- chomp($line);
- if ($line=~ /^hostname\s+(.+?)$/ && ($context eq '')) {
- $mgm_name = $1;
- @nodes = ($mgm_name, @nodes);
- &print_debug("parse_mgm_name: found hostname: $mgm_name",$debuglevel_main,1);
- $context = '';
- @rulebases = ($mgm_name);
- $rulebase_name = $mgm_name;
- &print_debug("parse_mgm_name: found hostname of single system: $mgm_name and setting rulebase_name to $mgm_name",$debuglevel_main,1);
- return $mgm_name;
- }
- }
- &print_debug("ERROR: end of parse_mgm_name: at end without match (mgm_name=$mgm_name)",$debuglevel_main,-1);
-}
-
-sub extract_ip_addresses_from_string {
- my $input_string = shift;
- my $debug = shift;
- my @list_of_ips = ();
- my $network_part;
- my $ip;
-
- print_debug("acl extract_ip_addresses_from_string entering with: input=$input_string", $debug, 4);
- while ($input_string =~ /^.*?(h?o?s?t?)\s?(\d+\.\d+\.\d+\.\d+)(.*)$/) { # at least one explicit ip address included
- my $rest = $6;
- my $host = $1;
- my $ip = $2;
- if (defined($host) && $host ne '') {
- @list_of_ips = (@list_of_ips, "$ip/32");
- } else {
- if (defined($network_part)) {
- @list_of_ips = (@list_of_ips, "$network_part/" . &calc_subnetmask($ip));
- undef($network_part);
- } else {
- $network_part = $ip;
- }
- }
- $input_string = $rest;
- }
- return join(',', @list_of_ips);
-}
-
-# result: ($application_name, $proto, $destination_port, $icmp_art)
-sub extract_services_from_string {
- my $input_string = shift;
- my $debug = shift;
- my @list_of_svcs = ();
- my $svc;
- my $proto;
- my $destination_port;
- my $icmp_art;
- my $application_name;
-
- # cases:
- # icmp object-group VPNConc 21.253.174.192 255.255.255.240 echo-reply
- # udp object-group kraft_proxies any eq domain
- # tcp host 82.122.108.51 host 46.162.37.17 eq 6614
- # esp any object-group VPNConc
-
- print_debug("acl extract_services_from_string entering with: input=$input_string", $debug, 4);
- if ($input_string =~ /^.*?(tcp|udp)\s(.*?)\seq\s(.*)$/) {
- $proto = $1;
- $destination_port = $3;
- $icmp_art = '';
- $application_name = "${proto}_$destination_port";
- @list_of_svcs = (@list_of_svcs, "$application_name/$proto/$destination_port/$icmp_art");
- }
-=cut
- # das funktioniert noch nicht, da der icmp type nicht trivial zu erkennen ist
- if ($input_string =~ /^.*?(icmp)\s(.*?)\s([\w\-]+)$/) {
- $proto = 'icmp';
- $destination_port = '';
- $icmp_art = $3;
- $application_name = "${proto}_$icmp_art";
- @list_of_svcs = (@list_of_svcs, "$application_name/$proto/$destination_port/$icmp_art");
- }
-=cut
- return join(',', @list_of_svcs);
-}
-
-# the following function does only parse simple objects without groups. Groups are parsed in a second run using function parse_config_group_objects
-sub parse_config_base_objects { # ($obj_file, $fworch_workdir, $debug_level, $mgm_name, $config_dir, $import_id)
- my $in_file_main = shift;
- my $fworch_workdir = shift;
- my $debug = shift;
- my $mgm_name = shift;
- my $config_dir = shift;
- my $import_id = shift;
- my ($zone, $address_group_name, $obj_name, $obj_ip, $group_member_name, $application_name, $group_name, $group_members, $proto, $icmp_art, $icmp_nummer,
- $source_port, $destination_port, $uuid, $members, $members_uid, $members_proto, $rpc);
- my $line = '';
- my $context = '';
- my @nodes= ();
- my $obj_mask;
-
- &print_debug("entering parse_config_base_objects =======================================================",$debug,2);
- NEW_LINE: foreach $line (@config_lines) {
- chomp($line);
- &print_debug("pcbo-line: $line", $debug, 9);
-# parsing both nw nad svc objects
- if ($line =~ /^access\-list\s.+?extended\s(permit|deny)\s(.+)$/ && ($context eq '' || $context eq 'network-object-group' || $context eq 'service-object-group' || $context eq 'acl-extended')) {
- my $acl = $2;
- $context = &switch_context($context, 'acl-extended', $debug);
- print_debug("acl match part: $acl", $debug, 7);
- foreach my $ip (split(/,/, &extract_ip_addresses_from_string($acl, $debug))) {
- ($obj_ip, $obj_mask) = split (/\//, $ip);
- $obj_name = $obj_ip;
- print_debug("acl expl network definition: found network $obj_name (raw: $ip) with ip $obj_ip and mask $obj_mask", $debug, 7);
- &add_nw_obj ($obj_name, $obj_ip, $obj_mask, $debug);
- }
- foreach my $svc (split(/,/, &extract_services_from_string($acl, $debug))) {
- ($application_name, $proto, $destination_port, $icmp_art) = split (/\//, $svc);
- print_debug("acl expl service definition: found name=$application_name, proto=$proto, dest_port=$destination_port,icmp_art=$icmp_art", $debug, 4);
- &add_nw_service_obj ($application_name, $proto, $source_port, $destination_port, $uuid, $rpc, $icmp_art, $icmp_nummer, $debug);
- undef($application_name); undef($source_port); undef($destination_port); undef($uuid); undef($rpc); undef($icmp_art); undef($icmp_nummer);
- }
- next NEW_LINE;
- }
-# parsing network objects
- if ($line =~ /^object\-group\snetwork\s(.+?)$/ && ($context eq '' || $context eq 'network-object-group' || $context eq 'service-object-group')) {
- $context = &switch_context($context, 'network-object-group', $debug);
- next NEW_LINE;
- }
- if ($line=~ /^\s+network\-object\s(host\s)?([\d\.]+)\s?([\d\.]*)$/ && $context eq 'network-object-group') {
- $obj_name = $2;
- $obj_ip = $2;
- $obj_mask = $3;
- print_debug("found obj $obj_name with ip $obj_ip and mask $obj_mask", $debug, 4);
- &add_nw_obj ($obj_name, $obj_ip, $obj_mask, $debug);
- undef($obj_name); undef($obj_ip); undef($obj_mask);
- next NEW_LINE;
- }
-# parsing network services
- if ($line =~ /^object\-group\sservice\s(.+?)\s(.+?)$/ && ($context eq '' || $context eq 'network-object-group' || $context eq 'service-object-group')) {
- $context = &switch_context($context, 'service-object-group', $debug);
- $proto = $2;
- next NEW_LINE;
- }
- if ($line =~ /^object\-group\sicmp\-type\s(.+?)$/ && ($context eq '' || $context eq 'network-object-group' || $context eq 'service-object-group')) {
- $context = &switch_context($context, 'service-object-group', $debug);
- $proto = 'icmp';
- next NEW_LINE;
- }
- if ($line=~ /^\s+port\-object\seq\s(.+?)$/ && $context eq 'service-object-group') {
- print_debug("found svc proto $proto", $debug, 4);
- $destination_port = $1;
- $application_name = "${proto}_$destination_port";
- print_debug("found svc obj $application_name with destination port $destination_port", $debug, 4);
- &add_nw_service_obj ($application_name, $proto, $source_port, $destination_port, $uuid, $rpc, $icmp_art, $icmp_nummer, $debug);
- undef($application_name); undef($source_port); undef($destination_port); undef($uuid); undef($rpc); undef($icmp_art); undef($icmp_nummer);
- # undef($proto); --> needed for following services in same group
- next NEW_LINE;
- }
- if ($line=~ /^\s+icmp\-object\s(.+?)$/ && $context eq 'service-object-group') {
- $icmp_art= $1;
- print_debug("found icmp obj $icmp_art", $debug, 4);
- $application_name = "${proto}_$icmp_art";
- print_debug("found svc icmp obj $application_name with type $icmp_art", $debug, 4);
- &add_nw_service_obj ($application_name, $proto, $source_port, $destination_port, $uuid, $rpc, $icmp_art, $icmp_nummer, $debug);
- undef($application_name); undef($source_port); undef($destination_port); undef($uuid); undef($rpc); undef($icmp_art); undef($icmp_nummer);
- # undef($proto); --> needed for following services in same group
- next NEW_LINE;
- }
- }
- return 0;
-}
-
-sub parse_config_group_objects { # ($obj_file, $fworch_workdir, $debug_level, $mgm_name, $config_dir, $import_id)
- my $in_file_main = shift;
- my $fworch_workdir = shift;
- my $debug = shift;
- my $mgm_name = shift;
- my $config_dir = shift;
- my $import_id = shift;
- my ($zone, $address_group_name, $act_obj_mask, $obj_name, $obj_ip, $group_member_name, $application_name);
- my ($comment, $proto, $icmp_art, $icmp_nummer, $source_port, $destination_port, $uuid, $members, $members_uid, $members_proto, $rpc, $service_group_name);
- my $line = '';
- my $context = '';
- my @nodes= ();
-
- &print_debug("entering parse_config_group_objects =======================================================",$debug,2);
- NEW_LINE: foreach $line (@config_lines) {
- chomp($line);
- &print_debug("pcgo-line: $line", $debug, 9);
- if ($line =~ /^\s+description (.*)$/) { $comment = $1; } # general collection of description
-# parsing network object groups
- if ($line =~ /^object\-group\snetwork\s(.+?)$/ && ($context eq '' || $context eq 'network-object-group' || $context eq 'service-object-group')) {
- $context = &switch_context($context, 'network-object-group', $debug);
- $address_group_name = $1;
- print_debug("found address group $address_group_name", $debug, 4);
- next NEW_LINE;
- }
- if ($line=~ /^\s+network\-object\s(host\s)?([\d\.]+)\s?([\d\.]*)$/ && $context eq 'network-object-group') {
- $group_member_name = $2;
- $act_obj_mask = $3;
- if (!defined($act_obj_mask) || $act_obj_mask eq '') { $act_obj_mask = "32"; }
- else { $act_obj_mask = &calc_subnetmask($act_obj_mask); if ($act_obj_mask ne "32") { $group_member_name = $group_member_name . '_mask_' . $act_obj_mask; } }
- print_debug("found address group member of group $address_group_name: $group_member_name", $debug, 4);
- &add_nw_obj_group_member ($address_group_name, $group_member_name, $debug);
- undef($group_member_name);
- next NEW_LINE;
- }
-# parsing service groups
- if ($line =~ /^object\-group\sservice\s(.+?)\s(.+?)$/ && ($context eq '' || $context eq 'network-object-group' || $context eq 'service-object-group')) {
- $context = &switch_context($context, 'service-object-group', $debug);
- $proto = $2;
- $service_group_name = $1;
- $members = '';
- next NEW_LINE;
- }
- if ($line=~ /^\s+port\-object\seq\s(.+?)$/ && $context eq 'service-object-group') {
- $destination_port = $1;
- $application_name = "${proto}_$destination_port";
- if ($members ne '') { $members .= '|'; }
- $members .= "$application_name";
- print_debug("found application $application_name in service group $service_group_name", $debug, 6);
- next NEW_LINE;
- }
- if ($line=~ /^\s+icmp\-object\s(.+?)$/ && $context eq 'service-object-group') {
- $icmp_art= $1;
- print_debug("found icmp obj $icmp_art", $debug, 4);
- $application_name = "${proto}_$icmp_art";
- if ($members ne '') { $members .= '|'; }
- $members .= "$application_name";
- print_debug("found application $application_name in service group $service_group_name", $debug, 6);
- next NEW_LINE;
- }
- if ($context eq 'service-object-group' && $line =~ /^\w/) {
- print_debug("adding service group $service_group_name", $debug, 6);
- &add_nw_service_obj_grp ($service_group_name, $members, $comment, $debug);
- undef($comment);
- next NEW_LINE;
- }
- }
- return 0;
-}
-
-sub cfg_file_complete { # check auf Vollstaendigkeit des Config-Files:
- my $debug_level = shift;
- my $ln_cnt = $#config_lines;
- my $cfg_file_complete = 1;
-
- while ($config_lines[$ln_cnt] =~ /^\s*$/ ) { $ln_cnt -- ; } # ignore empty lines at the end
- if ($config_lines[$ln_cnt] !~ /^\:\send$/) {
- $cfg_file_complete = 0;
- print_debug ("ERROR: expected last line to contain either primary node info or top level curly bracket. Instead got: " . $config_lines[$ln_cnt], $debug_level, -1);
- }
- return $cfg_file_complete;
-}
-
-sub switch_context {
- my $old_level = shift;
- my $new_level = shift;
- my $debug_level = shift;
- print_debug("switching context from $old_level to $new_level", $debug_level, 8);
- return $new_level;
-}
-
- sub parse_config_rules { # ($in_file_main, $fworch_workdir, $debuglevel_main, $mgm_name_in_config, $config_dir, $import_id)
- my $in_file_main = shift;
- my $fworch_workdir = shift;
- my $debug = shift;
- my $mgm_name = shift;
- my $config_dir = shift;
- my $import_id = shift;
- my ($zone, $action, $source, $destination, $application, $from_zone, $to_zone, $policy_name, $disabled, $track);
- my $line = '';
- my $context = '';
- my $rule_no = 0;
- my $list;
- my $list_typ;
-
- &print_debug("entering parse_config_rules =======================================================",$debug,2);
- NEW_LINE: foreach $line (@config_lines) {
- chomp($line);
- &print_debug("pcr: $line", $debug, 9);
-# rules start
- if ($line =~ /^security\s\{$/ && $context eq '') { $context = &switch_context($context, 'security', $debug); next NEW_LINE; }
- if ($line =~ /^\s+policies\s\{$/ && $context eq 'security') { $context = &switch_context($context, 'security/policies', $debug); next NEW_LINE; }
- if ($line=~ /^\s+from\-zone\s(.+?)\sto\-zone\s(.+?)\s\{$/ && $context eq 'security/policies') {
- $context = &switch_context($context, 'security/policies/zone', $debug);
- $from_zone = $1;
- $to_zone = $2;
- &print_debug("entering policy from zone $from_zone to zone $to_zone",$debug,2);
- next NEW_LINE;
- }
- if ($line=~ /^\s+(inactive)?\:?\s?policy\s(.+?)\s\{$/ && $context eq 'security/policies/zone') {
- $disabled = $1; if (!defined($disabled)) { $disabled = ''; }
- $policy_name = $2;
- $context = &switch_context($context, 'security/policies/zone/policy', $debug);
- &print_debug("entering policy $policy_name (zone $from_zone to $to_zone)",$debug,2);
- next NEW_LINE;
- }
-# match part of rule
- if ($line=~ /^\s+match\s\{$/ && $context eq 'security/policies/zone/policy') { $context = &switch_context($context, 'security/policies/zone/policy/match', $debug); next NEW_LINE; }
- if ($line =~ /^\s+(source|destination)\-address\s(.+?)\;?$/ && $context eq 'security/policies/zone/policy/match') {
- $list_typ = $1;
- $list = $2;
- if ($list_typ eq 'source') { $source = $list; }
- if ($list_typ eq 'destination') { $destination = $list; }
-# &print_debug("pcr: nw-list=$list", $debug, 9);
- if ($list =~ /^\[/ && $list !~ /\]$/) { $context = &switch_context($context, "security/policies/zone/policy/match/$list_typ", $debug); }
- next NEW_LINE;
- }
- if ($line =~ /^\s+application\s(.+?)\;?$/ && $context eq 'security/policies/zone/policy/match') {
- $application = $1;
-# &print_debug("pcr: appl-list=$application", $debug, 9);
- if ($application =~ /^\[/ && $application !~ /\]$/) { $context = &switch_context($context, 'security/policies/zone/policy/match/application', $debug); }
- next NEW_LINE;
- }
- if ($line=~ /^\s+\}$/ && $context eq 'security/policies/zone/policy/match') { $context = &switch_context($context, 'security/policies/zone/policy', $debug); next NEW_LINE; }
-# dealing with multiple source, destination or application lines of rules
- if ($context =~ /^security\/policies\/zone\/policy\/match\/(\w+)$/) {
- $list_typ = $1;
- my $multi_line;
- if ($line =~ /\s*(.*?)\;?$/) { $multi_line = $1; }
- if ($list_typ eq 'source') { $source .= " $multi_line"; }
- if ($list_typ eq 'destination') { $destination.= " $multi_line"; }
- if ($list_typ eq 'application') { $application .= " $multi_line"; }
- if ($multi_line =~ /\]$/) { $context = &switch_context($context, "security/policies/zone/policy/match", $debug); undef($list_typ); undef($list); } # last line reached
- next NEW_LINE;
- }
-
-# action / track part of rule (then)
- if ($line=~ /^\s+then\s\{$/ && $context eq 'security/policies/zone/policy') { $context = &switch_context($context, 'security/policies/zone/policy/action', $debug); next NEW_LINE; }
- if ($line=~ /^\s+(permit|deny|reject)\;$/ && $context eq 'security/policies/zone/policy/action') { $action = $1; next NEW_LINE; }
- if ($line=~ /^\s+(permit|deny|reject)\s\{$/ && $context eq 'security/policies/zone/policy/action') {
- $action = $1;
- $context = &switch_context($context, 'security/policies/zone/policy/action/action_parameter', $debug);
- next NEW_LINE;
- } # ignoring further action parameters
- if ($line=~ /^\s+(log|count)\s\{$/ && $context eq 'security/policies/zone/policy/action') {
- my $found_track = $1;
- if ($track eq '') { $track = $1; }
- elsif ($found_track eq 'count' && $track eq 'log') { $track = 'log count'; }
- $context = &switch_context($context, 'security/policies/zone/policy/action/action_parameter', $debug);
- next NEW_LINE;
- } # ignoring further tracking parameters
-=cut
- track=log wird in diesem Fall nicht gefunden:
- permit {
- destination-address {
- drop-untranslated;
- }
- }
- log {
- session-close;
- }
-=cut
- if ($line=~ /^\s+.+?\{$/ && $context eq 'security/policies/zone/policy/action/action_parameter') {
- $context = &switch_context($context, 'security/policies/zone/policy/action/action_parameter/null', $debug);
- next NEW_LINE;
- }
-#closing sections
- if ($line=~ /^\s+.+?\{$/ && $context eq 'security/policies/zone/policy/action/action_parameter/null') {
- $context = &switch_context($context, 'security/policies/zone/policy/action/action_parameter', $debug);
- next NEW_LINE;
- }
- if ($line=~ /^\s+\}$/ && $context eq 'security/policies/zone/policy/action/action_parameter') { $context = &switch_context($context, 'security/policies/zone/policy/action', $debug); next NEW_LINE; }
- if ($line=~ /^\s+\}$/ && $context eq 'security/policies/zone/policy/action') { $context = &switch_context($context, 'security/policies/zone/policy', $debug); next NEW_LINE; }
- if ($line=~ /^\s+\}$/ && $context eq 'security/policies/zone/policy') {
- if (!defined($track)) { $track=''; }
- &print_debug("found rule from $from_zone to $to_zone, name=$policy_name, disabled=$disabled: src=$source, dst=$destination, svc=$application, action=$action, track=$track",$debug,2);
- # Regel wegschreiben
- $rule_no = &add_rule ($rule_no, $from_zone, $to_zone, $policy_name, $disabled, $source, $destination, $application, $action, $track, $debug);
- $context = &switch_context($context, 'security/policies/zone', $debug);
- $track = ''; undef($policy_name);
- next NEW_LINE;
- } # end of rule
- if ($line=~ /^\s+\}$/ && $context eq 'security/policies/zone') { $context = switch_context($context, 'security/policies', $debug); undef($from_zone); undef($to_zone); next NEW_LINE; }
- if ($line=~ /^\s+\}$/ && $context eq 'security/policies') { $context = &switch_context($context, 'security', $debug); next NEW_LINE; }
- if ($line=~ /^\}$/) { $context = &switch_context($context, '', $debug); next NEW_LINE; }
- }
- &sort_rules_and_add_zone_headers ();
- $rulebases{"$rulebase_name.ruleorder"} = join(',', @ruleorder);
- return 0;
- }
-
-#####################################################################################
-# MAIN
-
-sub parse_config { # ($obj_file, $rule_file, $rulebases, $user, $fworch_workdir, $debug_level, $mgm_name, $config_dir, $import_id)
- my $in_file_main = shift;
- shift; shift; shift; # $rule_file und $rulebases und $user nicht verwendet
- my $fworch_workdir = shift;
- my $debuglevel_main = shift;
- my $mgm_name = shift;
- my $config_dir = shift;
- my $import_id = shift;
- my $debug_level = shift;
-
- # initializing global variables:
- @services = ();
- @network_objects = ();
- &print_debug ("in_file_main=$in_file_main, fworch_workdir=$fworch_workdir, debuglevel_main=$debuglevel_main, mgm_name=$mgm_name, config_dir=$config_dir, import_id=$import_id", $debuglevel_main, 6);
-
- open (IN, $in_file_main) || die "$in_file_main konnte nicht geoeffnet werden.\n";
- @config_lines = ; # sichern Config-array fuer spaetere Verwendung
- close (IN);
-
- if (!&cfg_file_complete($debuglevel_main)) { return "incomplete-config-file-$mgm_name"; }
- else {
-# my $device_type=9; # ASA 8.x fest gesetzt # TODO move to config
-# &read_predefined_services($device_type, $debuglevel_main); # schreibt die predefined services in @services und %services
- my $mgm_name_in_config = &parse_mgm_name($in_file_main, $fworch_workdir, $debuglevel_main, $mgm_name, $config_dir, $import_id);
- &parse_config_base_objects ($in_file_main, $fworch_workdir, $debuglevel_main, $mgm_name, $config_dir, $import_id); # zones, simple network and service objects
- push @zones, "global"; # Global Zone immer hinzufuegen
- foreach my $zone (@zones) { object_address_add("any", "0.0.0.0", "0.0.0.0", $zone, "any-obj for Zone added by fworch"); &print_debug(""); } # Any-Objekte fuer alle Zonen einfuegen
- &parse_config_group_objects ($in_file_main, $fworch_workdir, $debuglevel_main, $mgm_name, $config_dir, $import_id); # groups are parsed in separate cycle to ensure that all base objects are complete
-# &resolve_service_uuid_references ($debuglevel_main);
- &parse_config_rules ($in_file_main, $fworch_workdir, $debuglevel_main, $mgm_name_in_config, $config_dir, $import_id); # finally parsing the rule base, ignoring the rulebase name in fworch config
-
- &print_results_files_objects($fworch_workdir, $mgm_name, $import_id);
- &print_results_files_rules ($fworch_workdir, $mgm_name, $import_id);
- &print_results_files_zones ($fworch_workdir, $mgm_name, $import_id);
-# print_results_monitor('objects');
-# print_results_monitor('rules');
- }
- return 0;
-}
-
-1;
-__END__
-
-=head1 NAME
-
-CACTUS::FWORCH::parser - Perl extension for fworch cisco parser
-
-=head1 SYNOPSIS
-
- use CACTUS::FWORCH::import::cisco;
-
-=head1 DESCRIPTION
-
-fworch perl Module support for importing configs into fworch Database
-
-=head2 EXPORT
-
- global variables
-
-=head1 SEE ALSO
-
- behind the door
-
-=cut
diff --git a/roles/importer/files/importer/CACTUS/FWORCH/import/fortinet.pm b/roles/importer/files/importer/CACTUS/FWORCH/import/fortinet.pm
deleted file mode 100644
index e85a030b87..0000000000
--- a/roles/importer/files/importer/CACTUS/FWORCH/import/fortinet.pm
+++ /dev/null
@@ -1,1226 +0,0 @@
-package CACTUS::FWORCH::import::parser;
-
-use strict;
-use warnings;
-use Time::HiRes qw(time); # fuer hundertstelsekundengenaue Messung der Ausfuehrdauer
-use CACTUS::FWORCH;
-use CACTUS::FWORCH::import;
-use CACTUS::read_config;
-
-require Exporter;
-our @ISA = qw(Exporter);
-
-our %EXPORT_TAGS = ( 'basic' => [ qw( ©_config_from_mgm_to_iso &parse_config ) ] );
-
-our @EXPORT = ( @{ $EXPORT_TAGS{'basic'} } );
-our $VERSION = '0.3';
-
-our @config_lines; # array der Zeilen des Config-Files im Originalzustand (kein d2u etc.)
-our $parser_mode; # Typ des Config-Formates (basic, data)
-our $rule_order = 0; # Reihenfolge der Rules im Configfile
-our $rulebase_name;
-
-#####################################
-# add_nw_obj
-# param1: obj_name
-# param2: obj_ip (with netmask in form 1.2.3.0/24 or ::/0)
-# param3: obj zone
-# param4: debuglevel [0-?]
-#####################################
-sub add_nw_obj {
- my $act_obj_name = shift;
- my $obj_ip = shift;
- my $obj_type = shift;
- my $act_obj_zone = shift;
- my $act_obj_comm = shift;
- my $act_obj_ipaddr_last = shift;
- my $uid_postfix = shift;
- my $debuglevel = shift;
- my $act_obj_nameZ = '';
- my $act_obj_ipaddr = '';
- my $act_obj_mask = '';
- my $act_obj_type = '';
- my $act_obj_loc = '';
- my $act_obj_color = 'black';
- my $act_obj_sys = '';
- my $act_obj_uid = '';
- my $ipv6 = 0;
-
- if (!defined($act_obj_ipaddr_last)) { $act_obj_ipaddr_last = ''; }
- if (!defined($obj_type)) { $obj_type = ''; }
- if (!defined($act_obj_comm)) { $act_obj_comm = ''; }
- print_debug("add_nw_obj called with name=$act_obj_name, ip=$obj_ip, type=$obj_type, obj_ipaddr_last=$act_obj_ipaddr_last, zone=$act_obj_zone", $debuglevel, 4);
-# if ($obj_type ne 'ip_range') {
- ($act_obj_ipaddr, $act_obj_mask) = split (/\//, $obj_ip);
- if ($obj_ip =~/\:/) { $ipv6 = 1; }
- print_debug("split: ip=$act_obj_ipaddr, mask=$act_obj_mask", $debuglevel, 7);
-# }
- $act_obj_nameZ = "${act_obj_name}$uid_postfix"; # ipv6_uid_postfix in Feldindex mit aufgenommen, um kollidierende Namen zu verhindern
- if (!defined ($network_objects{"$act_obj_nameZ.name"})) {
- @network_objects = (@network_objects, $act_obj_nameZ);
- $network_objects{"$act_obj_nameZ.name"} = $act_obj_name;
- $network_objects{"$act_obj_nameZ.UID"} = $act_obj_nameZ;
- if ($obj_type ne 'ip_range') {
- if ((!$ipv6 && $act_obj_mask==32) || ($ipv6 && $act_obj_mask==128)) { $obj_type = 'host' }
- else { $obj_type = 'network'; }
- }
- $network_objects{"$act_obj_nameZ.type"} = $obj_type;
- $network_objects{"$act_obj_nameZ.netmask"} = $act_obj_mask ;
- $network_objects{"$act_obj_nameZ.zone"} = $act_obj_zone; # neues Feld fuer Zone
- $network_objects{"$act_obj_nameZ.ipaddr"} = $act_obj_ipaddr;
- $network_objects{"$act_obj_nameZ.ipaddr_last"} = $act_obj_ipaddr_last;
- $network_objects{"$act_obj_nameZ.color"} = $act_obj_color;
- $network_objects{"$act_obj_nameZ.comments"} = $act_obj_comm;
- $network_objects{"$act_obj_nameZ.location"} = $act_obj_loc;
- $network_objects{"$act_obj_nameZ.sys"} = $act_obj_sys;
- } else {
- print_debug ("found duplicate object definition for network object $act_obj_name in zone $act_obj_zone", $debuglevel, -1);
- }
-}
-
-
-#####################################
-# add_nw_service_obj
-# param1: input-line
-# param2: debuglevel [0-?]
-#####################################
-sub add_nw_service_obj { # ($application_name, $proto, $source_port, $destination_port, $uuid, $rpc, $icmp_art, $icmp_nummer, $debug_level_main)
- my $act_obj_name = shift;
- my $act_obj_proto = shift;
- my $act_obj_src = shift;
- my $act_obj_dst = shift;
- my $act_obj_uid = shift;
- my $act_obj_rpc = shift;
- my $icmp_art = shift;
- my $icmp_nummer = shift;
- my $comment = shift;
- my $debuglevel = shift
- my @range;
- my $act_obj_typ = 'simple';
- my $act_obj_type = 'simple';
- my $act_obj_src_last = '';
- my $act_obj_dst_last = '';
- my $act_obj_time = '';
- my $act_obj_time_std = '';
- my $act_obj_color = 'black';
-
- if (!defined ($services{"$act_obj_name.name"})) {
- @services = (@services, $act_obj_name);
- $services{"$act_obj_name.name"} = $act_obj_name;
- if (defined($act_obj_src)) {
- @range = split ( /-/, $act_obj_src);
- $services{"$act_obj_name.src_port"} = $range[0];
- if (defined($range[1])) {
- $services{"$act_obj_name.src_port_last"} = $range[1];
- } else {
- $services{"$act_obj_name.src_port_last"} = '';
- }
- }
- if (defined($act_obj_dst)) {
- @range = split ( /-/, $act_obj_dst);
- $services{"$act_obj_name.port"} = $range[0];
- if (defined($range[1])) {
- $services{"$act_obj_name.port_last"} = $range[1];
- } else {
- $services{"$act_obj_name.port_last"} = '';
- }
- }
- if (defined($act_obj_proto)) {
- $services{"$act_obj_name.ip_proto"} = get_proto_number($act_obj_proto)
- } else {
- # $services{"$act_obj_name.ip_proto"} = '';
- }
- $services{"$act_obj_name.timeout"} = $act_obj_time;
- $services{"$act_obj_name.color"} = $act_obj_color;
- if (defined($comment) && $comment ne '') { $services{"$act_obj_name.comments"} = $comment; } else { $services{"$act_obj_name.comments"} = ''; }
- $services{"$act_obj_name.typ"} = $act_obj_typ;
- $services{"$act_obj_name.type"} = $act_obj_type;
- if (defined($act_obj_rpc)) {
- $services{"$act_obj_name.rpc_port"} = $act_obj_rpc;
- } else {
- $services{"$act_obj_name.rpc_port"} = '';
- }
- $services{"$act_obj_name.UID"} = $act_obj_name;
- print_debug("add_nw_service_obj: added application $act_obj_name", $debuglevel, 4);
- } else {
- print_debug("add_nw_service_obj: warning duplicate defintion of service $act_obj_name", $debuglevel, 1);
- }
- return;
-}
-
-#####################################
-# add_nw_obj_grp
-# param1: input-line
-# param2: debuglevel [0-?]
-#####################################
-sub add_nw_obj_grp { # ($obj_name, $members, $comment, $objgrp_uid, $debuglevel_main);
- my $act_obj_name = shift;
- my $act_obj_members = shift;
- my $act_obj_zone = shift;
- my $comment = shift;
- my $uid = shift;
- my $uid_postfix = shift;
- my $debuglevel = shift;
- my $act_obj_color = 'black';
- my $act_obj_nameZ;
- my $members_refs_local = '';
-
- print_debug("add_nw_obj_grp called with name=$act_obj_name", $debuglevel, 3);
-# $act_obj_nameZ = "${act_obj_name}"; # CHANGE: Zone nicht in Feldindex aufgenommen
- $act_obj_nameZ = "${act_obj_name}$uid_postfix"; # ipv6_uid_postfix in Feldindex mit aufgenommen, um kollidierende Namen zu verhindern
- if (!defined ($network_objects{"$act_obj_nameZ.name"})) {
- @network_objects = (@network_objects, $act_obj_nameZ);
- $network_objects{"$act_obj_nameZ.name"} = $act_obj_name;
-# if (defined($uid)) {
-# $network_objects{"$act_obj_nameZ.UID"} = $uid . $uid_postfix; # $act_obj_nameZ;
-# } else {
- $network_objects{"$act_obj_nameZ.UID"} = $act_obj_nameZ;
-# }
- $network_objects{"$act_obj_nameZ.type"} = 'group';
- $network_objects{"$act_obj_nameZ.zone"} = $act_obj_zone; # neues Feld fuer Zone
- $network_objects{"$act_obj_nameZ.color"} = $act_obj_color;
- $network_objects{"$act_obj_nameZ.comments"} = $comment;
- $network_objects{"$act_obj_nameZ.members"} = &fortinet_split_list($act_obj_members);
- if ($uid_postfix ne '') { # only add ipv6 postfix for non-ipv4 objects
- if ($act_obj_members =~ /^\"(.*?)\"$/) { # standard list format: "x1" "x2" "x3"
- my @tmp_list = split(/\"\s\"/, $1);
- foreach my $member_local (@tmp_list) {
- $members_refs_local .= '"' . $member_local . $uid_postfix . '" ';
- }
- if ($members_refs_local =~ /^(.*?)\s$/) { $members_refs_local = $1; }
- $network_objects{"$act_obj_nameZ.member_refs"} = &fortinet_split_list($members_refs_local);
- } else {
- print_debug ("add_nw_obj_grp::non-matching members format found $act_obj_name, members: $act_obj_members", $debuglevel, -1);
- }
- } else {
- $network_objects{"$act_obj_nameZ.member_refs"} = &fortinet_split_list($act_obj_members);
- }
- } else {
- print_debug ("found duplicate object definition for network object $act_obj_name in zone $act_obj_zone", $debuglevel, -1);
- }
-}
-
-#####################################
-# add_nw_service_obj_grp
-# param1: input-line
-# param2: debuglevel [0-?]
-#####################################
-sub add_nw_service_obj_grp { # ($application_name, $members, $members_proto, $members_uid, $comment, $debuglevel_main);
- my $act_obj_name = shift;
- my $act_obj_members = shift;
- my $comment = shift;
- my $debuglevel = shift;
- my $act_obj_typ = 'group';
- my $act_obj_type = 'group';
- my $act_obj_proto = '';
- my $act_obj_src_last = '';
- my $act_obj_dst_last = '';
- my $act_obj_time = '';
- my $act_obj_time_std = '';
- my $act_obj_color = 'black';
- my $act_obj_rpc = '';
- my $mbrlst = '';
-
- @services = (@services, $act_obj_name);
- $services{"$act_obj_name.name"} = $act_obj_name;
- $services{"$act_obj_name.members"} = &fortinet_split_list($act_obj_members);
- $services{"$act_obj_name.member_refs"} = &fortinet_split_list($act_obj_members);
- print_debug("adding service group $act_obj_name with members $act_obj_members", $debuglevel, 6);
- $services{"$act_obj_name.ip_proto"} = $act_obj_proto;
- $services{"$act_obj_name.timeout"} = $act_obj_time;
- $services{"$act_obj_name.color"} = $act_obj_color;
- if (defined($comment) && $comment ne '') { $services{"$act_obj_name.comments"} = $comment; } else { $services{"$act_obj_name.comments"} = ''; }
- $services{"$act_obj_name.typ"} = $act_obj_typ;
- $services{"$act_obj_name.type"} = $act_obj_type;
- $services{"$act_obj_name.rpc_port"} = $act_obj_rpc;
- $services{"$act_obj_name.UID"} = $act_obj_name;
-}
-
-sub fortinet_split_list { # param1 = list of objects (network or service)
- my $list = shift;
- my $debug_level = shift;
- my $orig_list = $list;
-
- if ($list =~ /^\"(.*?)\"$/) { # standard list format: "x1" "x2" "x3"
- $list = $1;
- $list = join('|', split(/\"\s\"/, $list));
- } else {
- print_debug("warning in fortinet_split_list: orig_list=$orig_list; found no match for object list", $debug_level, 1);
- }
-# print_debug("fortinet_split_list: orig_list=$orig_list, result=$list", $debug_level, 5);
- return $list;
-}
-
-#####################################
-# add_rule
-# param1:
-# debuglevel [integer]
-#####################################
-sub add_rule { # ($rule_no, $from_zone, $to_zone, $policy_id, $disabled, $source, $destination, $application, $action, $track, $debuglevel_main)
- my $rule_no = shift;
- my $from_zone = shift;
- my $to_zone = shift;
- my $policy_id = shift;
- my $disabled = shift;
- my $source = shift;
- my $destination = shift;
- my $service = shift;
- my $action = shift;
- my $track = shift;
- my $comment = shift;
- my $policy_name = shift;
- my $svc_neg = shift;
- my $src_neg = shift;
- my $dst_neg = shift;
- my $uid_postfix = shift;
- my $debuglevel = shift;
- my $rule_id;
-
-
-# print_debug ("add_rule: rulebase_name=$rulebase_name, rulecount=" . $rulebases{"$rulebase_name.rulecount"}, $debuglevel, 4);
- $rulebases{"$rulebase_name.rulecount"} = $rule_no + 1; # Anzahl der Regeln wird sukzessive hochgesetzt
-# $rule_id = "from_zone__$from_zone" . "__to_zone__$to_zone" . "__$rule_id";
- $ruleorder[$rule_no] = $rule_no;
-
- if (!defined($track) || $track eq '') { $track = 'none'; }
- if (length($track)<3) { print_debug ("warning, short track: <$track>", $debuglevel, 1); }
-
- $rulebases{"$rulebase_name.$rule_no.src"} = '';
- foreach my $src (split(/\|/, &fortinet_split_list($source, $debuglevel))) {
- if ($rulebases{"$rulebase_name.$rule_no.src"} ne '') {
- $rulebases{"$rulebase_name.$rule_no.src"} .= '|';
- $rulebases{"$rulebase_name.$rule_no.src.refs"} .= '|';
- }
- $rulebases{"$rulebase_name.$rule_no.src"} .= "$src";
-# $rulebases{"$rulebase_name.$rule_no.src.refs"} .= ("$src" . "__zone__$from_zone");
- $rulebases{"$rulebase_name.$rule_no.src.refs"} .= ("$src$uid_postfix"); # CHANGE
- }
- $rulebases{"$rulebase_name.$rule_no.dst"} = '';
- foreach my $dst (split(/\|/, &fortinet_split_list($destination, $debuglevel))) {
- if ($rulebases{"$rulebase_name.$rule_no.dst"} ne '') {
- $rulebases{"$rulebase_name.$rule_no.dst"} .= '|';
- $rulebases{"$rulebase_name.$rule_no.dst.refs"} .= '|';
- }
- $rulebases{"$rulebase_name.$rule_no.dst"} .= "$dst";
-# $rulebases{"$rulebase_name.$rule_no.dst.refs"} .= ("$dst" . "__zone__$to_zone");
- $rulebases{"$rulebase_name.$rule_no.dst.refs"} .= ("$dst$uid_postfix"); # CHANGE
- }
-
- $rulebases{"$rulebase_name.$rule_no.services"} = '';
- foreach my $svc (split(/\|/, &fortinet_split_list($service, $debuglevel))) {
- if ($rulebases{"$rulebase_name.$rule_no.services"} ne '') {
- $rulebases{"$rulebase_name.$rule_no.services"} .= '|';
- $rulebases{"$rulebase_name.$rule_no.services.refs"} .= '|';
- }
- $rulebases{"$rulebase_name.$rule_no.services"} .= "$svc";
- $rulebases{"$rulebase_name.$rule_no.services.refs"} .= "$svc";
- }
-
- $rulebases{"$rulebase_name.$rule_no.id"} = $policy_id;
- $rulebases{"$rulebase_name.$rule_no.ruleid"} = $policy_id;
- $rulebases{"$rulebase_name.$rule_no.order"} = $rule_no;
- if ($disabled eq 'inactive') { $rulebases{"$rulebase_name.$rule_no.disabled"} = '1'; }
- else { $rulebases{"$rulebase_name.$rule_no.disabled"} = '0'; }
- $rulebases{"$rulebase_name.$rule_no.src.zone"} = $from_zone;
- $rulebases{"$rulebase_name.$rule_no.dst.zone"} = $to_zone;
- $rulebases{"$rulebase_name.$rule_no.services.op"} = $svc_neg;
- $rulebases{"$rulebase_name.$rule_no.src.op"} = $src_neg;
- $rulebases{"$rulebase_name.$rule_no.dst.op"} = $dst_neg;
- $rulebases{"$rulebase_name.$rule_no.action"} = $action;
- $rulebases{"$rulebase_name.$rule_no.track"} = $track;
- $rulebases{"$rulebase_name.$rule_no.install"} = ''; # set hostname verwenden ?
- $rulebases{"$rulebase_name.$rule_no.name"} = $policy_name;
- $rulebases{"$rulebase_name.$rule_no.time"} = '';
- if (defined($comment) && $comment ne '') { $rulebases{"$rulebase_name.$rule_no.comments"} = $comment; }
- $rulebases{"$rulebase_name.$rule_no.UID"} = $policy_id;
- $rulebases{"$rulebase_name.$rule_no.header_text"} = '';
- print_debug ("added_rule: rulebase_name=$rulebase_name, from_zone: " . $from_zone . ", to_zone: " . $to_zone, $debuglevel, 4);
- return $rule_no+1;
-}
-
-############################################################
-# add_zone ($new_zone)
-############################################################
-sub add_zone {
- my $new_zone = shift;
- my $debug = shift;
- my $is_there = 0;
- foreach my $elt (@zones) { if ($elt eq $new_zone) { $is_there = 1; last; } }
- if (!$is_there) { push @zones, $new_zone; &print_debug("adding new zone: $new_zone", $debug, 4); }
-}
-
-############################################################
-# object_address_add (name, ip, mask, zone, comment)
-############################################################
-sub object_address_add {
- my $act_obj_name = $_[0];
- my $act_obj_ipaddr = $_[1];
- my $act_obj_mask = $_[2];
- my $act_obj_zone = $_[3];
- my $act_obj_comm = $_[4];
- my @params;
- my $act_obj_nameZ = '';
- my $act_obj_ipaddr_last = '';
- my $act_obj_type = 'simple';
- my $act_obj_loc = '';
- my $act_obj_color = 'black';
- my $act_obj_sys = '';
- my $act_obj_uid = '';
-
-# $act_obj_nameZ = "${act_obj_name}__zone__$act_obj_zone"; # Zone in Feldindex mit aufgenommen
- $act_obj_nameZ = "${act_obj_name}"; # CHANGE Zone nicht in Feldindex aufgenommen
- if (!defined ($network_objects{"$act_obj_nameZ.name"})) {
- @network_objects = (@network_objects, $act_obj_nameZ);
- $network_objects{"$act_obj_nameZ.name"} = $act_obj_name;
- } elsif (defined ($network_objects{"$act_obj_nameZ.name"})) {
- print "sub ns_object_address NET_OBJECT: $act_obj_nameZ ist bereits definiert.\n";
- } else {
- print "sub ns_object_address NET_OBJECT: $act_obj_nameZ ist undefiniert.\n";
- }
- my $subnetbits = &calc_subnetmask ($act_obj_mask);
- if ($subnetbits==32) { $network_objects{"$act_obj_nameZ.type"} = 'host' };
- if ($subnetbits<32) { $network_objects{"$act_obj_nameZ.type"} = 'network' };
- $network_objects{"$act_obj_nameZ.netmask"} = $act_obj_mask ;
- $network_objects{"$act_obj_nameZ.zone"} = $act_obj_zone; # neues Feld fuer Zone
- $network_objects{"$act_obj_nameZ.ipaddr"} = $act_obj_ipaddr;
- $network_objects{"$act_obj_nameZ.ipaddr_last"} = $act_obj_ipaddr_last;
- $network_objects{"$act_obj_nameZ.color"} = $act_obj_color;
- $network_objects{"$act_obj_nameZ.comments"} = $act_obj_comm;
- $network_objects{"$act_obj_nameZ.location"} = $act_obj_loc;
- $network_objects{"$act_obj_nameZ.sys"} = $act_obj_sys;
- $network_objects{"$act_obj_nameZ.UID"} = $act_obj_nameZ;
-}
-
-############################################################
-# copy_config_from_mgm_to_iso($ssh_private_key, $ssh_user, $ssh_hostname, $management_name,
-# $obj_file_base, $cfg_dir, $rule_file_base)
-# Kopieren der Config-Daten vom Management-System zum ITSecorg-Server
-############################################################
-
-
-sub copy_config_from_mgm_to_iso {
- my $ssh_user = shift;
- my $ssh_hostname = shift;
- my $management_name = shift; # not used
- my $obj_file_base = shift;
- my $cfg_dir = shift;
- my $rule_file_base = shift;
- my $workdir = shift;
- my $auditlog = shift;
- my $prev_import_time= shift;
- my $ssh_port = shift;
- my $config_path_on_mgmt = shift;
- my $debug_level = shift;
- my $cmd;
- my $fehler_count = 0;
- my $result;
-
- if ($config_path_on_mgmt ne '') { # not a real fortigate but a standard ssh server
- $cmd = "$scp_bin $scp_batch_mode_switch -i $workdir/${CACTUS::FWORCH::ssh_id_basename} $ssh_user\@$ssh_hostname:$config_path_on_mgmt/fortigate.cfg $cfg_dir/$obj_file_base";
- } else { # standard fortigate
- $cmd = "$ssh_bin -o StrictHostKeyChecking=no -i $workdir/${CACTUS::FWORCH::ssh_id_basename} $ssh_user\@$ssh_hostname show full-configuration > $cfg_dir/$obj_file_base"; # fortigate
- # adding "-o StrictHostKeyChecking=no" to allow for failover of fortinet machines:
- }
- #print_debug("copy_config_from_mgm_to_iso cmd=$cmd", $debuglevel, 4);
- if (system ($cmd)) { $fehler_count++; }
- return ($fehler_count, "$cfg_dir/$obj_file_base" );
-}
-
-sub sort_rules_and_add_zone_headers {
- my $debug = shift;
- my $anzahl_regeln;
- my $count;
- my $zone_string;
- my @rule_zones = ();
-
- # Nachbereitung Regeln: Sortierung nach a) Zonen b) $ruleorder
- if (!defined($rulebases{"$rulebase_name.rulecount"})) {
- $anzahl_regeln = 0;
- } else {
- $anzahl_regeln = $rulebases{"$rulebase_name.rulecount"};
- }
- for ($count=0; $count<$anzahl_regeln; $count++) {
- $zone_string = $rulebases{"$rulebase_name.$ruleorder[$count].src.zone"};
- $zone_string .= " : ";
- $zone_string .= $rulebases{"$rulebase_name.$ruleorder[$count].dst.zone"};
- push @rule_zones, $zone_string
- }
- my @idx = ();
- my $item;
- for (@rule_zones) {
- ($item) = $_;
- push @idx, $item;
- }
-
- @ruleorder = @ruleorder[ sort { $idx[$a] cmp $idx[$b] } 0 .. $anzahl_regeln-1 ];
- @rule_zones = @rule_zones[ sort { $idx[$a] cmp $idx[$b] } 0 .. $anzahl_regeln-1 ];
-
- # Nachbereitung Regeln: Header-Regeln vor Zonenwechsel einfuegen
- my $new_zone_string;
- my $old_zone_string = "";
- my $rule_header_count = 1;
- my $rule_header_offset = &CACTUS::read_config::read_config('rule_header_offset') * 1;
- my $new_rule_id;
- for ($count = 0; $count < $anzahl_regeln; $count++) {
- $new_zone_string = $rule_zones[$count];
- if ($new_zone_string ne $old_zone_string) { # insert header rule
- $new_rule_id = $rule_header_offset+$rule_header_count++;
- (my $src_zone, my $dst_zone) = split (/ : /, $new_zone_string);
- splice(@ruleorder,$count,0,$new_rule_id); # fuegt neue Regel ein
- splice(@rule_zones,$count,0,$new_zone_string);
- $anzahl_regeln++;
- $rulebases{"$rulebase_name.rulecount"} = $anzahl_regeln;
- $rulebases{"$rulebase_name.$ruleorder[$count].id"} = $new_rule_id;
- $rulebases{"$rulebase_name.$ruleorder[$count].header_text"} = $new_zone_string;
- $rulebases{"$rulebase_name.$ruleorder[$count].UID"} = $new_rule_id;
- $rulebases{"$rulebase_name.$ruleorder[$count].src"} = "all";
- $rulebases{"$rulebase_name.$ruleorder[$count].src.refs"} = "all";
- $rulebases{"$rulebase_name.$ruleorder[$count].dst"} = "all";
- $rulebases{"$rulebase_name.$ruleorder[$count].dst.refs"} = "all";
- $rulebases{"$rulebase_name.$ruleorder[$count].services"} = "all";
- $rulebases{"$rulebase_name.$ruleorder[$count].action"} = "deny";
- $rulebases{"$rulebase_name.$ruleorder[$count].src.zone"} = $src_zone;
- $rulebases{"$rulebase_name.$ruleorder[$count].dst.zone"} = $dst_zone;
- $rulebases{"$rulebase_name.$ruleorder[$count].disabled"} = '0';
- $rulebases{"$rulebase_name.$ruleorder[$count].src.op"} = '0';
- $rulebases{"$rulebase_name.$ruleorder[$count].dst.op"} = '0';
- $rulebases{"$rulebase_name.$ruleorder[$count].services.op"} = '0';
- }
- $old_zone_string = $new_zone_string;
- }
-=cut
- # moving any:any section down to the end
- my $destination_rule_no;
- for ($count = $anzahl_regeln-1; $count >= 0; $count--) {
- if ($rule_zones[$count] eq 'any : any') { # cut rule within any:any section and paste at the end
-
- $destination_rule_no = $anzahl_regeln-1;
-
- # this is the header rule to be moved to the top of the any:any section:
- while ($rule_zones[$destination_rule_no] eq 'any : any' && $rulebases{"$rulebase_name.$ruleorder[$count].header_text"} ne '') {
- $destination_rule_no--;
- }
-
- # this is a deny rule (and no section header) to be moved to the bottom of the any:any section:
- if ($rulebases{"$rulebase_name.$ruleorder[$count].action"} eq 'deny' && $rulebases{"$rulebase_name.$ruleorder[$count].header_text"} eq '') {
- $destination_rule_no = $anzahl_regeln-1;
- }
-
- # make sure that standard any:any rules are inserted before deny rules
- while ($rule_zones[$destination_rule_no] eq 'any : any' && $rulebases{"$rulebase_name.$ruleorder[$destination_rule_no].action"} eq 'deny' &&
- $rulebases{"$rulebase_name.$ruleorder[$count].action"} ne 'deny' &&
- $rulebases{"$rulebase_name.$ruleorder[$count].header_text"} eq '') {
- $destination_rule_no--;
- }
-
- if ($count ne $destination_rule_no) { # avoid moving on the same spot
- &print_debug("moving any : any rule with id " . $rulebases{"$rulebase_name.$ruleorder[$count].id"} .
- " from pos $count to pos $destination_rule_no", $debug, 2);
- splice( @ruleorder, $destination_rule_no, 0, splice( @ruleorder, $count, 1 ) );
- splice( @rule_zones, $destination_rule_no, 0, splice( @rule_zones, $count, 1 ) );
- }
- }
- }
-=cut
- # moving any:any section down to the end
- my ($destination_rule_no, $first_any_rule, $last_any_rule, $number_of_any_rules);
- for ($count=0; $count<$anzahl_regeln; $count++) {
- if (!defined($first_any_rule) && $rule_zones[$count] eq 'any : any') { $first_any_rule = $count; }
- if (defined($first_any_rule) && !defined($last_any_rule) && $rule_zones[$count] ne 'any : any') { $last_any_rule = $count-1; }
- }
- if (defined($first_any_rule) && !defined($last_any_rule)) { $last_any_rule = $number_of_any_rules-1; }
- if (defined($first_any_rule)) { # only move any:any rules if they exist
- $number_of_any_rules = $last_any_rule - $first_any_rule + 1;
- $destination_rule_no = $anzahl_regeln-$number_of_any_rules;
- &print_debug("moving any:any rules from pos $first_any_rule to pos $last_any_rule to the bottom ($destination_rule_no)", $debug, 2);
- splice( @ruleorder, $destination_rule_no, 0, splice( @ruleorder, $first_any_rule, $number_of_any_rules ) );
- splice( @rule_zones, $destination_rule_no, 0, splice( @rule_zones, $first_any_rule, $number_of_any_rules ) );
- }
-}
-
-sub parse_mgm_name { # params: $debug_level
- # since fortiOS 5.6 the hostname is not part of the config?!
- my $debuglevel_main = shift;
- my $mgm_name = '';
- my $line = '';
- my $context = '';
- my @nodes= ();
-
- &print_debug("entering parse_mgm_name",$debuglevel_main,2);
- NEW_LINE: foreach $line (@config_lines) {
- chomp($line);
- print_debug("parse_mgm_name::parsing line $line", $debuglevel_main, 9);
-# parsing device name(s) --- set hostname "xxxx"
- if ($line =~ /^config\ssystem\sglobal/ && $context eq '') { $context = "system global"; &print_debug("found system line: $line",$debuglevel_main,5); next NEW_LINE; }
- if ($line=~ /^\s+set\s+hostname\s+\"(.+?)\"$/ && ($context eq 'system global')) {
- $mgm_name = $1;
- @nodes = ($mgm_name, @nodes);
- &print_debug("parse_mgm_name: found hostname: $mgm_name",$debuglevel_main,1);
- next NEW_LINE;
- }
- if ($line=~ /^end$/ && $context eq 'system global') {
- $context = '';
- return $mgm_name;
- }
- }
- &print_debug("ERROR: end of parse_mgm_name: at end without match (mgm_name=$mgm_name)",$debuglevel_main,-1);
- return 0;
-}
-
-sub parse_vdom_names { # ($debug_level)
- my $debuglevel_main = shift;
- my $line = '';
- my $context = '';
- my @vdom= ();
-
- &print_debug("entering parse_vdom_names",$debuglevel_main,2);
- NEW_LINE: foreach $line (@config_lines) {
- chomp($line);
- print_debug("parse_vdom_names::parsing line $line", $debuglevel_main, 9);
- if ($line=~ /^config\svdom$/ && $context eq '') { $context = 'vdom'; next NEW_LINE; }
- if ($line=~ /^edit\s(.+?)$/ && $context eq 'vdom') { @vdom = (@vdom, $1); next NEW_LINE; }
- if ($line=~ /^end$/ && $context eq 'vdom') { $context = ''; return join(',', @vdom); }
- }
- &print_debug("end of parse_vdom_names: at end without any vdom match",$debuglevel_main,2);
- return '';
-}
-
-# the following function does only parse simple objects without groups. Groups are parsed in a second run using function parse_config_group_objects
-sub parse_config_base_objects { # ($debug_level, $mgm_name)
- sub transform_port_list_to_artificial_services {
- # params: ($destination_port, $proto, $application_name, $source_port, $uuid, $rpc, $icmp_art, $icmp_nummer, $comment, $debug)
- my $destination_port = shift;
- my $proto = shift;
- my $application_name = shift;
- my $source_port = shift;
- my $uuid = shift;
- my $rpc = shift;
- my $icmp_art = shift;
- my $icmp_nummer = shift;
- my $comment = shift;
- my $debug = shift;
- my $members = '';
-
- foreach my $current_port (split (/\s/, $destination_port)) {
- if ($current_port =~ /^(.+?)\:(.+?)$/) { # handle case: set tcp-portrange 0-65535:0-65535
- $current_port = $1;
- $source_port = $2;
- }
- my $member = "${application_name}_FWORCH-multiGRP-${current_port}_$proto";
- $members .= '"' . $member . '" ';
- print_debug("adding arty service ${application_name}::$member with dst-port $current_port", $debug, 7);
- &add_nw_service_obj ($member, $proto, $source_port, $current_port, $uuid, $rpc, $icmp_art, $icmp_nummer, $comment, $debug);
- }
- $members =~ s/\s$//; # remove trailing blank
- return $members
- }
-
- my $debug = shift;
- my $mgm_name = shift;
- my ($zone, $address_group_name, $obj_type, $obj_name, $obj_ip, $obj_ip_last, $obj_netmask, $group_member_name, $application_name, $group_name, $group_members, $proto, $icmp_art, $icmp_nummer,
- $source_port, $destination_port, $uuid, $members, $members_uid, $members_proto, $rpc, $comment, $uid_postfix);
- my $line = '';
- my $context = '';
- my @nodes= ();
- my $v6flag = 0;
-
- &print_debug("entering parse_config_base_objects =======================================================",$debug,2);
- NEW_LINE: foreach $line (@config_lines) {
- chomp($line);
- &print_debug("pcbo-line: $line", $debug, 9);
- if ($line =~ /^end$/ && ($context eq 'firewall address' || $context eq 'firewall addrgrp' || $context eq 'firewall vip')) {
- &print_debug("switching back to top level, resetting uid_postfix", $debug, 6);
- $context = &switch_context($context, '', $debug); $v6flag = 0; $uid_postfix = ''; next NEW_LINE;
- }
- if ($line =~ /^config firewall (multicast\-)?address(6?)$/ && $context eq '') {
- my $mc = $1;
- my $v6 = $2;
- $v6flag = 0; $uid_postfix = '';
- if (defined($mc) && $mc eq 'multicast-') { $uid_postfix = '.multicast.uid'; }
- if (defined($v6) && $v6 eq '6') {
- $v6flag = 1;
- if ($uid_postfix eq '.multicast.uid') { $uid_postfix = '.multicast.ipv6.uid'; } else { $uid_postfix = '.ipv6.uid'; }
- }
- &print_debug("switching to firewall address, uid_postfix=$uid_postfix", $debug, 6);
- $context = &switch_context($context, 'firewall address', $debug);
- next NEW_LINE;
- }
- if ($line =~ /^config firewall addrgrp(6?)$/ && $context eq '') {
- if (defined($1) && $1 eq '6') { $v6flag = 1; $uid_postfix = ".ipv6.uid"; } else { $v6flag = 0; $uid_postfix = ''; }
- &print_debug("switching to firewall addrgrp, v6_uid_postfix=$uid_postfix", $debug, 6);
- $context = &switch_context($context, 'firewall addrgrp', $debug);
- next NEW_LINE;
- }
- if ($line =~ /^\s+edit\s\"(.+)"$/ && ($context eq 'firewall address' || $context eq 'firewall addrgrp')) {
- $obj_name = $1;
- print_debug("found object name $obj_name", $debug, 6);
- $context = &switch_context($context, $context . ' single object', $debug);
- next NEW_LINE;
- }
- if ($line =~ /^\s+next$/ && $context eq 'firewall address single object') {
- if (!defined($zone)) { $zone = 'global'; }
- if (!defined($obj_ip)) {
- if ($v6flag==1) {
- $obj_ip = '::'; $obj_netmask = '0';
- } else {
- $obj_ip = '0.0.0.0'; $obj_netmask = '0.0.0.0';
- }
- }
- if (!defined($obj_ip_last)) { $obj_ip_last = ''; }
- if (!defined($obj_type)) { $obj_type = ''; }
- if ($obj_type eq 'interface-subnet')
- {
- # interface-subnet is not CIDR conform, therefore we change the netmask to a single host
- $obj_type = 'host';
- if ($v6flag==1)
- {
- $obj_netmask = '128';
- }
- else
- {
- $obj_netmask = '255.255.255.255';
- }
- }
- if (!defined($comment)) { $comment = ''; }
- if (!defined($obj_netmask)) { $obj_netmask = '255.255.255.255'; }
- if (!$v6flag) { $obj_netmask = &calc_subnetmask($obj_netmask); }
- print_debug("adding nw_obj type=$obj_type, name=$obj_name with ip $obj_ip/$obj_netmask, obj_ip_last=$obj_ip_last, zone=$zone", $debug, 5);
- &add_nw_obj ($obj_name, "$obj_ip/$obj_netmask", $obj_type, $zone, $comment, $obj_ip_last, $uid_postfix, $debug);
- undef($obj_name); undef($obj_ip); undef($comment); undef($zone); undef($obj_ip_last); undef($obj_type); undef($obj_netmask);
- $context = &switch_context($context, 'firewall address', $debug);
- next NEW_LINE;
- }
- # adding zone
- if ($line=~ /^\s+set\sassociated\-interface\s\"(.+?)\"$/ && ($context eq 'firewall address single object' || $context eq 'firewall addrgrp single object')) {
- $zone = $1; &add_zone ($zone, $debug);
- print_debug("found zone $zone", $debug, 2);
- next NEW_LINE;
- }
- if ($line =~ /^\s+set\suuid\s(\w+)$/ && ($context eq 'firewall address single object' || $context eq 'firewall addrgrp single object' ||
- $context eq 'firewall vip single object')) {
- $uuid = $1;
- print_debug("found object uid $uuid", $debug, 4);
- next NEW_LINE;
- }
- if ($line =~ /^\s+set\stype\s([\w\-]+)$/ && $context eq 'firewall address single object') {
- $obj_type = $1;
- if ($obj_type eq 'multicastrange' || $obj_type eq 'iprange') { $obj_type = 'ip_range'; }
- print_debug("found object type $obj_type", $debug, 4);
- next NEW_LINE;
- }
- if ($line =~ /^\s+set\scomment\s[\'\"](.*?)[\'\"]$/ && ($context eq 'firewall address single object' || $context eq 'firewall addrgrp single object'
- || $context eq 'firewall vip single object')) {
- if (defined($comment) && $comment ne '') { $comment .= "; $1"; } else { $comment = $1; }
- print_debug("found object comment $comment", $debug, 4);
- next NEW_LINE;
- }
- if ($line =~ /^\s+set\s(wildcard\-)?fqdn\s\"(.*?)\"$/ && ($context eq 'firewall address single object' || $context eq 'firewall addrgrp single object')) {
- my ($wcard, $fqdn);
- if (defined($1)) { $wcard = $1; } else { $wcard = ''; }
- $fqdn = $2;
- if (defined($comment) && $comment ne '') { $comment .= "; ${wcard}fqdn $fqdn"; } else { $comment = "${wcard}fqdn $fqdn"; }
- print_debug("found fqdn object $comment", $debug, 4);
- next NEW_LINE;
- }
-
- if ($line =~ /^\s+set\sassociated\-interface\s\"(\w+)\"$/ && $context eq 'firewall address single object') {
- $zone = $1;
- print_debug("found object zone $zone", $debug, 4);
- next NEW_LINE;
- }
- if ($line =~ /^\s+set\ssubnet\s(.+?)\s(.+?)$/ && $context eq 'firewall address single object') {
- $obj_ip = $1;
- $obj_netmask = $2;
- print_debug("found object ip $obj_ip with mask $obj_netmask", $debug, 4);
- next NEW_LINE;
- }
-########### multicast/ip range ipv4 network objects #####################
- if ($context eq 'firewall address single object' && $line =~ /^\s+set\sstart\-ip\s([\d\.]+)$/) {
- $obj_ip = $1;
- print_debug("found ip range start-ip $obj_ip, context=$context", $debug, 6);
- next NEW_LINE;
- }
- if ($context eq 'firewall address single object' && $line =~ /^\s+set\send\-ip\s([\d\.]+)$/) {
- $obj_ip_last = "$1";
- print_debug("found ip range end-ip $obj_ip_last, context=$context", $debug, 6);
- next NEW_LINE;
- }
-########### NAT network objects #####################
- if ( $context eq '' && $line =~ /^config firewall vip$/) { $context = &switch_context($context, 'firewall vip', $debug); next NEW_LINE; }
-
- if ($context eq 'firewall vip' && $line =~ /^\s+edit\s\"(.+)"$/) {
- $obj_name = $1;
- print_debug("found vip object name $obj_name", $debug, 4);
- $context = &switch_context($context, $context . ' single object', $debug);
- next NEW_LINE;
- }
- if ($context eq 'firewall vip single object' && $line =~ /^\s+set\sextip\s([\d\.]+)\-?(.*?)$/) {
- $obj_ip = $1;
- print_debug("found nat object (starting) ip $obj_ip", $debug, 5);
- if (defined($2) && $2 ne '') { $obj_ip_last = "$2"; $obj_type = 'ip_range';
- print_debug("found nat object (ending) ip $obj_ip_last", $debug, 5);
- } else { $obj_type = 'host'; }
- next NEW_LINE;
- }
- if ($line =~ /^\s+set\stype\s(\w+?)\-nat$/ && $context eq 'firewall vip single object') {
- $obj_type = $1;
- print_debug("found object type $obj_type", $debug, 4);
- $obj_type = 'host'; # dirty - resolve this to ip range type?
- next NEW_LINE;
- }
- if ($line=~ /^\s+set\sextintf\s\"(.+?)\"$/ && $context eq 'firewall vip single object') {
-# $zone = $1;
-# if ($zone ne 'any') {
-# &add_zone ($zone, $debug);
-# print_debug("found zone $zone", $debug, 2);
-# } else {
-# $zone = 'global';
-# }
- # above is not working with latest NAT example; so simply defining all VIPs as in zone global
- $zone = 'global';
- next NEW_LINE;
- }
- if ($line =~ /^\s+next$/ && $context eq 'firewall vip single object') {
- if (!defined($zone)) { $zone = 'global'; }
- $obj_netmask = '255.255.255.255';
- print_debug("found nat obj $obj_name with ip $obj_ip in zone $zone", $debug, 5);
- &add_nw_obj ($obj_name, $obj_ip . '/' . &calc_subnetmask ($obj_netmask), $obj_type, $zone, $comment, $obj_ip_last, $uid_postfix, $debug);
- undef($obj_name); undef($obj_ip); undef($comment); undef($zone);
- $context = &switch_context($context, 'firewall vip', $debug);
- next NEW_LINE;
- }
-
-#################### network object group ####################################
- if ($line =~ /^\s+set\smember\s(.+)$/ && $context eq 'firewall addrgrp single object') {
- $members = $1;
- print_debug("found object member string $members", $debug, 4);
- next NEW_LINE;
- }
- if ($line =~ /^\s+next$/ && $context eq 'firewall addrgrp single object') {
- # address group wegschreiben
- if (!defined($zone)) { $zone = 'global'; }
- &add_nw_obj_grp ($obj_name, $members, $zone, $comment, $uuid, $uid_postfix, $debug);
- undef($obj_name); undef($members); undef($comment);
- $context = &switch_context($context, 'firewall addrgrp', $debug);
- next NEW_LINE;
- }
-
-########### service section
-# --------------- parsing network services ----------------
- if ($line =~ /^config firewall service custom$/ && $context eq '') {
- $context = &switch_context($context, 'firewall service custom', $debug);
- next NEW_LINE;
- }
- if ($line =~ /^end$/ && $context eq 'firewall service custom') {
- $context = &switch_context($context, '', $debug);
- next NEW_LINE;
- }
- if ($line =~ /\s+edit\s\"(.+?)\"$/ && $context eq 'firewall service custom') {
- $application_name = $1;
- print_debug("found application $application_name", $debug, 5);
- $context = &switch_context($context, 'firewall service custom single', $debug);
- next NEW_LINE;
- }
- if ($line=~ /^\s+set\scomment\s[\'\"](.+?)[\'\"]$/ && $context eq 'firewall service custom single') { $comment = $1; }
-# if ($line =~ /^\s+icmp\-(.+?)\s(\d+)\;$/ && $context eq 'applications/application') { $icmp_art = $1; $icmp_nummer = $2; next NEW_LINE; }
-
- if ($line =~ /\s+set\sprotocol\sICMP$/ && $context eq 'firewall service custom single') {
- $proto = 'icmp';
- print_debug("found icmp based service $application_name", $debug, 5);
- next NEW_LINE;
- }
-# set tcp-portrange 22:1024-65535 143-145:100-200 22123
-# set udp-portrange 68 889 789-891
-# rebuild this into a group of services with single ports
- if ($line =~ /\s+set\s(udp|tcp)\-portrange\s(.+?)$/ &&
- ($context eq 'firewall service custom single' || $context eq 'firewall service single multi-port group')) {
- my $old_proto = $proto;
- my $old_destination_port = $destination_port;
- $proto = $1;
- $destination_port = $2;
- if (defined($old_proto)) { # this is not the first portrange line for this service
- $context = &switch_context($context, 'firewall service single multi-port group', $debug);
- print_debug("starting multi-port firewall service group due to multi-proto service $application_name", $debug, 5);
- # transform old portrange into group
- $members = &transform_port_list_to_artificial_services($old_destination_port, $old_proto, $application_name,
- $source_port, $uuid, $rpc, $icmp_art, $icmp_nummer, $comment, $debug);
- print_debug("arty service 1st of 2 lines $application_name members: $members", $debug, 7);
- $members .= " " . &transform_port_list_to_artificial_services($destination_port, $proto, $application_name,
- $source_port, $uuid, $rpc, $icmp_art, $icmp_nummer, $comment, $debug);
- print_debug("arty service 1st & 2nd line $application_name members: $members", $debug, 7);
- } elsif ($destination_port =~ /\s/) { # more than one port separated by space - but first portrange line encountered
- print_debug("starting multi-port firewall service group single proto $application_name", $debug, 5);
- $context = &switch_context($context, 'firewall service single multi-port group', $debug);
- $members = &transform_port_list_to_artificial_services($destination_port, $proto, $application_name,
- $source_port, $uuid, $rpc, $icmp_art, $icmp_nummer, $comment, $debug);
- print_debug("arty service 1st line $application_name members: $members", $debug, 5);
- } else { # single port range
- print_debug("starting vanilla service $application_name", $debug, 6);
- # nothing else todo except for checking for source port
- if ($destination_port =~ /^(.+?)\:(.+?)$/) { # handle port ranges including source ports: set tcp-portrange 0-65535:0-65535
- $destination_port = $1;
- $source_port = $2;
- }
- }
- next NEW_LINE;
- }
- if ($line =~ /\s+next$/ && $context eq 'firewall service custom single') { # service wegschreiben
- &print_debug("before calling add_nw_service_obj: for application $application_name", $debug, 5);
- &add_nw_service_obj ($application_name, $proto, $source_port, $destination_port, $uuid, $rpc, $icmp_art, $icmp_nummer, $comment, $debug);
- $context = &switch_context($context, 'firewall service custom', $debug);
- undef($application_name); undef($proto); undef($source_port); undef($destination_port); undef($uuid); undef($rpc); undef($icmp_art); undef($icmp_nummer);
- undef ($comment);
- next NEW_LINE;
- }
-# -------------- parsing service groups ------------------
- if ($line =~ /^config firewall service group$/ && $context eq '') {
- print_debug("starting firewall service group", $debug, 6);
- $context = &switch_context($context, 'firewall service group', $debug);
- next NEW_LINE;
- }
- if ($line =~ /^\s+edit\s\"(.+?)"$/ && $context eq 'firewall service group') {
- $context = &switch_context($context, 'firewall service single group', $debug);
- $application_name = $1;
- next NEW_LINE;
- }
- if ($line =~ /^\s+set\smember\s(.+)$/ && $context eq 'firewall service single group') {
- $members = $1;
- print_debug("found service members $members in service group $application_name", $debug, 6);
- next NEW_LINE;
- }
- if ($line =~ /^\s+set\scomment\s[\'\"](.*?)[\'\"]$/ && ($context eq 'firewall service single group')) {
- $comment = $1;
- print_debug("found service group comment $comment", $debug, 4);
- next NEW_LINE;
- }
-# closig statements
- if ($line =~ /^end$/ && $context eq 'firewall service group') { $context = &switch_context($context, '', $debug); }
- if ($line =~ /^\s+next$/ && ($context eq 'firewall service single group' || $context eq 'firewall service single multi-port group')) {
- if ($context eq 'firewall service single group') {
- $context = &switch_context($context, 'firewall service group', $debug);
- } else { # moving from multi-port group back to non-group environment (context='firewall service single multi-port group')
- $context = &switch_context($context, 'firewall service custom', $debug);
- }
- if (!defined($members)) { print_debug("warning: application group $application_name: members undefined", $debug, 1); }
- print_debug("adding service group $application_name with members: $members", $debug, 5);
- # service group wegschreiben
- &add_nw_service_obj_grp ($application_name, $members, $comment, $debug);
- undef($application_name); undef($proto); undef($source_port); undef($destination_port); undef($uuid); undef($rpc); undef($icmp_art); undef($icmp_nummer);
- undef ($comment); undef ($members);
- next NEW_LINE;
- }
- }
- return 0;
-}
-
-
-sub cfg_file_complete { # check auf Vollstaendigkeit des Config-Files:
- my $debug_level = shift;
- my $ln_cnt = $#config_lines;
- my $cfg_file_complete = 1;
-
-
- while ($config_lines[$ln_cnt] =~ /^\s*$/ ) { $ln_cnt -- ; } # ignore empty lines at the end
- if ($config_lines[$ln_cnt-2] !~ /^end/ && $config_lines[$ln_cnt-1] !~ /^$/ && $config_lines[$ln_cnt] !~ /^\w+ [\$\#] \.$/) {
- $cfg_file_complete = 0;
- print_debug ("ERROR: expected end of config to be end, blank line, prompt with fw name. Instead got: " .
- $config_lines[$ln_cnt-2] . $config_lines[$ln_cnt-1] . $config_lines[$ln_cnt], $debug_level, -1);
- }
- return $cfg_file_complete;
-}
-
-sub switch_context {
- my $old_level = shift;
- my $new_level = shift;
- my $debug_level = shift;
- print_debug("switching context from $old_level to $new_level", $debug_level, 4);
- return $new_level;
-}
-
-sub parse_config_rules { # ($debuglevel_main, $mgm_name_in_config)
- my $debug = shift;
- my $mgm_name = shift;
- my ($source, $destination, $application, $from_zone, $to_zone, $policy_name, $track, $uid_postfix, $comment);
- my $line = '';
- my $context = '';
- my $disabled ='';
- my $action = 'deny';
- my $rule_no = 0;
- my ($policy_id, $policy_uid);
- my $list;
- my $list_typ;
- my $svc_neg = 0;
- my $src_neg = 0;
- my $dst_neg = 0;
- my $policy_type;
-
- &print_debug("entering parse_config_rules =======================================================",$debug,2);
- NEW_LINE: foreach $line (@config_lines) {
- chomp($line);
- &print_debug("pcr: $line", $debug, 9);
- if ($line =~ /^config firewall policy(.*?)$/ && $context eq '') { # match all policy types 4, 6, 4to6 6to4, ...
- $policy_type = $1;
- $context = &switch_context($context, 'firewall policy', $debug);
- next NEW_LINE;
- }
- if ($line =~ /^\s+edit\s(\d+)$/ && $context eq 'firewall policy') {
- $context = &switch_context($context, 'firewall policy single rule', $debug);
- $policy_id = $1;
- # to avoid duplicate policy ids (v6 and v4 may have clashing ids)
- if ($policy_type ne '') { $policy_id = "ipv$policy_type.$policy_id"; }
- &print_debug("found policy $policy_id",$debug,2);
- next NEW_LINE;
- }
- if ($line=~ /^\s+set\ssrcintf\s\"(.+?)\"$/ && $context eq 'firewall policy single rule') {
- $from_zone = $1;
- &add_zone ($from_zone, $debug);
- &print_debug("found from zone $from_zone",$debug,5);
- next NEW_LINE;
- }
- if ($line=~ /^\s+set\sdstintf\s\"(.+?)\"$/ && $context eq 'firewall policy single rule') {
- $to_zone = $1;
- &add_zone ($to_zone, $debug);
- &print_debug("found to zone $to_zone",$debug,5);
- next NEW_LINE;
- }
- # negated rule parts:
- if ($line=~ /^\s+set\s(service|srcaddr|dstaddr)\-negate\senable$/ && $context eq 'firewall policy single rule') {
- my $negation = $1;
- if ($negation eq 'service') { $svc_neg= '1'; }
- if ($negation eq 'srcaddr') { $src_neg= '1'; }
- if ($negation eq 'dstaddr') { $dst_neg= '1'; }
- &print_debug("found negation $negation",$debug,5);
- next NEW_LINE;
- }
-
- if ($line=~ /^\s+set\sname\s\"(.*?)\"$/ && $context eq 'firewall policy single rule') {
- $policy_name = $1;
- &print_debug("found rule name $policy_name",$debug,5);
- next NEW_LINE;
- }
- if ($line=~ /^\s+set\scomments\s\"(.*?)\"$/ && $context eq 'firewall policy single rule') {
- $comment = $1;
- &print_debug("found single line rule comment $comment",$debug,5);
- next NEW_LINE;
- }
- if ($line=~ /^\s+set\scomments\s\"(.*?)$/ && $context eq 'firewall policy single rule') {
- $comment = $1;
- $context = &switch_context($context, 'firewall policy single rule multi-line comment', $debug);
- &print_debug("found start of multi-line rule comment $comment",$debug,5);
- next NEW_LINE;
- }
- if ($line=~ /^(.*?)(\"?)$/ && $context eq 'firewall policy single rule multi-line comment') {
- $comment .= "\n$1";
- &print_debug("found more lines of multi-line comment $comment",$debug,5);
- if (defined($2) && $2 eq '"') { $context = &switch_context($context, 'firewall policy single rule', $debug); }
- next NEW_LINE;
- }
- if ($line=~ /^\s+set\sstatus\s(en|dis)able$/ && $context eq 'firewall policy single rule') {
- if ($1 eq 'en') { $disabled=''; } else { $disabled = "inactive" }
- &print_debug("found rule dis/enable statement: $disabled",$debug,5);
- next NEW_LINE;
- }
- if ($line=~ /^\s+set\suuid\s(.*?)$/ && $context eq 'firewall policy single rule') {
- $policy_uid = $1;
- &print_debug("found policy uid: $policy_uid",$debug,5);
- next NEW_LINE;
- }
-
- if ($line =~ /^\s+set (src|dst)addr\s(.+)$/ && $context eq 'firewall policy single rule') {
- $list_typ = $1;
- $list = $2;
- if ($list_typ eq 'src') { $source = $list; }
- if ($list_typ eq 'dst') { $destination = $list; }
- &print_debug("found src/dst list ($list_typ): $list",$debug,5);
- next NEW_LINE;
- }
- if ($line =~ /^\s+set\sservice\s(.*?)$/ && $context eq 'firewall policy single rule') {
- $application = $1;
- &print_debug("found service: $application",$debug,5);
- next NEW_LINE;
- }
-# action / track part of rule (then)
- if ($line=~ /^\s+set\saction\s(accept|deny|reject)$/ && $context eq 'firewall policy single rule') {
- $action = $1;
- &print_debug("found action: $action",$debug,5);
- next NEW_LINE;
- }
- if ($line=~ /^\s+set\slogtraffic[\s|-](disable|utm|all|start enable)$/ && $context eq 'firewall policy single rule') {
- my $found_track = $1;
- if ($found_track eq 'disable') { $found_track = 'none'; }
- if (!defined($track) || $track eq '') { $track = $found_track; }
- elsif ($found_track eq 'start enable') { $track .= ' start'; }
- &print_debug("found track: $track",$debug,5);
- next NEW_LINE;
- }
-#closing sections
- if ($line =~ /^end$/ && $context eq 'firewall policy') {
- $context = &switch_context($context, '', $debug);
- &print_debug("found end of policy section, stopping rule parsing.",$debug,5);
- next NEW_LINE;
-# config file may contain more than one "config firewall policy" section
- }
- if ($line =~ /^\s+next$/ && $context eq 'firewall policy single rule') {
- if (!defined($track)) { $track=''; }
- if (!defined($policy_name)) { $policy_name=''; }
- if (!defined($from_zone)) { $from_zone=''; }
- if (!defined($to_zone)) { $to_zone=''; }
- if (!defined($policy_name)) { $policy_name=''; }
- if ($policy_type ne '') { $uid_postfix = ".ipv$policy_type.uid"; } else { $uid_postfix = ''; }
- &print_debug("found rule from $from_zone to $to_zone, name=$policy_name, disabled=$disabled, src=$source, dst=$destination, svc=$application, action=$action, track=$track", $debug,3);
- # Regel wegschreiben
-
- # using UID as uid
- # $rule_no = &add_rule ($rule_no, $from_zone, $to_zone, $policy_uid, $disabled, $source, $destination, $application,
- # $action, $track, $comment, $policy_name, $svc_neg, $src_neg, $dst_neg, $uid_postfix, $debug);
-
- # using id as uid
- $rule_no = &add_rule ($rule_no, $from_zone, $to_zone, $policy_id, $disabled, $source, $destination, $application,
- $action, $track, $comment, $policy_name, $svc_neg, $src_neg, $dst_neg, $uid_postfix, $debug);
-
- $context = &switch_context($context, 'firewall policy', $debug);
- # reset all values after a rule has been parsed:
- $action='deny'; # default value for action = deny (not contained in config!)
- $disabled=''; $source=''; $destination=''; $application=''; $uid_postfix='';
- undef($track); undef($policy_name); undef($comment); undef($from_zone); undef($to_zone);
- $svc_neg = 0; $src_neg = 0; $dst_neg = 0;
-
- next NEW_LINE;
- } # end of rule
- }
- &sort_rules_and_add_zone_headers($debug);
- $rulebases{"$rulebase_name.ruleorder"} = join(',', @ruleorder);
- return 0;
-}
-
-sub purge_non_vdom_config_lines { # ($vdom_name)
- my $vdom_name = shift;
- my $debug = shift;
- my $ln_cnt = 0;
- my $found_correct_vdom = 0;
- my @vdom_config_lines;
- my ($start_of_vdom, $end_of_vdom);
-
- $end_of_vdom = $#config_lines; # if vdom is the last vdom in file
- LINE: while (!$found_correct_vdom && $ln_cnt<$#config_lines) {
- if (!defined($start_of_vdom) &&
- $config_lines[$ln_cnt] =~ /^config vdom$/ &&
- $config_lines[$ln_cnt+1] =~ /^edit ${vdom_name}$/
- # && $config_lines[$ln_cnt+2] =~ /^config system settings$/
- #&& $config_lines[$ln_cnt+2] =~ /^config system /
- && $config_lines[$ln_cnt+2] !~ /^next$/ # filter out vdom defining lines at the very begining
- )
- {
- $start_of_vdom = $ln_cnt+2;
- $ln_cnt++ ;
- }
- if (defined($start_of_vdom) &&
- $config_lines[$ln_cnt] =~ /^config vdom$/ &&
- $config_lines[$ln_cnt+1] =~ /^edit .+?$/
- #&& $config_lines[$ln_cnt+2] =~ /^config system /
- && $config_lines[$ln_cnt+2] !~ /^next$/
- )
- {
- $end_of_vdom = $ln_cnt;
- $ln_cnt = $#config_lines;
- }
- $ln_cnt++ ;
- }
- @vdom_config_lines = splice(@config_lines, $start_of_vdom, $end_of_vdom-$start_of_vdom+1);
- &print_debug("found vdom $vdom_name from line $start_of_vdom to line $end_of_vdom",$debug,1);
- @config_lines = @vdom_config_lines;
-}
-
-sub remove_pagination { # removes --More-- ^M ^M
- my $debug = shift;
- my $ln_cnt = 0;
-
- &print_debug("start of pagination removal",$debug,3);
- while ($ln_cnt<$#config_lines) {
- if ($config_lines[$ln_cnt] =~ /^\-\-More\-\-\s\r\s+\r(\s*.+)$/) {
- $config_lines[$ln_cnt] = $1;
- }
- $ln_cnt++;
- }
- &print_debug("end of pagination removal",$debug,3);
-}
-
-#####################################################################################
-# MAIN
-
-sub parse_config { # ($obj_file, $rule_file, $rulebases, $user, $fworch_workdir, $debug_level, $mgm_name, $config_dir, $import_id)
- my $in_file_main = shift;
- shift; shift; shift; # $rule_file und $rulebases und $user nicht verwendet
- my $fworch_workdir = shift;
- my $debuglevel_main = shift;
- my $mgm_name = shift;
- my $config_dir = shift;
- my $import_id = shift;
- my $debug_level = shift;
- my $vdom_name;
-
- # initializing global variables:
- @services = ();
- @network_objects = ();
- &print_debug ("in_file_main=$in_file_main, fworch_workdir=$fworch_workdir, debuglevel_main=$debuglevel_main, mgm_name=$mgm_name, config_dir=$config_dir, import_id=$import_id", $debuglevel_main, 6);
-
- open (IN, $in_file_main) || die "$in_file_main konnte nicht geoeffnet werden.\n";
- @config_lines = ; # sichern Config-array fuer spaetere Verwendung
- close (IN);
- &remove_pagination($debuglevel_main);
-
- if (!&cfg_file_complete($debuglevel_main)) { return "incomplete-config-file-$mgm_name"; }
- else {
-# my $mgm_name_in_config = &parse_mgm_name($debuglevel_main);
- @rulebases = ($mgm_name);
- $rulebase_name = $mgm_name;
- &print_debug("parse_mgm_name: found hostname of single system: $mgm_name and setting rulebase_name to $mgm_name",$debuglevel_main,1);
-
- my $vdom_names = &parse_vdom_names($debuglevel_main);
- if ($vdom_names ne '' && $mgm_name =~ /.+?\_\_\_(.+)$/ ) { # file contains multiple vdoms and mgm_name also contains xxx___vdom_name
- $vdom_name = $1;
- &print_debug ("rulebase_name: $rulebase_name, mgm_name=$mgm_name, mgm_name_in_config=$mgm_name, vdom_name:$vdom_name", $debuglevel_main, 2);
- &purge_non_vdom_config_lines ($vdom_name,$debuglevel_main); # removes all lines that do not belong to correct vdom from @config_lines
- }
- &parse_config_base_objects ($debuglevel_main, $mgm_name); # zones, simple network and service objects
- push @zones, "global"; # Global Zone immer hinzufuegen
- &parse_config_rules ($debuglevel_main, $mgm_name); # finally parsing the rule base, ignoring the rulebase name in fworch config
-
- &print_results_files_objects($fworch_workdir, $mgm_name, $import_id);
- &print_results_files_rules ($fworch_workdir, $mgm_name, $import_id);
- &print_results_files_zones ($fworch_workdir, $mgm_name, $import_id);
-# print_results_monitor('objects');
-# print_results_monitor('rules');
- }
- return 0;
-}
-
-1;
-__END__
-
-=head1 NAME
-
-CACTUS::FWORCH::parser - Perl extension for fworch fortinet parser
-
-=head1 SYNOPSIS
-
- use CACTUS::FWORCH::import::fortinet;
-
-=head1 DESCRIPTION
-
-fworch Perl Module support for importing configs into fworch Database
-
-=head2 EXPORT
-
- global variables
-
-
-=head1 SEE ALSO
-
- behind the door
-
-=head1 AUTHOR
-
-Cactus eSecurity, tmp@cactus.de
-
-=cut
diff --git a/roles/importer/files/importer/CACTUS/FWORCH/import/juniper.pm b/roles/importer/files/importer/CACTUS/FWORCH/import/juniper.pm
deleted file mode 100644
index 452268a52a..0000000000
--- a/roles/importer/files/importer/CACTUS/FWORCH/import/juniper.pm
+++ /dev/null
@@ -1,1075 +0,0 @@
-
-package CACTUS::FWORCH::import::parser;
-
-use strict;
-use warnings;
-use Time::HiRes qw(time); # fuer hundertstelsekundengenaue Messung der Ausfuehrdauer
-use CACTUS::FWORCH;
-use CACTUS::FWORCH::import;
-use CACTUS::read_config;
-
-require Exporter;
-our @ISA = qw(Exporter);
-
-our %EXPORT_TAGS = ( 'basic' => [ qw( ©_config_from_mgm_to_iso &parse_config ) ] );
-
-our @EXPORT = ( @{ $EXPORT_TAGS{'basic'} } );
-our $VERSION = '0.3';
-
-our @config_lines; # array der Zeilen des Config-Files im Originalzustand (kein d2u etc.)
-our $parser_mode; # Typ des Config-Formates (basic, data)
-our $rule_order = 0; # Reihenfolge der Rules im Configfile
-our $rulebase_name;
-our %junos_uuids = ();
-
-## parse_audit_log Funktion fuer JunOS (noch) nicht implementiert
-sub parse_audit_log { }
-
-#####################################
-# add_nw_obj
-# param1: obj_name
-# param2: obj_ip (with netmask)
-# param3: obj zone
-# param4: debuglevel [0-?]
-#####################################
-sub add_nw_obj {
- my $act_obj_name = shift;
- my $obj_ip = shift;
- my $act_obj_zone = shift;
- my $act_obj_comm = shift;
- my $debuglevel = shift;
- my $act_obj_nameZ = '';
- my $act_obj_ipaddr = '';
- my $act_obj_ipaddr_last = '';
- my $act_obj_mask = '';
- my $act_obj_type = '';
- my $act_obj_loc = '';
- my $act_obj_color = 'black';
- my $act_obj_sys = '';
- my $act_obj_uid = '';
- my $ipv6 = 0;
-
- print_debug("add_nw_obj called with name=$act_obj_name, ip=$obj_ip, zone=$act_obj_zone", $debuglevel, 5);
- ($act_obj_ipaddr, $act_obj_mask) = split (/\//, $obj_ip);
- if ($obj_ip =~/\:/) { $ipv6 = 1; }
- print_debug("split: ip=$act_obj_ipaddr, mask=$act_obj_mask", $debuglevel, 4);
-# $act_obj_nameZ = "${act_obj_name}__zone__$act_obj_zone"; # Zone in Feldindex mit aufgenommen
- $act_obj_nameZ = "${act_obj_name}"; # CHANGE: Zone nicht in Feldindex aufgenommen
- if (!defined ($network_objects{"$act_obj_nameZ.name"})) {
- @network_objects = (@network_objects, $act_obj_nameZ);
- $network_objects{"$act_obj_nameZ.name"} = $act_obj_name;
- $network_objects{"$act_obj_nameZ.UID"} = $act_obj_nameZ;
- if ((!$ipv6 && $act_obj_mask==32) || ($ipv6 && $act_obj_mask==128)) { $network_objects{"$act_obj_nameZ.type"} = 'host' }
- else { $network_objects{"$act_obj_nameZ.type"} = 'network' }
- $network_objects{"$act_obj_nameZ.netmask"} = $act_obj_mask ;
- $network_objects{"$act_obj_nameZ.zone"} = $act_obj_zone; # neues Feld fuer Zone
- $network_objects{"$act_obj_nameZ.ipaddr"} = $act_obj_ipaddr;
- $network_objects{"$act_obj_nameZ.ipaddr_last"} = $act_obj_ipaddr_last;
- $network_objects{"$act_obj_nameZ.color"} = $act_obj_color;
- $network_objects{"$act_obj_nameZ.comments"} = $act_obj_comm;
- $network_objects{"$act_obj_nameZ.location"} = $act_obj_loc;
- $network_objects{"$act_obj_nameZ.sys"} = $act_obj_sys;
- } else {
- print_debug ("found duplicate object definition for network object $act_obj_name in zone $act_obj_zone", $debuglevel, -1);
- }
-}
-
-#####################################
-# add_nw_obj_group_member
-# param1: input-line
-# param2: debuglevel [0-?]
-#####################################
-sub add_nw_obj_group_member{
- my $group_name = shift;
- my $member_name = shift;
- my $act_obj_zone = shift;
- my $comment = shift;
- my $debuglevel = shift;
-
- my $act_obj_name = '';
- my $group_nameZ = '';
- my $act_obj_mbr = '';
- my $act_obj_fkt = '';
- my $act_obj_color = 'black';
- my $act_obj_comm = '';
- my $mbrlst = '';
- my $mbr_ref_lst = '';
-
-# $group_nameZ = "${group_name}__zone__$act_obj_zone"; # Zone in Feldindex mit aufgenommen
- $group_nameZ = "${group_name}"; # CHANGE: Zone nicht in Feldindex aufgenommen
- if (!defined ($network_objects{"$group_nameZ.name"})) {
- @network_objects = (@network_objects, $group_nameZ);
- $network_objects{"$group_nameZ.name"} = $group_name;
- $network_objects{"$group_nameZ.UID"} = $group_nameZ;
- print_debug ("added group $group_nameZ", $debuglevel, 5);
- $network_objects{"$group_nameZ.zone"} = $act_obj_zone; # neues Feld fuer Zone
- $network_objects{"$group_nameZ.type"} = 'group';
- $network_objects{"$group_nameZ.color"} = $act_obj_color;
- }
-# die ("reference to undefined network object $member_name found in group $group_name, zone: $act_obj_zone");
- if (defined($network_objects{"$group_nameZ.members"})) {
- $mbrlst = $network_objects{"$group_nameZ.members"};
- $mbr_ref_lst = $network_objects{"$group_nameZ.member_refs"};
- }
- if ( $mbrlst eq '' ) {
- $mbrlst = $member_name;
-# $mbr_ref_lst = $member_name . "__zone__$act_obj_zone";
- $mbr_ref_lst = $member_name; #CHANGE
- }
- else {
- $mbrlst = "$mbrlst|$member_name";
-# $mbr_ref_lst = "$mbr_ref_lst|$member_name" . "__zone__$act_obj_zone";
- $mbr_ref_lst = "$mbr_ref_lst|$member_name"; #CHANGE
- }
- $network_objects{"$group_nameZ.members"} = $mbrlst;
- $network_objects{"$group_nameZ.member_refs"} = $mbr_ref_lst;
- if (defined($comment) && $comment ne '') { $network_objects{"$group_nameZ.comments"} = $comment; }
- return;
-}
-
-#####################################
-# add_nw_service_obj
-# param1: input-line
-# param2: debuglevel [0-?]
-#####################################
-sub add_nw_service_obj { # ($application_name, $proto, $source_port, $destination_port, $uuid, $rpc, $icmp_art, $icmp_nummer, $debug_level_main)
- my $act_obj_name = shift;
- my $act_obj_proto = shift;
- my $act_obj_src = shift;
- my $act_obj_dst = shift;
- my $act_obj_uid = shift;
- my $act_obj_rpc = shift;
- my $icmp_art = shift;
- my $icmp_nummer = shift;
- my $comment = shift;
- my $debuglevel = shift
- my @range;
- my $act_obj_typ = 'simple';
- my $act_obj_type = '';
- my $act_obj_src_last = '';
- my $act_obj_dst_last = '';
- my $act_obj_time = '';
- my $act_obj_time_std = '';
- my $act_obj_color = 'black';
-
- if (!defined ($services{"$act_obj_name.name"})) {
- @services = (@services, $act_obj_name);
- $services{"$act_obj_name.name"} = $act_obj_name;
- if (defined($act_obj_src)) {
- @range = split ( /-/, $act_obj_src);
- $services{"$act_obj_name.src_port"} = $range[0];
- if (defined($range[1])) {
- $services{"$act_obj_name.src_port_last"} = $range[1];
- } else {
- $services{"$act_obj_name.src_port_last"} = '';
- }
- }
- if (defined($act_obj_dst)) {
- @range = split ( /-/, $act_obj_dst);
- $services{"$act_obj_name.port"} = $range[0];
- if (defined($range[1])) {
- $services{"$act_obj_name.port_last"} = $range[1];
- } else {
- $services{"$act_obj_name.port_last"} = '';
- }
- }
- if (defined($act_obj_proto)) {
- $services{"$act_obj_name.ip_proto"} = get_proto_number($act_obj_proto)
- } else {
- $services{"$act_obj_name.ip_proto"} = '';
- }
- $services{"$act_obj_name.timeout"} = $act_obj_time;
- $services{"$act_obj_name.color"} = $act_obj_color;
- if (defined($comment) && $comment ne '') { $services{"$act_obj_name.comments"} = $comment; } else { $services{"$act_obj_name.comments"} = ''; }
- $services{"$act_obj_name.typ"} = $act_obj_typ;
- $services{"$act_obj_name.type"} = $act_obj_type;
- if (defined($act_obj_rpc)) {
- $services{"$act_obj_name.rpc_port"} = $act_obj_rpc;
- } else {
- $services{"$act_obj_name.rpc_port"} = '';
- }
- $services{"$act_obj_name.UID"} = $act_obj_name;
- if (defined($act_obj_uid) && $act_obj_uid ne '') { $junos_uuids{"$act_obj_uid"} = $act_obj_name; } # collect uid refs
- print_debug("add_nw_service_obj: added application $act_obj_name", $debuglevel, 4);
- } else {
- print_debug("add_nw_service_obj: warning duplicate defintion of service $act_obj_name", $debuglevel, 1);
- }
- return;
-}
-
-#####################################
-# add_nw_service_obj_grp
-# param1: input-line
-# param2: debuglevel [0-?]
-#####################################
-sub add_nw_service_obj_grp { # ($application_name, $members, $members_proto, $members_uid, $comment, $debuglevel_main);
- my $act_obj_name = shift;
- my $act_obj_members = shift;
- my $comment = shift;
- my $debuglevel = shift;
- my $act_obj_typ = 'group';
- my $act_obj_type = '';
- my $act_obj_proto = '';
- my $act_obj_src_last = '';
- my $act_obj_dst_last = '';
- my $act_obj_time = '';
- my $act_obj_time_std = '';
- my $act_obj_color = 'black';
- my $act_obj_rpc = '';
- my $mbrlst = '';
-
- if (!defined ($services{"$act_obj_name.name"})) {
- @services = (@services, $act_obj_name);
- $services{"$act_obj_name.name"} = $act_obj_name;
- print_debug("adding service group $act_obj_name", $debuglevel, 5);
- } else {
- print_debug("re-defining service group $act_obj_name", $debuglevel, 5);
- }
- if (defined($act_obj_members) && $act_obj_members ne '') {
- $services{"$act_obj_name.members"} = $act_obj_members; # simple group case
- $services{"$act_obj_name.member_refs"} = $act_obj_members; # simple group case
- print_debug("adding service group $act_obj_name with members $act_obj_members", $debuglevel, 5);
- } else {
- print_debug("no members defined", $debuglevel, 1);
- }
- $services{"$act_obj_name.ip_proto"} = $act_obj_proto;
- $services{"$act_obj_name.timeout"} = $act_obj_time;
- $services{"$act_obj_name.color"} = $act_obj_color;
- if (defined($comment) && $comment ne '') { $services{"$act_obj_name.comments"} = $comment; } else { $services{"$act_obj_name.comments"} = ''; }
- $services{"$act_obj_name.typ"} = $act_obj_typ;
- $services{"$act_obj_name.type"} = $act_obj_type;
- $services{"$act_obj_name.rpc_port"} = $act_obj_rpc;
- $services{"$act_obj_name.UID"} = $act_obj_name;
-}
-
-sub junos_split_list { # param1 = list of objects (network or service)
- my $list = shift;
- my $debug_level = shift;
- my $orig_list = $list;
-
-# if ($list =~ /^\[\s([\w\-\_\/\.\d\s]+?)\s\]$/) { # standard list format: [ x1 x2 x3 ]
- if ($list =~ /^\[\s(.+?)\s\]$/) { # standard list format: [ x1 x2 x3 ]
- $list = $1;
- $list = join('|', split(/\s+/, $list));
-# } elsif ($list =~ /[\w\-\_\/\.\d]+/) {
- } elsif ($list =~ /[^\s]+/) {
- # found single object, no changes necessary
- } else {
- print_debug("warning in junos_split_list: orig_list=$orig_list; found no match for object list", $debug_level, 1);
- }
-# print_debug("junos_split_list: orig_list=$orig_list, result=$list", $debug_level, 5);
- return $list;
-}
-
-#####################################
-# add_rule
-# param1:
-# debuglevel [integer]
-#####################################
-sub add_rule { # ($rule_no, $from_zone, $to_zone, $policy_name, $disabled, $source, $destination, $application, $action, $track, $debuglevel_main)
- my $rule_no = shift;
- my $from_zone = shift;
- my $to_zone = shift;
- my $policy_name = shift;
- my $disabled = shift;
- my $source = shift;
- my $destination = shift;
- my $service = shift;
- my $action = shift;
- my $track = shift;
- my $comment = shift;
- my $debuglevel = shift;
- my $rule_id;
-
-# print_debug ("add_rule: rulebase_name=$rulebase_name, rulecount=" . $rulebases{"$rulebase_name.rulecount"}, $debuglevel, 4);
- $rulebases{"$rulebase_name.rulecount"} = $rule_no + 1; # Anzahl der Regeln wird sukzessive hochgesetzt
- $rule_id = "from_zone__$from_zone" . "__to_zone__$to_zone" . "__$policy_name";
- $ruleorder[$rule_no] = $rule_no;
-
- if (!defined($track) || $track eq '') { $track = 'none'; }
- if (length($track)<3) { print_debug ("warning, short track: <$track>", $debuglevel, 1); }
-
- $rulebases{"$rulebase_name.$rule_no.src"} = '';
- foreach my $src (split(/\|/, &junos_split_list($source, $debuglevel))) {
- if ($rulebases{"$rulebase_name.$rule_no.src"} ne '') {
- $rulebases{"$rulebase_name.$rule_no.src"} .= '|';
- $rulebases{"$rulebase_name.$rule_no.src.refs"} .= '|';
- }
- $rulebases{"$rulebase_name.$rule_no.src"} .= "$src";
-# $rulebases{"$rulebase_name.$rule_no.src.refs"} .= ("$src" . "__zone__$from_zone");
- $rulebases{"$rulebase_name.$rule_no.src.refs"} .= ("$src"); # CHANGE
- }
- $rulebases{"$rulebase_name.$rule_no.dst"} = '';
- foreach my $dst (split(/\|/, &junos_split_list($destination, $debuglevel))) {
- if ($rulebases{"$rulebase_name.$rule_no.dst"} ne '') {
- $rulebases{"$rulebase_name.$rule_no.dst"} .= '|';
- $rulebases{"$rulebase_name.$rule_no.dst.refs"} .= '|';
- }
- $rulebases{"$rulebase_name.$rule_no.dst"} .= "$dst";
-# $rulebases{"$rulebase_name.$rule_no.dst.refs"} .= ("$dst" . "__zone__$to_zone");
- $rulebases{"$rulebase_name.$rule_no.dst.refs"} .= ("$dst"); # CHANGE
- }
-
- $rulebases{"$rulebase_name.$rule_no.services"} = '';
- foreach my $svc (split(/\|/, &junos_split_list($service, $debuglevel))) {
- if ($rulebases{"$rulebase_name.$rule_no.services"} ne '') {
- $rulebases{"$rulebase_name.$rule_no.services"} .= '|';
- $rulebases{"$rulebase_name.$rule_no.services.refs"} .= '|';
- }
- $rulebases{"$rulebase_name.$rule_no.services"} .= "$svc";
- $rulebases{"$rulebase_name.$rule_no.services.refs"} .= "$svc";
- }
-
- $rulebases{"$rulebase_name.$rule_no.id"} = $rule_id;
- $rulebases{"$rulebase_name.$rule_no.ruleid"} = $rule_id;
- $rulebases{"$rulebase_name.$rule_no.order"} = $rule_no;
- if ($disabled eq 'inactive') { $rulebases{"$rulebase_name.$rule_no.disabled"} = '1'; }
- else { $rulebases{"$rulebase_name.$rule_no.disabled"} = '0'; }
- $rulebases{"$rulebase_name.$rule_no.src.zone"} = $from_zone;
- $rulebases{"$rulebase_name.$rule_no.dst.zone"} = $to_zone;
- $rulebases{"$rulebase_name.$rule_no.services.op"} = '0';
- $rulebases{"$rulebase_name.$rule_no.src.op"} = '0';
- $rulebases{"$rulebase_name.$rule_no.dst.op"} = '0';
- $rulebases{"$rulebase_name.$rule_no.action"} = $action;
- $rulebases{"$rulebase_name.$rule_no.track"} = $track;
- $rulebases{"$rulebase_name.$rule_no.install"} = ''; # set hostname verwenden ?
- $rulebases{"$rulebase_name.$rule_no.name"} = $policy_name; # kein Aequivalent zu CP rule_name
- $rulebases{"$rulebase_name.$rule_no.time"} = '';
- if (defined($comment) && $comment ne '') { $rulebases{"$rulebase_name.$rule_no.comments"} = $comment; }
- else { $rulebases{"$rulebase_name.$rule_no.comments"} = $policy_name; }
- $rulebases{"$rulebase_name.$rule_no.UID"} = $rule_id;
- $rulebases{"$rulebase_name.$rule_no.header_text"} = '';
- return $rule_no+1;
-}
-
-############################################################
-# add_zone ($new_zone)
-############################################################
-sub add_zone {
- my $new_zone = shift;
- my $debug = shift;
- my $is_there = 0;
- foreach my $elt (@zones) { if ($elt eq $new_zone) { $is_there = 1; last; } }
- if (!$is_there) { push @zones, $new_zone; &print_debug("adding new zone: $new_zone", $debug, 1); }
-}
-
-############################################################
-# object_address_add (name, ip, mask, zone, comment)
-############################################################
-sub object_address_add {
- my $act_obj_name = $_[0];
- my $act_obj_ipaddr = $_[1];
- my $act_obj_mask = $_[2];
- my $act_obj_zone = $_[3];
- my $act_obj_comm = $_[4];
- my @params;
- my $act_obj_nameZ = '';
- my $act_obj_ipaddr_last = '';
- my $act_obj_type = 'simple';
- my $act_obj_loc = '';
- my $act_obj_color = 'black';
- my $act_obj_sys = '';
- my $act_obj_uid = '';
-
-# $act_obj_nameZ = "${act_obj_name}__zone__$act_obj_zone"; # Zone in Feldindex mit aufgenommen
- $act_obj_nameZ = "${act_obj_name}"; # CHANGE Zone nicht in Feldindex aufgenommen
- if (!defined ($network_objects{"$act_obj_nameZ.name"})) {
- @network_objects = (@network_objects, $act_obj_nameZ);
- $network_objects{"$act_obj_nameZ.name"} = $act_obj_name;
- } elsif (defined ($network_objects{"$act_obj_nameZ.name"})) {
- print "sub ns_object_address NET_OBJECT: $act_obj_nameZ ist bereits definiert.\n";
- } else {
- print "sub ns_object_address NET_OBJECT: $act_obj_nameZ ist undefiniert.\n";
- }
- my $subnetbits = calc_subnetmask ($act_obj_mask);
- if ($subnetbits==32) { $network_objects{"$act_obj_nameZ.type"} = 'host' };
- if ($subnetbits<32) { $network_objects{"$act_obj_nameZ.type"} = 'network' };
- $network_objects{"$act_obj_nameZ.netmask"} = $act_obj_mask ;
- $network_objects{"$act_obj_nameZ.zone"} = $act_obj_zone; # neues Feld fuer Zone
- $network_objects{"$act_obj_nameZ.ipaddr"} = $act_obj_ipaddr;
- $network_objects{"$act_obj_nameZ.ipaddr_last"} = $act_obj_ipaddr_last;
- $network_objects{"$act_obj_nameZ.color"} = $act_obj_color;
- $network_objects{"$act_obj_nameZ.comments"} = $act_obj_comm;
- $network_objects{"$act_obj_nameZ.location"} = $act_obj_loc;
- $network_objects{"$act_obj_nameZ.sys"} = $act_obj_sys;
- $network_objects{"$act_obj_nameZ.UID"} = $act_obj_nameZ;
-}
-
-############################################################
-# read_predefined_services(
-############################################################
-sub read_predefined_services {
- my $device_type = shift;
-# my $predefined_service_string = shift;
- my $debug_level = shift;
- my $predef_svc;
- my ($svc_name,$ip_proto,$port,$port_end,$timeout,$comment,$typ,$group_members);
-
- $predef_svc = exec_pgsql_cmd_return_value ("SELECT dev_typ_predef_svc FROM stm_dev_typ WHERE dev_typ_id=$device_type");
- my @predef_svc = split /\n/, $predef_svc;
-
- &print_debug("inserting pre-defined services for junos: $predef_svc",$debug_level,8);
-
- foreach my $svc_line (@predef_svc) {
- ($svc_name,$ip_proto,$port,$port_end,$timeout,$comment,$typ,$group_members) = split /;/, $svc_line;
- $services{"$svc_name.name"} = $svc_name;
- $services{"$svc_name.port"} = $port;
- $services{"$svc_name.port_last"} = $port_end;
- $services{"$svc_name.ip_proto"} = $ip_proto;
- $services{"$svc_name.timeout"} = $timeout;
- $services{"$svc_name.color"} = "black";
-# $services{"$svc_name.comments"} = "$predefined_service_string, $comment";
- $services{"$svc_name.comments"} = $comment;
- $services{"$svc_name.typ"} = $typ;
- $services{"$svc_name.type"} = "";
- $services{"$svc_name.rpc_port"} = "";
- $services{"$svc_name.UID"} = $svc_name;
- $services{"$svc_name.members"} = $group_members;
- $services{"$svc_name.member_refs"} = $group_members;
- push @services, $svc_name;
- }
- return;
-}
-
-sub resolve_service_uuid_references { # ($debuglevel_main);
- my $debug = shift;
-
- foreach my $nw_svc (@services) {
- &print_debug("resolve_service_uuid_references: checking service $nw_svc, typ=" . $services{"$nw_svc.typ"} . "type=" . $services{"$nw_svc.type"}, $debug, 5);
- if ($services{"$nw_svc.typ"} eq 'group') {
- &print_debug("resolve_service_uuid_references: checking service group $nw_svc", $debug, 5);
- my @members = split (/\|/, $services{"$nw_svc.member_refs"});
- my $member_string = '';
- my $change_flag = 0;
- foreach my $member (@members) {
- &print_debug("resolve_service_uuid_references: checking member $member", $debug, 5);
- if ($member =~ /^[a-f0-9]\-[a-f0-9]\-[a-f0-9]\-[a-f0-9]\-[a-f0-9]$/) {
- my $old_member = $member;
- $change_flag = 1;
- $member = $junos_uuids{"$member"};
- &print_debug("resolve_service_uuid_references: replacing uuid $old_member with $member", $debug, 5);
- }
- if ($member_string ne '') { $member_string .= '|'; }
- $member_string .= $member;
- }
- if ($change_flag) {
- $services{"$nw_svc.members"} = $member_string;
- $services{"$nw_svc.member_refs"} = $member_string;
- }
- }
- }
- return;
-}
-
-############################################################
-# copy_config_from_mgm_to_iso($ssh_private_key, $ssh_user, $ssh_hostname, $management_name,
-# $obj_file_base, $cfg_dir, $rule_file_base)
-# Kopieren der Config-Daten vom Management-System zum ITSecorg-Server
-############################################################
-
-sub copy_config_from_mgm_to_iso {
- my $ssh_user = shift;
- my $ssh_hostname = shift;
- my $management_name = shift;
- my $obj_file_base = shift;
- my $cfg_dir = shift;
- my $rule_file_base = shift;
- my $workdir = shift;
- my $debug_level = shift;
- my $cmd;
- my $fehler_count = 0;
- my $result;
- my $simulate = 0;
-
- if ($simulate) { # get config from std ssh server, not from native junos system
- $cmd = "echo \"applications {\" > $cfg_dir/$obj_file_base";
- if (system ($cmd)) { $fehler_count++; }
- $cmd = "$scp_bin $scp_batch_mode_switch -i $workdir/$CACTUS::FWORCH::ssh_id_basename $ssh_user\@$ssh_hostname:predef_svc_junos $cfg_dir/$obj_file_base.1";
- if (system ($cmd)) { $fehler_count++; }
- $cmd = "cat $cfg_dir/$obj_file_base.1 >> $cfg_dir/$obj_file_base";
- if (system ($cmd)) { $fehler_count++; }
- $cmd = "echo \"}\" >> $cfg_dir/$obj_file_base";
- if (system ($cmd)) { $fehler_count++; }
- $cmd = "$scp_bin $scp_batch_mode_switch -i $workdir/$CACTUS::FWORCH::ssh_id_basename $ssh_user\@$ssh_hostname:junos_config $cfg_dir/$obj_file_base.1";
- if (system ($cmd)) { $fehler_count++; }
- $cmd = "cat $cfg_dir/$obj_file_base.1 >> $cfg_dir/$obj_file_base";
- if (system ($cmd)) { $fehler_count++; }
- $cmd = "rm $cfg_dir/$obj_file_base.1";
- if (system ($cmd)) { $fehler_count++; }
- } else {
- if (system ("echo \"applications {\" > $cfg_dir/$obj_file_base")) { $fehler_count++; }
- $cmd = "$ssh_bin -i $workdir/$CACTUS::FWORCH::ssh_id_basename $ssh_user\@$ssh_hostname show configuration groups junos-defaults applications > $cfg_dir/$obj_file_base.1";
- if (system ($cmd)) { $fehler_count++; }
- if (system ("cat $cfg_dir/$obj_file_base.1 >> $cfg_dir/$obj_file_base")) { $fehler_count++; }
- if (system ("echo \"}\" >> $cfg_dir/$obj_file_base")) { $fehler_count++; }
- $cmd = "$ssh_bin -i $workdir/$CACTUS::FWORCH::ssh_id_basename $ssh_user\@$ssh_hostname show config > $cfg_dir/$obj_file_base.1";
- if (system ($cmd)) { $fehler_count++; }
- if (system ("cat $cfg_dir/$obj_file_base.1 >> $cfg_dir/$obj_file_base")) { $fehler_count++; }
- if (system ("rm $cfg_dir/$obj_file_base.1")) { $fehler_count++; }
- }
- return ($fehler_count, "$cfg_dir/$obj_file_base" );
-}
-
-sub sort_rules_and_add_zone_headers {
- my $anzahl_regeln;
- my $count;
- my $zone_string;
- my @rule_zones = ();
-
- # Nachbereitung Regeln: Sortierung nach a) Zonen b) $ruleorder
- if (!defined($rulebases{"$rulebase_name.rulecount"})) {
- $anzahl_regeln = 0;
- } else {
- $anzahl_regeln = $rulebases{"$rulebase_name.rulecount"};
- }
- for ($count=0; $count<$anzahl_regeln; $count++) {
- $zone_string = $rulebases{"$rulebase_name.$ruleorder[$count].src.zone"};
- $zone_string .= " : ";
- $zone_string .= $rulebases{"$rulebase_name.$ruleorder[$count].dst.zone"};
- push @rule_zones, $zone_string
- }
- my @idx = ();
- my $item;
- for (@rule_zones) {
- ($item) = $_;
- push @idx, $item;
- }
-
- @ruleorder = @ruleorder[ sort { $idx[$a] cmp $idx[$b] } 0 .. $anzahl_regeln-1 ];
- @rule_zones = @rule_zones[ sort { $idx[$a] cmp $idx[$b] } 0 .. $anzahl_regeln-1 ];
-
- # Nachbereitung Regeln: Header-Regeln vor Zonenwechsel einfuegen
- my $new_zone_string;
- my $old_zone_string = "";
- my $rule_header_count = 1;
- my $rule_header_offset = &CACTUS::read_config::read_config('rule_header_offset') * 1;
- my $new_rule_id;
- for ($count = 0; $count < $anzahl_regeln; $count++) {
- $new_zone_string = $rule_zones[$count];
- if ($new_zone_string ne $old_zone_string) { # insert header rule
- $new_rule_id = $rule_header_offset+$rule_header_count++;
- (my $src_zone, my $dst_zone) = split / : /, $new_zone_string;
- splice(@ruleorder,$count,0,$new_rule_id); # fuegt neue Regel ein
- splice(@rule_zones,$count,0,$new_zone_string);
- $anzahl_regeln++;
- $rulebases{"$rulebase_name.rulecount"} = $anzahl_regeln;
- $rulebases{"$rulebase_name.$ruleorder[$count].id"} = $new_rule_id;
- $rulebases{"$rulebase_name.$ruleorder[$count].header_text"} = $new_zone_string;
- $rulebases{"$rulebase_name.$ruleorder[$count].UID"} = $new_rule_id;
- $rulebases{"$rulebase_name.$ruleorder[$count].src"} = "any";
- $rulebases{"$rulebase_name.$ruleorder[$count].dst"} = "any";
- $rulebases{"$rulebase_name.$ruleorder[$count].services"} = "any";
- $rulebases{"$rulebase_name.$ruleorder[$count].action"} = "deny";
- $rulebases{"$rulebase_name.$ruleorder[$count].src.zone"} = $src_zone;
- $rulebases{"$rulebase_name.$ruleorder[$count].dst.zone"} = $dst_zone;
- $rulebases{"$rulebase_name.$ruleorder[$count].disabled"} = '0';
- $rulebases{"$rulebase_name.$ruleorder[$count].src.op"} = '0';
- $rulebases{"$rulebase_name.$ruleorder[$count].dst.op"} = '0';
- $rulebases{"$rulebase_name.$ruleorder[$count].services.op"} = '0';
- }
- $old_zone_string = $new_zone_string;
- }
-}
-
-sub parse_mgm_name { # ($obj_file, $fworch_workdir, $debug_level, $mgm_name, $config_dir, $import_id)
- my $in_file_main = shift;
- my $fworch_workdir = shift;
- my $debuglevel_main = shift;
- my $mgm_name = shift;
- my $config_dir = shift;
- my $import_id = shift;
- my $line = '';
- my $context = '';
- my @nodes= ();
-
- &print_debug("entering parse_mgm_name",$debuglevel_main,2);
- NEW_LINE: foreach $line (@config_lines) {
- chomp($line);
-# parsing device name(s)
- if ($line=~ /^\s+host\-name\s+(.+?)\;$/ && ($context eq 'groups/node/system' || $context eq 'system')) {
- $mgm_name = $1;
- @nodes = ($mgm_name, @nodes);
- &print_debug("parse_mgm_name: found hostname: $mgm_name",$debuglevel_main,1);
- next NEW_LINE;
- }
- # cluster node
- if ($line =~ /^groups\s\{$/ && $context eq '') { $context = 'groups'; next NEW_LINE; }
- if ($line =~ /^\s+node(\d+)\s\{$/ && $context eq 'groups') { $context .= "/node"; next NEW_LINE; }
- if ($line =~ /^\s+system\s\{$/ && $context eq 'groups/node') { $context .= "/system"; &print_debug("found system line: $line",$debuglevel_main,5); next NEW_LINE; }
- if ($line=~ /^\}$/ && $context eq 'groups/node/system') { # in case of cluster: add both node names to make up the cluster name
- $context = '';
- $mgm_name = join('_', sort (@nodes));
- @rulebases = ($mgm_name);
- $rulebase_name = $mgm_name;
- &print_debug("parse_mgm_name: found hostname combined: $mgm_name and setting rulebase_name to $mgm_name",$debuglevel_main,1);
- return $mgm_name;
- }
- # single node
- if ($line =~ /^system\s\{$/ && $context eq '') { $context = "system"; &print_debug("found system line: $line",$debuglevel_main,5); next NEW_LINE; }
- if ($line=~ /^\}$/ && $context eq 'system') { # in case of single system
- $context = '';
- @rulebases = ($mgm_name);
- $rulebase_name = $mgm_name;
- &print_debug("parse_mgm_name: found hostname of single system: $mgm_name and setting rulebase_name to $mgm_name",$debuglevel_main,1);
- return $mgm_name;
- }
- }
- &print_debug("ERROR: end of parse_mgm_name: at end without match (mgm_name=$mgm_name)",$debuglevel_main,-1);
-}
-
-# the following function does only parse simple objects without groups. Groups are parsed in a second run using function parse_config_group_objects
-sub parse_config_base_objects { # ($obj_file, $fworch_workdir, $debug_level, $mgm_name, $config_dir, $import_id)
- my $in_file_main = shift;
- my $fworch_workdir = shift;
- my $debug = shift;
- my $mgm_name = shift;
- my $config_dir = shift;
- my $import_id = shift;
- my ($zone, $address_group_name, $obj_name, $obj_ip, $group_member_name, $application_name, $group_name, $group_members, $proto, $icmp_art, $icmp_nummer,
- $source_port, $destination_port, $uuid, $members, $members_uid, $members_proto, $rpc, $comment);
- my $line = '';
- my $context = '';
- my @nodes= ();
-
- &print_debug("entering parse_config_base_objects =======================================================",$debug,2);
- NEW_LINE: foreach $line (@config_lines) {
- chomp($line);
- &print_debug("pcbo-line: $line", $debug, 9);
-# if ($line =~ /^\s+\}$/ && $context eq 'security/address-book') { $context = &switch_context($context, 'security', $debug); next NEW_LINE; }
-# if ($line =~ /^\s+\}$/ && $context eq 'security/address-book/global') { $context = &switch_context($context, 'security/address-book', $debug); next NEW_LINE; }
-# if ($line=~ /^\}$/) { $context = &switch_context($context, '', $debug); next NEW_LINE; }
-# if ($line=~ /^\}$/ && $context eq 'applications/application') { $context = &switch_context($context, 'applications', $debug); next NEW_LINE; }
- if ($line=~ /^\}$/ && $context ne 'applications/application' && $context ne 'applications/application-set'
- && $context ne'') { $context = &switch_context($context, '', $debug); next NEW_LINE; }
- # added ne 'applications/application' to enable parsing of predefined services in the beginning of the file
-# parsing zones
- if ($line =~ /^security\s\{$/ && $context eq '') { $context = &switch_context($context, 'security', $debug); next NEW_LINE; }
- if ($line =~ /^\s+zones\s\{$/ && $context eq 'security') { $context = &switch_context($context, 'security/zones', $debug); next NEW_LINE; }
- if ($line =~ /^\s+address-book\s\{$/ && $context eq 'security') { $context = &switch_context($context, 'security/address-book', $debug); next NEW_LINE; }
- if ($line =~ /^\s+global\s\{$/ && $context eq 'security/address-book') {
- $context = &switch_context($context, 'security/address-book/global', $debug);
- $zone = 'global';
- next NEW_LINE;
- }
-# if ($line=~ /^\s+security\-zone\s([\w\.\_\-]+)\s\{$/ && ($context eq 'security/zones' || $context eq 'security/zones/address-book')) {
- if ($line=~ /^\s+security\-zone\s([\w\.\_\-]+)\s\{$/) {
- $zone = $1; &add_zone ($zone, $debug);
- print_debug("found zone $zone", $debug, 2);
- next NEW_LINE;
- }
-# parsing network objects
- if ($line=~ /^\s+address\-book\s\{$/ && $context eq 'security/zones') { $context = &switch_context($context, 'security/zones/address-book', $debug); next NEW_LINE; }
- # old comment syntax /* xxx */
- if ($line=~ /^\s+\/\*\s(.*?)\s\*\/$/ && $context eq 'security/zones/address-book') { $comment = $1; next NEW_LINE; }
- # new comment syntax description "xx yy zz";
- if ($line=~ /^\s+description\s\"?(.+?)\"?\;$/ &&
- ($context eq 'security/zones/address-book' || $context eq 'security/address-book/global' || $context eq 'applications/application')) {
- $comment = $1;
- print_debug("found comment $comment", $debug, 4);
- next NEW_LINE;
- }
- # start of comment brackets (containing only obj name)
- if ($line=~ /^\s+address\s(.+?)\s\{$/ && ($context eq 'security/zones/address-book' || $context eq 'security/address-book/global')) {
- $obj_name = $1;
- print_debug("found object name $obj_name", $debug, 4);
- next NEW_LINE;
- }
- # ip address only within comment brackets
- if ($line=~ /^\s+([\d\:\.\/]+)\;$/ && ($context eq 'security/zones/address-book' || $context eq 'security/address-book/global')) {
- $obj_ip = $1;
- print_debug("found object ip $obj_ip for obj $obj_name", $debug, 4);
- next NEW_LINE;
- }
- if ($line=~ /^\s+\}$/ && ($context eq 'security/zones/address-book' || $context eq 'security/address-book/global')) {
- if (defined($obj_name) && defined($obj_ip) && defined($comment)) {
- print_debug("found obj $obj_name with ip $obj_ip in zone $zone; comment=$comment", $debug, 4);
- &add_nw_obj ($obj_name, $obj_ip, $zone, $comment, $debug);
- undef($obj_name); undef($obj_ip); undef($comment);
- next NEW_LINE;
- }
- }
- if ($line=~ /^\s+address\s(.+?)\s([\d\:\.\/]+)\;$/ && ($context eq 'security/zones/address-book' || $context eq 'security/address-book/global')) {
- $obj_name = $1;
- $obj_ip = $2;
- print_debug("found obj $obj_name with ip $obj_ip in zone $zone", $debug, 4);
- &add_nw_obj ($obj_name, $obj_ip, $zone, $comment, $debug);
- undef($obj_name); undef($obj_ip); undef($comment);
- next NEW_LINE;
- }
-# parsing network services (applications)
- if ($line =~ /^applications\s\{$/ && $context eq '') {
- $context = &switch_context($context, 'applications', $debug);
- next NEW_LINE;
- }
- if ($line=~ /^\s*\/\*\s(.*?)\s\*\/$/ && $context eq 'applications') { $comment = $1; }
- if ($line =~ /^\s*application\s(.+?)\s\{/ && $context eq 'applications') {
- $application_name = $1;
- print_debug("found application $application_name", $debug, 4);
- $context = &switch_context($context, 'applications/application', $debug);
- next NEW_LINE;
- }
- if ($line =~ /^\s*application\s(.+?)\sprotocol\s(.+?)\;$/ && $context eq 'applications') { # sonderfall: single-line-service-definition
- $application_name = $1;
- &add_nw_service_obj ($1, $2, '', '1-65535', '', '', '', '', $comment, $debug);
- print_debug("found application $application_name in single line definition", $debug, 4);
- undef ($comment);
- next NEW_LINE;
- }
- if ($line =~ /^\s+protocol\s(\w+?)\;$/ && $context eq 'applications/application') { $proto = $1; next NEW_LINE;}
- if ($line =~ /^\s+source-port\s(.+?)\;$/ && $context eq 'applications/application') { $source_port = $1; next NEW_LINE; }
- if ($line =~ /^\s+destination-port\s(.+?)\;$/ && $context eq 'applications/application') { $destination_port = $1; next NEW_LINE; }
- if ($line =~ /^\s+uuid\s(.+?)\;$/ && $context eq 'applications/application') { $uuid = $1; next NEW_LINE; }
- if ($line =~ /^\s+rpc\-program\-number\s([\d\-]+?)\;$/ && $context eq 'applications/application') { $rpc = $1; next NEW_LINE;}
- if ($line =~ /^\s+icmp\-(.+?)\s(\d+)\;$/ && $context eq 'applications/application') { $icmp_art = $1; $icmp_nummer = $2; next NEW_LINE; }
- if ($line =~ /^\s*\}$/ && $context eq 'applications/application') {
- # service wegschreiben
- &print_debug("before calling add_nw_service_obj: for application $application_name", $debug, 5);
- &add_nw_service_obj ($application_name, $proto, $source_port, $destination_port, $uuid, $rpc, $icmp_art, $icmp_nummer, $comment, $debug);
- $context = &switch_context($context, 'applications', $debug);
- undef($application_name); undef($proto); undef($source_port); undef($destination_port); undef($uuid); undef($rpc); undef($icmp_art); undef($icmp_nummer);
- undef ($comment);
- next NEW_LINE;
- }
- }
- return 0;
-}
-
-
-sub parse_config_group_objects { # ($obj_file, $fworch_workdir, $debug_level, $mgm_name, $config_dir, $import_id)
- my $in_file_main = shift;
- my $fworch_workdir = shift;
- my $debug = shift;
- my $mgm_name = shift;
- my $config_dir = shift;
- my $import_id = shift;
- my ($zone, $address_group_name, $obj_name, $obj_ip, $group_member_name, $application_name, $proto, $icmp_art, $icmp_nummer, $source_port, $destination_port, $uuid, $members, $members_uid, $members_proto, $rpc);
- my $line = '';
- my $context = '';
- my $comment;
- my @nodes= ();
-
- &print_debug("entering parse_config_group_objects =======================================================",$debug,2);
- NEW_LINE: foreach $line (@config_lines) {
- chomp($line);
- &print_debug("pcgo-line: $line", $debug, 9);
- if ($line=~ /^application\s/ && $context eq 'applications') {
- $context = &switch_context($context, 'applications/application', $debug); next NEW_LINE;
- }
- if ($line=~ /^\}$/ && $context eq 'applications/application') { $context = &switch_context($context, 'applications', $debug); next NEW_LINE; }
- if ($line=~ /^\}$/ && $context ne 'applications' && $context ne 'applications/application' && $context ne 'applications/application-set'
- && $context ne '') { $context = &switch_context($context, '', $debug); next NEW_LINE; }
-# if ($line=~ /^\}$/) { $context = &switch_context($context, '', $debug); next NEW_LINE; }
-# parsing zones
- if ($line =~ /^security\s\{$/ && $context eq '') { $context = &switch_context($context, 'security', $debug); next NEW_LINE; }
- if ($line =~ /^\s+zones\s\{$/ && $context eq 'security') { $context = &switch_context($context, 'security/zones', $debug); next NEW_LINE; }
- if ($line=~ /^\s+security\-zone\s([\w\.\_\-]+)\s\{$/ && ($context eq 'security/zones' || $context eq 'security/zones/address-book')) {
- $zone = $1;
- print_debug("entering zone $zone, context=$context", $debug, 4);
- next NEW_LINE;
- }
-# parsing network objects
- if ($line=~ /^\s+address\-book\s\{$/ && ($context eq 'security/zones')) { $context = &switch_context($context, 'security/zones/address-book', $debug); next NEW_LINE; }
- if ($line=~ /^\s+address\-book\s\{$/ && ($context eq 'security' || $context eq 'security')) {
- $context = &switch_context($context, 'security/address-book', $debug); next NEW_LINE;
- }
- if ($line=~ /^\s+(.+?)\s\{$/ && $context eq 'security/address-book') {
- $zone = $1;
- $context = &switch_context($context, "security/address-book/$zone", $debug);
- next NEW_LINE;
- }
-# parsing network object groups
- if ($line=~ /^\s+\/\*\s(.*?)\s\*\/$/ && $context eq 'security/zones/address-book') { $comment = $1; next NEW_LINE; }
- if ($line=~ /^\s+address\-set\s(.+?)\s\{$/ && ($context eq 'security/zones/address-book' || $context eq 'security/address-book/global')) {
- $address_group_name = $1;
- print_debug("entering network obj group $address_group_name in zone $zone, context=$context", $debug, 4);
- $context = &switch_context($context, 'security/zones/address-book/address-set', $debug);
- next NEW_LINE;
- }
- if ($line=~ /^\s+description\s\"?(.+?)\"?\;$/ && ($context eq 'security/zones/address-book/address-set' || $context eq 'security/address-book/global')) {
- $comment = $1;
- next NEW_LINE;
- }
- if ($line=~ /^\s+address\s(.+?)\;$/ && $context eq 'security/zones/address-book/address-set') {
- $group_member_name = $1;
- print_debug("found address group member of group $address_group_name: $group_member_name in zone $zone", $debug, 4);
- &add_nw_obj_group_member ($address_group_name, $group_member_name, $zone, $comment, $debug);
- undef($group_member_name); undef($comment);
- next NEW_LINE;
- }
- if ($line=~ /^\s+\}$/ && $context eq 'security/zones/address-book/address-set') { $context = &switch_context($context, 'security/zones/address-book', $debug); next NEW_LINE; }
- if ($line=~ /^\}$/ && ($context =~ '^security')) { $context = &switch_context($context, '', $debug); next NEW_LINE; }
-# parsing network services
- if ($line =~ /^applications\s\{$/ && $context eq '') { $context = &switch_context($context, 'applications', $debug); next NEW_LINE; }
-# parsing service groups
- if ($line =~ /^\s*application\-set\s(.+?)\s\{$/ && $context eq 'applications') {
- $application_name = $1;
- $members = '';
- print_debug("found application-set $application_name", $debug, 6);
- $context = &switch_context($context, 'applications/application-set', $debug);
- next NEW_LINE;
- }
- if ($line =~ /^\s+application\s([\w\-\.\_]+?)\;/ && $context eq 'applications/application-set') {
- if ($members ne '') { $members .= '|'; }
- $members .= "$1";
- print_debug("found application $1 in application-set $application_name", $debug, 6);
- next NEW_LINE;
- }
-# parsing term quasi service groups
- if ($line =~ /^\s+application\s(.+?)\s\{/ && $context eq 'applications') {
- $application_name = $1;
- print_debug("found application $application_name", $debug, 4);
- $context = &switch_context($context, 'applications/application', $debug);
- next NEW_LINE;
- }
- if ($line =~ /^\s+term\sterm0\sprotocol\s(\w+?)\suuid\s(.+?)\;$/ && $context eq 'applications/application') { # parse this line twice!
- $members = ''; $members_uid = ''; $members_proto = '';
- $context = &switch_context($context, 'applications/application-set', $debug);
- print_debug("pcgo: found term, switchting to group mode ($application_name)", $debug, 6);
- }
- if ($line =~ /^\s+term\sterm\d+\sprotocol\s\w+\suuid\s(.+?)\;$/ && $context eq 'applications/application-set') {
- my $term_uuid = $1;
- if (defined($junos_uuids{"$term_uuid"})) {
- print_debug("pcgo: found single resolvable uuid ref (group: $application_name, uuid: $term_uuid) for application " . $junos_uuids{"$term_uuid"}, $debug, 6);
- if ($members ne '') { $members .= '|'; }
- $members .= $junos_uuids{"$term_uuid"};
- } else {
- print_debug("pcgo: ignoring unresolvable uuid ref ($application_name): $term_uuid", $debug, 6);
- }
- next NEW_LINE;
- }
- if ($line =~ /^\s*\}$/ && $context eq 'applications/application') { $context = &switch_context($context, 'applications', $debug); } # ignore simple applications
- if ($line =~ /^\s*\}$/ && $context eq 'applications/application-set') {
- $context = &switch_context($context, 'applications', $debug);
- # service group wegschreiben
- &add_nw_service_obj_grp ($application_name, $members, $comment, $debug);
- undef($comment);
- next NEW_LINE;
- }
- if ($line =~ /^\}$/ && $context eq 'applications') { $context = &switch_context($context, '', $debug); next NEW_LINE; }
- }
- return 0;
-}
-
-sub cfg_file_complete { # check auf Vollstaendigkeit des Config-Files:
- my $debug_level = shift;
- my $ln_cnt = $#config_lines;
- my $cfg_file_complete = 1;
-
- while ($config_lines[$ln_cnt] =~ /^\s*$/ ) { $ln_cnt -- ; } # ignore empty lines at the end
- if ($config_lines[$ln_cnt] !~ /^\s*\{primary\:node\d+\}$/ && $config_lines[$ln_cnt] !~ /^\}$/) {
- $cfg_file_complete = 0;
- print_debug ("ERROR: expected last line to contain either primary node info or top level curly bracket. Instead got: " . $config_lines[$ln_cnt], $debug_level, -1);
- }
- return $cfg_file_complete;
-}
-
-sub switch_context {
- my $old_level = shift;
- my $new_level = shift;
- my $debug_level = shift;
- print_debug("switching context from $old_level to $new_level", $debug_level, 8);
- return $new_level;
-}
-
- sub parse_config_rules { # ($in_file_main, $fworch_workdir, $debuglevel_main, $mgm_name_in_config, $config_dir, $import_id)
- my $in_file_main = shift;
- my $fworch_workdir = shift;
- my $debug = shift;
- my $mgm_name = shift;
- my $config_dir = shift;
- my $import_id = shift;
- my ($zone, $action, $source, $destination, $application, $from_zone, $to_zone, $policy_name, $disabled, $track);
- my $line = '';
- my $context = '';
- my $rule_no = 0;
- my $list;
- my $list_typ;
- my $comment;
-
- &print_debug("entering parse_config_rules =======================================================",$debug,2);
- NEW_LINE: foreach $line (@config_lines) {
- chomp($line);
- &print_debug("pcr: $line", $debug, 9);
-# rules start
- if ($line =~ /^security\s\{$/ && $context eq '') { $context = &switch_context($context, 'security', $debug); next NEW_LINE; }
- if ($line =~ /^\s+policies\s\{$/ && $context eq 'security') { $context = &switch_context($context, 'security/policies', $debug); next NEW_LINE; }
- if ($line=~ /^\s+from\-zone\s(.+?)\sto\-zone\s(.+?)\s\{$/ && $context eq 'security/policies') {
- $context = &switch_context($context, 'security/policies/zone', $debug);
- $from_zone = $1;
- $to_zone = $2;
- &print_debug("entering policy from zone $from_zone to zone $to_zone",$debug,2);
- next NEW_LINE;
- }
- if ($line=~ /^\s+\/\*\s(.*?)\s\*\/$/ && $context eq 'security/policies/zone') { $comment = $1; }
-# if ($line=~ /^\s+\#\s(.*)$/ && $context eq 'security/policies/zone') { $comment = $1; }
- if ($line=~ /^\s+(inactive)?\:?\s?policy\s(.+?)\s\{$/ && $context eq 'security/policies/zone') {
- $disabled = $1; if (!defined($disabled)) { $disabled = ''; }
- $policy_name = $2;
- $context = &switch_context($context, 'security/policies/zone/policy', $debug);
- &print_debug("entering policy $policy_name (zone $from_zone to $to_zone)",$debug,2);
- next NEW_LINE;
- }
-# match part of rule
- # new comment syntax description "xx yy zz";
- if ($line=~ /^\s+description\s\"?(.+?)\"?\;$/ && $context eq 'security/policies/zone/policy') {
- $comment = $1;
- print_debug("found rule comment $comment", $debug, 4);
- next NEW_LINE;
- }
-
- if ($line=~ /^\s+match\s\{$/ && $context eq 'security/policies/zone/policy') { $context = &switch_context($context, 'security/policies/zone/policy/match', $debug); next NEW_LINE; }
- if ($line =~ /^\s+(source|destination)\-address\s(.+?)\;?$/ && $context eq 'security/policies/zone/policy/match') {
- $list_typ = $1;
- $list = $2;
- if ($list_typ eq 'source') { $source = $list; }
- if ($list_typ eq 'destination') { $destination = $list; }
-# &print_debug("pcr: nw-list=$list", $debug, 9);
- if ($list =~ /^\[/ && $list !~ /\]$/) { $context = &switch_context($context, "security/policies/zone/policy/match/$list_typ", $debug); }
- next NEW_LINE;
- }
- if ($line =~ /^\s+application\s(.+?)\;?$/ && $context eq 'security/policies/zone/policy/match') {
- $application = $1;
-# &print_debug("pcr: appl-list=$application", $debug, 9);
- if ($application =~ /^\[/ && $application !~ /\]$/) { $context = &switch_context($context, 'security/policies/zone/policy/match/application', $debug); }
- next NEW_LINE;
- }
- if ($line=~ /^\s+\}$/ && $context eq 'security/policies/zone/policy/match') { $context = &switch_context($context, 'security/policies/zone/policy', $debug); next NEW_LINE; }
-# dealing with multiple source, destination or application lines of rules
- if ($context =~ /^security\/policies\/zone\/policy\/match\/(\w+)$/) {
- $list_typ = $1;
- my $multi_line;
- if ($line =~ /\s*(.*?)\;?$/) { $multi_line = $1; }
- if ($list_typ eq 'source') { $source .= " $multi_line"; }
- if ($list_typ eq 'destination') { $destination.= " $multi_line"; }
- if ($list_typ eq 'application') { $application .= " $multi_line"; }
- if ($multi_line =~ /\]$/) { $context = &switch_context($context, "security/policies/zone/policy/match", $debug); undef($list_typ); undef($list); } # last line reached
- next NEW_LINE;
- }
-
-# action / track part of rule (then)
- if ($line=~ /^\s+then\s\{$/ && $context eq 'security/policies/zone/policy') { $context = &switch_context($context, 'security/policies/zone/policy/action', $debug); next NEW_LINE; }
- if ($line=~ /^\s+(permit|deny|reject)\;$/ && $context eq 'security/policies/zone/policy/action') { $action = $1; next NEW_LINE; }
- if ($line=~ /^\s+(permit|deny|reject)\s\{$/ && $context eq 'security/policies/zone/policy/action') {
- $action = $1;
- $context = &switch_context($context, 'security/policies/zone/policy/action/action_parameter', $debug);
- next NEW_LINE;
- } # ignoring further action parameters
- if ($line=~ /^\s+(log|count)\s\{$/ && $context eq 'security/policies/zone/policy/action') {
- my $found_track = $1;
- if (!defined($track) || $track eq '') { $track = $found_track; }
- elsif ($found_track eq 'count' && $track eq 'log') { $track = 'log count'; }
- $context = &switch_context($context, 'security/policies/zone/policy/action/action_parameter', $debug);
- next NEW_LINE;
- } # ignoring further tracking parameters
-=cut
- track=log wird in diesem Fall nicht gefunden:
- permit {
- destination-address {
- drop-untranslated;
- }
- }
- log {
- session-close;
- }
-=cut
- if ($line=~ /^\s+.+?\{$/ && $context eq 'security/policies/zone/policy/action/action_parameter') {
- $context = &switch_context($context, 'security/policies/zone/policy/action/action_parameter/null', $debug);
- next NEW_LINE;
- }
-#closing sections
- if ($line=~ /^\s+.+?\{$/ && $context eq 'security/policies/zone/policy/action/action_parameter/null') {
- $context = &switch_context($context, 'security/policies/zone/policy/action/action_parameter', $debug);
- next NEW_LINE;
- }
- if ($line=~ /^\s+\}$/ && $context eq 'security/policies/zone/policy/action/action_parameter') { $context = &switch_context($context, 'security/policies/zone/policy/action', $debug); next NEW_LINE; }
- if ($line=~ /^\s+\}$/ && $context eq 'security/policies/zone/policy/action') { $context = &switch_context($context, 'security/policies/zone/policy', $debug); next NEW_LINE; }
- if ($line=~ /^\s+\}$/ && $context eq 'security/policies/zone/policy') {
- if (!defined($track)) { $track=''; }
- &print_debug("found rule from $from_zone to $to_zone, name=$policy_name, disabled=$disabled: src=$source, dst=$destination, svc=$application, action=$action, track=$track",$debug,2);
- # Regel wegschreiben
- $rule_no = &add_rule ($rule_no, $from_zone, $to_zone, $policy_name, $disabled, $source, $destination, $application, $action, $track, $comment, $debug);
- $context = &switch_context($context, 'security/policies/zone', $debug);
- $track = ''; undef($policy_name); undef($comment);
- next NEW_LINE;
- } # end of rule
- if ($line=~ /^\s+\}$/ && $context eq 'security/policies/zone') { $context = switch_context($context, 'security/policies', $debug); undef($from_zone); undef($to_zone); next NEW_LINE; }
- if ($line=~ /^\s+\}$/ && $context eq 'security/policies') { $context = &switch_context($context, 'security', $debug); next NEW_LINE; }
- if ($line=~ /^\}$/) { $context = &switch_context($context, '', $debug); next NEW_LINE; }
- }
- &sort_rules_and_add_zone_headers ();
- $rulebases{"$rulebase_name.ruleorder"} = join(',', @ruleorder);
- return 0;
- }
-
-#####################################################################################
-# MAIN
-
-sub parse_config { # ($obj_file, $rule_file, $rulebases, $user, $fworch_workdir, $debug_level, $mgm_name, $config_dir, $import_id)
- my $in_file_main = shift;
- shift; shift; shift; # $rule_file und $rulebases und $user nicht verwendet
- my $fworch_workdir = shift;
- my $debuglevel_main = shift;
- my $mgm_name = shift;
- my $config_dir = shift;
- my $import_id = shift;
- my $debug_level = shift;
-
- # initializing global variables:
- @services = ();
- @network_objects = ();
- &print_debug ("in_file_main=$in_file_main, fworch_workdir=$fworch_workdir, debuglevel_main=$debuglevel_main, mgm_name=$mgm_name, config_dir=$config_dir, import_id=$import_id", $debuglevel_main, 6);
-
- open (IN, $in_file_main) || die "$in_file_main konnte nicht geoeffnet werden.\n";
- @config_lines = ; # sichern Config-array fuer spaetere Verwendung
- close (IN);
-
- if (!&cfg_file_complete($debuglevel_main)) { return "incomplete-config-file-$mgm_name"; }
- else {
- my $device_type=8; # junos 10.x fest gesetzt # move to config
- &read_predefined_services($device_type, $debuglevel_main); # schreibt die predefined services in @services und %services
- my $mgm_name_in_config = &parse_mgm_name($in_file_main, $fworch_workdir, $debuglevel_main, $mgm_name, $config_dir, $import_id);
- &parse_config_base_objects ($in_file_main, $fworch_workdir, $debuglevel_main, $mgm_name, $config_dir, $import_id); # zones, simple network and service objects
- push @zones, "global"; # Global Zone immer hinzufuegen
- foreach my $zone (@zones) { object_address_add("any", "0.0.0.0", "0.0.0.0", $zone, "any-obj for zone $zone added by fworch"); &print_debug(""); } # Any-Objekte fuer alle Zonen einfuegen
- &parse_config_group_objects ($in_file_main, $fworch_workdir, $debuglevel_main, $mgm_name, $config_dir, $import_id); # groups are parsed in separate cycle to ensure that all base objects are complete
-# &resolve_service_uuid_references ($debuglevel_main);
- &parse_config_rules ($in_file_main, $fworch_workdir, $debuglevel_main, $mgm_name_in_config, $config_dir, $import_id); # finally parsing the rule base, ignoring the rulebase name in fworch config
-
- &print_results_files_objects($fworch_workdir, $mgm_name, $import_id);
- &print_results_files_rules ($fworch_workdir, $mgm_name, $import_id);
- &print_results_files_zones ($fworch_workdir, $mgm_name, $import_id);
-# print_results_monitor('objects');
-# print_results_monitor('rules');
- }
- return 0;
-}
-
-1;
-__END__
-
-=head1 NAME
-
-CACTUS::FWORCH::parser - Perl extension for JunOS parser
-
-=head1 SYNOPSIS
-
- use CACTUS::FWORCH::import::juniper;
-
-=head1 DESCRIPTION
-
-fworch Perl Module support for importing configs into fworch Database
-
-=head2 EXPORT
-
- global variables
-
-
-=head1 SEE ALSO
-
- behind the door
-
-=head1 AUTHOR
-
- Cactus eSecurity, tmp@cactus.de
-
-=cut
diff --git a/roles/importer/files/importer/CACTUS/FWORCH/import/netscreen.pm b/roles/importer/files/importer/CACTUS/FWORCH/import/netscreen.pm
deleted file mode 100644
index 5f44d0d406..0000000000
--- a/roles/importer/files/importer/CACTUS/FWORCH/import/netscreen.pm
+++ /dev/null
@@ -1,1147 +0,0 @@
-package CACTUS::FWORCH::import::parser;
-
-use strict;
-use warnings;
-use Time::HiRes qw(time); # fuer hundertstelsekundengenaue Messung der Ausfuehrdauer
-use CACTUS::FWORCH;
-use CACTUS::FWORCH::import;
-use CACTUS::read_config;
-# use Scalar::Util;
-
-require Exporter;
-our @ISA = qw(Exporter);
-
-our %EXPORT_TAGS = ( 'basic' => [ qw( ©_config_from_mgm_to_iso &parse_config ) ] );
-
-our @EXPORT = ( @{ $EXPORT_TAGS{'basic'} } );
-our $VERSION = '0.3';
-
-our @config_lines; # array der Zeilen des Config-Files im Originalzustand (kein d2u etc.)
-our $parser_mode; # Typ des Config-Formates (basic, data)
-our $rule_order = 0; # Reihenfolge der Rules im Configfile
-our $rulebase_name;
-
-## parse_audit_log Funktion fuer netscreen noch nicht implementiert
-
-sub parse_audit_log { }
-
-######################################
-# remove_unwanted_chars
-# param1: debuglevel [0-?]
-# gobal var in and out: @config_lines
-#####################################
-sub remove_unwanted_chars {
- my $debuglevel = shift;
- my $line;
- my @cleaned_lines = ();
-
- LINE: foreach $line (@config_lines) {
- if ($line =~ /^\s*$/) { # ignore empty lines
- } else { # remove non-printable characters
- my $line_orig = $line;
- $line =~ s/[^[:print:]]+//g;
- if ($line =~ /^\s+(.+?)$/) { $line = $1; }
- if ($line =~ /^(.+?)\s+$/) { $line = $1; }
- $line .= "\n";
- @cleaned_lines = (@cleaned_lines, "$line");
-# if ($line_orig ne $line) { print ("cleaned config line containing non-printable characters.\nOriginal line: $line_orig to: $line"); }
- }
- }
- @config_lines = @cleaned_lines;
-}
-
-######################################
-# ns_mode_check
-# bestimmt den Typ des Config-Formates
-# param1: input-filename
-# param2: debuglevel [0-?]
-#####################################
-sub ns_mode_check {
- my $debuglevel = shift;
- my $mode = '';
- my $line_num_for_check = 10; # number of lines for testing the config format
- my $i = 0;
- # Setzen des Parsermodes
- # umschalten des Betriebsmodus anhand der ersten $line_num_for_check Zeile des uebergebenen Konfigurationsfiles
- MODE_DEF: {
- for ($i = 0; $i < $line_num_for_check; ++$i) {
- if ($config_lines[$i]=~/^u?n?set\s/) {
- $mode = 'basic';
- last MODE_DEF;
- }
- elsif ($config_lines[$i]=~/^\(DM/) {
- $mode = 'data';
- last MODE_DEF;
- }
- else {
- if ($mode !~ /basic/ or /data/) {
- $mode = 'unknown';
- }
- }
- if ($debuglevel >= 7){
- print ("sub ns_mode_check line_$i, mode: $mode : $config_lines[$i]\n");
- }
- }
- $parser_mode = $mode;
- if ($mode =~/unknown/) {
- print "unknown format in config file - exiting!\n";
- return 1;
- }
- }
- if ($debuglevel >= 1){
- print ("sub ns_mode_check line_$i, mode: $mode : $config_lines[$i]\n");
- }
- return 0;
-}
-
-
-#####################################
-# is_domain_name
-# param1: string
-# returns true if string is domain name and no ip
-#####################################
-sub is_domain_name {
- my @parts = split(/\./, shift);
- if (scalar(@parts) > 1) {
- if (Scalar::Util::looks_like_number($parts[$#parts])) {
- return 0;
- } else {
- return 1;
- }
- }
- return 0;
-}
-
-#####################################
-# split_lines_to_parameter
-# param1: input-line
-# param2: debuglevel [0-?]
-# Separator=space, Strings in double quotes
-#####################################
-sub split_lines_to_parameter {
- my $line = $_[0];
- my $debuglevel = $_[1];
- my $i=0;
- my $l=0;
- my $k=$i;
- my @params;
- my @parameter = ();
-
- $parameter[$k] ='';
- $line =~ s/\n//;
- $line =~ s/\r//;
- @params = split (/ /,$line);
- while ($i <= $#params) {
- if ($params[$i] =~ /^".+"$/) {
- $parameter[$k] = "$params[$i]";
- $parameter[$k] =~ s/"//g;
- $k++;
- $i++;
- }
- elsif ($params[$i] =~ /^"/) {
- do {
- if (defined($parameter[$k])) {
- $parameter[$k] = "$parameter[$k] $params[$i]";
- } else {
- $parameter[$k] = "$params[$i]";
- }
- }
- while ($params[$i++] !~ /"$/ and $i <= $#params);
- $parameter[$k] =~ s/^\s//;
- $parameter[$k] =~ s/"//g;
- $k++;
- }
- else {
- $parameter[$k] = "$params[$i]";
- $k++;
- $i++;
- }
- }
- @parameter = @parameter;
-}
-
-
-#####################################
-# ns_object_address
-# param1: input-line
-# param2: debuglevel [0-?]
-#####################################
-sub ns_object_address {
- my $line = $_[0];
- my $debuglevel = $_[1];
- my $i=0;
- my $l=0;
- my @params;
- my $act_obj_zone = '';
- my $act_obj_name = '';
- my $act_obj_nameZ = '';
- my $act_obj_ipaddr = '';
- my $act_obj_ipaddr_last = '';
- my $act_obj_mask = '';
- my $act_obj_comm = '';
- my $act_obj_type = '';
- my $act_obj_loc = '';
- my $act_obj_color = 'black';
- my $act_obj_sys = '';
- my $act_obj_uid = '';
- my $test = '';
-
- @params = split_lines_to_parameter ($line,$debuglevel);
- $act_obj_zone = $params[2];
- $act_obj_name = $params[3];
- $act_obj_ipaddr = $params[4];
- $act_obj_mask = $params[5];
- if ($params[6]) {
- $act_obj_comm = $params[6];
- } else {
- if (defined($act_obj_mask) && $act_obj_mask !~ /\d+\.\d+\.\d+\.\d+/) { # 5. Parameter ist keine Netzmask, sondern Kommentar (bei v6-Objekten)
- $act_obj_comm = $act_obj_mask;
- ($test, $act_obj_mask) = split(/\//, $act_obj_ipaddr);
- }
- }
- if (&is_domain_name($act_obj_ipaddr)) {
- if (!defined($act_obj_comm) || $act_obj_comm eq '') {
- $act_obj_comm = "domain object: $act_obj_ipaddr";
- } else {
- $act_obj_comm .= ", domain object: $act_obj_ipaddr";
- }
- $act_obj_ipaddr = '0.0.0.1/32';
- }
-
- $act_obj_nameZ = "${act_obj_name}__zone__$act_obj_zone"; # Zone in Feldindex mit aufgenommen
- OBJECT_DEF_GROUP: {
- if (!defined ($network_objects{"$act_obj_nameZ.name"})) {
- @network_objects = (@network_objects, $act_obj_nameZ);
- $network_objects{"$act_obj_nameZ.name"} = $act_obj_name;
- $network_objects{"$act_obj_nameZ.UID"} = $act_obj_nameZ;
- if ($debuglevel >= 5){ print "ns_object_address: found new network object $act_obj_name\n"; }
- }
- else {
- last OBJECT_DEF_GROUP;
- }
- }
- my $is_ipv6 = ($act_obj_ipaddr =~ /\:/);
- my $subnetbits;
- if ($is_ipv6) {
- if (defined($act_obj_mask)) {
- $subnetbits = $act_obj_mask;
- } else {
- my $addr;
- ($addr, $subnetbits) = split (/\//, $act_obj_ipaddr);
- if (!defined($subnetbits)) {
- $subnetbits = 128;
- }
- }
- } else {
- $subnetbits = &calc_subnetmask ($act_obj_mask);
- }
-# print ("debug: ip_addr = $act_obj_ipaddr, mask=$act_obj_mask, subnetbits = $subnetbits\n");
- if (!$is_ipv6) {
- if (!defined($act_obj_mask)) {
- $act_obj_mask = '255.255.255.255';
- }
- if (!defined($subnetbits) || $subnetbits==32) { $network_objects{"$act_obj_nameZ.type"} = 'host'; }
- else { $network_objects{"$act_obj_nameZ.type"} = 'network'; }
- } else {
- if ($subnetbits==128) { $network_objects{"$act_obj_nameZ.type"} = 'host'; }
- else { $network_objects{"$act_obj_nameZ.type"} = 'network'; }
- }
- $network_objects{"$act_obj_nameZ.netmask"} = $act_obj_mask ;
- $network_objects{"$act_obj_nameZ.zone"} = $act_obj_zone; # neues Feld fuer Zone
- $network_objects{"$act_obj_nameZ.ipaddr"} = $act_obj_ipaddr;
- $network_objects{"$act_obj_nameZ.ipaddr_last"} = $act_obj_ipaddr_last;
- $network_objects{"$act_obj_nameZ.color"} = $act_obj_color;
- $network_objects{"$act_obj_nameZ.comments"} = $act_obj_comm;
- $network_objects{"$act_obj_nameZ.location"} = $act_obj_loc;
- $network_objects{"$act_obj_nameZ.sys"} = $act_obj_sys;
-}
-
-#####################################
-# ns_object_address_add
-# param1: name
-# param2: ip
-#####################################
-sub ns_object_address_add {
- my $act_obj_name = $_[0];
- my $act_obj_ipaddr = $_[1];
- my $act_obj_mask = $_[2];
- my $act_obj_zone = $_[3];
- my $act_obj_comm = $_[4];
- my @params;
- my $act_obj_nameZ = '';
- my $act_obj_ipaddr_last = '';
- my $act_obj_type = 'simple';
- my $act_obj_loc = '';
- my $act_obj_color = 'black';
- my $act_obj_sys = '';
- my $act_obj_uid = '';
- my $subnetbits;
-
- $act_obj_nameZ = "${act_obj_name}__zone__$act_obj_zone"; # Zone in Feldindex mit aufgenommen
- if (!defined ($network_objects{"$act_obj_nameZ.name"})) {
- @network_objects = (@network_objects, $act_obj_nameZ);
- $network_objects{"$act_obj_nameZ.name"} = $act_obj_name;
- } elsif (defined ($network_objects{"$act_obj_nameZ.name"})) {
- print "sub ns_object_address NET_OBJECT: $act_obj_nameZ ist bereits definiert.\n";
- } else {
- print "sub ns_object_address NET_OBJECT: $act_obj_nameZ ist undefiniert.\n";
- }
- my $is_ipv6 = ($act_obj_ipaddr =~ /\:/);
- if ($is_ipv6) {
- if (defined($act_obj_mask)) {
- $subnetbits = $act_obj_mask;
- } else {
- $subnetbits = 128;
- }
- } else {
- $subnetbits = &calc_subnetmask ($act_obj_mask);
- }
- if (!$is_ipv6) {
- if (!defined($act_obj_mask)) {
- $act_obj_mask = '255.255.255.255';
- }
- if ($subnetbits==32) { $network_objects{"$act_obj_nameZ.type"} = 'host'; }
- else { $network_objects{"$act_obj_nameZ.type"} = 'network'; }
- } else {
- if ($subnetbits==128) { $network_objects{"$act_obj_nameZ.type"} = 'host'; }
- else { $network_objects{"$act_obj_nameZ.type"} = 'network'; }
- }
- if (&is_domain_name($act_obj_ipaddr)) {
- if (!defined($act_obj_comm) || $act_obj_comm eq '') {
- $act_obj_comm = "domain object: $act_obj_ipaddr";
- } else {
- $act_obj_comm .= ", domain object: $act_obj_ipaddr";
- }
- $act_obj_ipaddr = '0.0.0.1/32';
- }
-
- $network_objects{"$act_obj_nameZ.netmask"} = $act_obj_mask ;
- $network_objects{"$act_obj_nameZ.zone"} = $act_obj_zone; # neues Feld fuer Zone
- $network_objects{"$act_obj_nameZ.ipaddr"} = $act_obj_ipaddr;
- $network_objects{"$act_obj_nameZ.ipaddr_last"} = $act_obj_ipaddr_last;
- $network_objects{"$act_obj_nameZ.color"} = $act_obj_color;
- $network_objects{"$act_obj_nameZ.comments"} = $act_obj_comm;
- $network_objects{"$act_obj_nameZ.location"} = $act_obj_loc;
- $network_objects{"$act_obj_nameZ.sys"} = $act_obj_sys;
- $network_objects{"$act_obj_nameZ.UID"} = $act_obj_nameZ;
-}
-
-#####################################
-# ns_object_group_address
-# param1: input-line
-# param2: debuglevel [0-?]
-#####################################
-sub ns_object_group_address {
- my $line = $_[0];
- my $debuglevel = $_[1];
- my $i=0;
- my $l=0;
- my @params;
- my $act_obj_zone = '';
- my $act_obj_name = '';
- my $act_obj_nameZ = '';
- my $act_obj_mbr = '';
- my $act_obj_fkt = '';
- my $act_obj_color = 'black';
- my $act_obj_comm = '';
- my $mbrlst = '';
- my $mbr_ref_lst = '';
-
- @params = split_lines_to_parameter ($line,$debuglevel);
- $act_obj_zone = $params[3];
- $act_obj_name = $params[4];
- if ($params[6]) { $act_obj_mbr = $params[6]; }
- if ($params[5]) { $act_obj_fkt = $params[5]; }
-
- $act_obj_nameZ = "${act_obj_name}__zone__$act_obj_zone"; # Zone in Feldindex mit aufgenommen
- OBJECT_DEF: {
- if (!defined ($network_objects{"$act_obj_nameZ.name"})) {
- if ($debuglevel >= 5){ print "ns_object_group_address: found new network object group $act_obj_name\n"; }
- @network_objects = (@network_objects, $act_obj_nameZ);
- $network_objects{"$act_obj_nameZ.name"} = $act_obj_name;
- $network_objects{"$act_obj_nameZ.UID"} = $act_obj_nameZ;
- }
- else {
- last OBJECT_DEF;
- }
- }
- $network_objects{"$act_obj_nameZ.zone"} = $act_obj_zone; # neues Feld fuer Zone
- $network_objects{"$act_obj_nameZ.type"} = 'group';
- $network_objects{"$act_obj_nameZ.color"} = $act_obj_color;
- if ( $act_obj_fkt =~ /add/ ) {
- if (defined($network_objects{"$act_obj_nameZ.members"})) {
- $mbrlst = $network_objects{"$act_obj_nameZ.members"};
- $mbr_ref_lst = $network_objects{"$act_obj_nameZ.member_refs"};
- }
- if ( $mbrlst eq '' ) {
- $mbrlst = $act_obj_mbr;
- $mbr_ref_lst = $act_obj_mbr . "__zone__$act_obj_zone";
- }
- else {
- $mbrlst = "$mbrlst|$act_obj_mbr";
- $mbr_ref_lst = "$mbr_ref_lst|$act_obj_mbr" . "__zone__$act_obj_zone";
- }
- $network_objects{"$act_obj_nameZ.members"} = $mbrlst;
- $network_objects{"$act_obj_nameZ.member_refs"} = $mbr_ref_lst;
- }
-}
-
-#####################################
-# ns_object_service
-# param1: input-line
-# param2: debuglevel [0-?]
-#####################################
-sub ns_object_service {
- my $line = $_[0];
- my $debuglevel = $_[1];
- my $i=0;
- my $l=0;
- my @params;
- my @range;
- my $act_obj_typ = '';
- my $act_obj_type = '';
- my $act_obj_name = '';
- my $act_obj_proto = '';
- my $act_obj_src = '';
- my $act_obj_src_last = '';
- my $act_obj_dst = '';
- my $act_obj_dst_last = '';
- my $act_obj_comm = '';
- my $act_obj_time = '';
- my $act_obj_time_std = '';
- my $act_obj_color = 'black';
- my $act_obj_rpc = '';
- my $act_obj_uid = '';
- my $extention = 0;
-
- @params = split_lines_to_parameter ($line,$debuglevel);
- $act_obj_name = $params[2];
- $extention = $params[3];
- $act_obj_typ = 'simple';
- $act_obj_type = $params[4];
- $act_obj_src = $params[6];
- $act_obj_dst = $params[8];
- if ($params[10]) {
- my $time1 = $params[10];
- if ($time1 eq 'never') {
- $time1 = 43200; # ca. 1 Monat sollte als utopisches Maximum reichen
- }
- $act_obj_time = 60 * $time1;
- }
- if ($extention =~ /protocol/) {
- if (!defined ($services{"$act_obj_name.name"})) {
- if ($debuglevel >= 5){ print "ns_object_service: found new service object $act_obj_name\n"; }
- @services = (@services, $act_obj_name);
- $services{"$act_obj_name.name"} = $act_obj_name;
- $services{"$act_obj_name.extention"} = $extention;
- }
- else {
- if ( $services{"$act_obj_name.comments"} =~ /netscreen_predefined_service/ ) {
- } else {
- print "sub ns_object_services SERVICE: $act_obj_name ist bereits definiert.\n";
- }
- }
- }
- elsif ($extention =~ /\+/) { # MultiDienste (+service)
- my $group_name = $act_obj_name;
- if (!defined ($services{"$act_obj_name.name"})) {
- print ("Fehler: +Service $act_obj_name ohne vorherige Definition\n");
- } else {
- if ($services{"$act_obj_name.typ"} ne 'group') { # ersten Dienst erst in Gruppe umwandeln
- # dazu erstmal den alten Dienst duplizieren (mit no=0)
- my $first_name = $act_obj_name . "_netscreen_PLUS_1";
- $services{"$first_name.name"} = $first_name;
- $services{"$first_name.src_port"} = $services{"$act_obj_name.src_port"};
- $services{"$first_name.src_port_last"} = $services{"$act_obj_name.src_port_last"};
- $services{"$first_name.port"} = $services{"$act_obj_name.port"};
- $services{"$first_name.port_last"} = $services{"$act_obj_name.port_last"};
- $services{"$first_name.ip_proto"} = $services{"$act_obj_name.ip_proto"};
- $services{"$first_name.timeout"} = $services{"$act_obj_name.timeout"};
- $services{"$first_name.color"} = $services{"$act_obj_name.color"};
- $services{"$first_name.comments"} = $services{"$act_obj_name.comments"};
- $services{"$first_name.typ"} = $services{"$act_obj_name.typ"};
- $services{"$first_name.type"} = $services{"$act_obj_name.type"};
- if (defined($services{"$act_obj_name.rpc_port"})) {
- $services{"$first_name.rpc_port"} = $services{"$act_obj_name.rpc_port"};
- }
- $services{"$first_name.UID"} = $first_name;
- push @services, $first_name;
-
- # jetzt den Ursprungsdienst in eine Gruppe umwandeln
- $services{"$group_name.typ"} = 'group';
- $services{"$group_name.members"} = "$first_name";
- $services{"$group_name.member_refs"} = "$first_name";
- $services{"$group_name.member_zahl"} = 1;
- undef ( $services{"$group_name.src_port"} );
- undef ( $services{"$group_name.src_port_last"} );
- undef ( $services{"$group_name.port"} );
- undef ( $services{"$group_name.port_last"} );
- undef ( $services{"$group_name.ip_proto"} );
- }
- $services{"$group_name.member_zahl"} = $services{"$group_name.member_zahl"} + 1;
- my $member_zahl = $services{"$group_name.member_zahl"};
- my $new_plus_svc_name = $group_name . "_netscreen_PLUS_" . $services{"$group_name.member_zahl"};
- push @services, $new_plus_svc_name;
- $act_obj_name = $new_plus_svc_name;
- $services{"$act_obj_name.name"} = $act_obj_name;
- $services{"$act_obj_name.UID"} = $act_obj_name;
- $services{"$act_obj_name.extention"} = $extention;
- $services{"$group_name.members"} .= "|$new_plus_svc_name";
- $services{"$group_name.member_refs"} .= "|$new_plus_svc_name";
- }
- } else {
- print "?\n";
- print "$line\n";
- }
- if ($act_obj_type =~ /rpc/) {
-# print "RPC-line: $line; obj_type: $act_obj_type, act_obj_src: $act_obj_src\n";
- $services{"$act_obj_name.src_port"} = 0;
- $services{"$act_obj_name.src_port_last"} = 65535;
- $services{"$act_obj_name.port"} = 0;
- $services{"$act_obj_name.port_last"} = 65535;
- $services{"$act_obj_name.typ"} ='rpc';
-# $services{"$act_obj_name.rpc_nr"} = $act_obj_src;
- $act_obj_rpc = $act_obj_src;
-# $act_obj_rpc = $act_obj_src;
-# if ($line =~ /protocol ms-rpc (uuid .*)$/) {
-# my $uuid = $1;
-# print " found in RPC-line: $uuid\n";
-# $services{"$act_obj_name.rpc_nr"} = $1;
-# $services{"$act_obj_name.svc_prod_specific"} = $1;
-# }
- } else {
- @range = split ( /-/, $act_obj_src);
- $services{"$act_obj_name.src_port"} = $range[0];
- $services{"$act_obj_name.src_port_last"} = $range[1];
- @range = split ( /-/, $act_obj_dst);
- $services{"$act_obj_name.port"} = $range[0];
- $services{"$act_obj_name.port_last"} = $range[1];
- }
- $services{"$act_obj_name.ip_proto"} = get_proto_number($act_obj_type);
- $services{"$act_obj_name.timeout"} = $act_obj_time;
- $services{"$act_obj_name.color"} = $act_obj_color;
- $services{"$act_obj_name.comments"} = $act_obj_comm;
- $services{"$act_obj_name.typ"} = $act_obj_typ;
- $services{"$act_obj_name.type"} = $act_obj_type;
- $services{"$act_obj_name.rpc_port"} = $act_obj_rpc;
- $services{"$act_obj_name.UID"} = $act_obj_name;
-}
-
-#####################################
-# ns_object_group_service
-# param1: input-line
-# param2: debuglevel [0-?]
-#####################################
-sub ns_object_group_service {
- my $line = $_[0];
- my $debuglevel = "";
- my $i=0;
- my $l=0;
- my @params;
- my @range;
- my $act_obj_typ = 'group';
- my $act_obj_type = '';
- my $act_obj_mbr = '';
- my $act_obj_fkt = '';
- my $act_obj_name = '';
- my $act_obj_proto = '';
- my $act_obj_src = '';
- my $act_obj_src_last = '';
- my $act_obj_dst = '';
- my $act_obj_dst_last = '';
- my $act_obj_comm = '';
- my $act_obj_time = '';
- my $act_obj_time_std = '';
- my $act_obj_color = 'black';
- my $act_obj_rpc = '';
- my $act_obj_uid = '';
- my $mbrlst = '';
- my $test = '';
-
- if (defined($_[1])) {
- $debuglevel = $_[1];
- }
- @params = split_lines_to_parameter ($line,$debuglevel);
- $act_obj_name = $params[3];
- if ($params[5]) {
- $act_obj_mbr = $params[5];
- }
- if ($params[4]) {
- $act_obj_fkt = $params[4];
- }
-
- if (!defined ($services{"$act_obj_name.name"})) {
- @services = (@services, $act_obj_name);
- $services{"$act_obj_name.name"} = $act_obj_name;
- }
- if ( $act_obj_fkt =~ /add/ ) {
- if (defined($services{"$act_obj_name.members"})) {
- $mbrlst = $services{"$act_obj_name.members"};
- }
- if ( $mbrlst eq '' ) {
- $mbrlst = $act_obj_mbr;
- }
- else {
- $mbrlst = "$mbrlst|$act_obj_mbr";
- }
- $services{"$act_obj_name.members"} = $mbrlst;
- $services{"$act_obj_name.member_refs"} = $mbrlst;
- }
- $services{"$act_obj_name.ip_proto"} = $act_obj_proto;
- $services{"$act_obj_name.timeout"} = $act_obj_time;
- $services{"$act_obj_name.color"} = $act_obj_color;
- $services{"$act_obj_name.comments"} = $act_obj_comm;
- $services{"$act_obj_name.typ"} = $act_obj_typ;
- $services{"$act_obj_name.type"} = $act_obj_type;
- $services{"$act_obj_name.rpc_port"} = $act_obj_rpc;
- $services{"$act_obj_name.UID"} = $act_obj_name;
-}
-
-#####################################
-# ns_rules_initial
-# param1: input-line [string]
-# param2: rule_order, [integer] Reihenfolge der Rules im Configfile
-#debuglevel [integer]
-#####################################
-sub ns_rules_initial {
- my $line = $_[0];
- my $order = $_[1];
- my $debuglevel = $_[2];
- my $i=0;
- my $l=0;
- my $act_rule_id = -1;
- my $act_rule_ruleid = -1;
- my $act_rule_name = '';
- my $act_rule_Szone = '';
- my $act_rule_Dzone = '';
- my $act_rule_src = '';
- my $act_rule_dst = '';
- my $act_rule_srv = '';
- my $act_rule_act = '';
- my $act_rule_track = '';
- my $act_rule_count = '';
- my @params;
- my @range;
- my $from_zone_idx = 5;
- my ($nat_idx, $action_idx);
-
- # Handling von dst MIP-IPs, muster: set policy id 8 from "Untrust" to "Trust" "Any" "MIP(12.7.2.0/24)" "ANY" permit log count
- if ($line =~ /set policy id \d+ (name .*?)?from \"([\w\_\-\.]+)\" to \"([\w\_\-\.]+)\"\s+\".*?\"\s+\"MIP\(([\d\.]+)\/?(\d+)?\)\"/) {
- my $mip_ip = $4;
- my $dst_zone = $3;
- my $netmask_bits = $5;
- my $netmask_string = '';
- if (!defined($netmask_bits) || $netmask_bits eq '') { $netmask_bits = 32; }
- my $netmask_dotted = &convert_mask_to_dot_notation($netmask_bits);
- if ($netmask_bits<32) { $netmask_string = "/$netmask_bits"; }
- if ($debuglevel>1) { print("ns_rules_initial: found dst MIP. MIP($mip_ip$netmask_string) in zone $dst_zone\n"); }
- &ns_object_address_add("MIP($mip_ip$netmask_string)", "$mip_ip", $netmask_dotted, $dst_zone, 'Virtual MIP, generated by fworch from interface MIP definition');
- }
-=cut
- # Handling von src MIP-IPs (wenn es sowas gibt), muster: set policy id 8 from "Untrust" to "Trust" "MIP(12.7.2.27)" "Any" "ANY" permit log count
- if ($line =~ /set policy id (name .*?)?\d+ from \"([\w\_\-\.]+)\" to \"([\w\_\-\.]+)\"\s+\"MIP\(([\d\.]+)\/?(\d+)?\)\"\s+\".*?\"/) {
- my $mip_ip = $4;
- my $src_zone = $2;
- my $netmask_bits = $5;
- my $netmask_string = '';
- if (!defined($netmask_bits) || $netmask_bits eq '') { $netmask_bits = 32; }
- my $netmask_dotted = &convert_mask_to_dot_notation($netmask_bits);
- if ($netmask_bits<32) { $netmask_string = "/$netmask_bits"; }
- &ns_object_address_add("MIP($mip_ip$netmask_string)", "$mip_ip", $netmask_dotted, $src_zone, 'Virtual MIP, generated by fworch from interface MIP definition');
- }
-=cut
- $rulebases{"$rulebase_name.rulecount"} = $order + 1; # Anzahl der Regeln wird sukzessive hochgesetzt
- @params = split_lines_to_parameter ($line,$debuglevel);
- $act_rule_id = $params[3];
- $act_rule_ruleid = $act_rule_id;
- $ruleorder[$order] = $act_rule_id;
- if ($params[4] =~ /^name$/) {
- $act_rule_name = $params[5];
- $from_zone_idx = 7;
- }
- $nat_idx = $from_zone_idx + 7;
- $action_idx = $nat_idx; # Normalfall ohne NAT
- $act_rule_Szone = $params[$from_zone_idx+0];
- $act_rule_Dzone = $params[$from_zone_idx+2];
- $act_rule_src = $params[$from_zone_idx+4];
- $act_rule_dst = $params[$from_zone_idx+5];
- my $act_rule_src_refs = "${act_rule_src}__zone__$act_rule_Szone";
- my $act_rule_dst_refs = "${act_rule_dst}__zone__$act_rule_Dzone";
- $act_rule_srv = $params[$from_zone_idx+6];
- if ($params[$nat_idx] =~ /^nat$/) {
- my $nat_type = "initial";
- my $line_after_nat = "initial";
- my $found_nat = 0;
-
- if ($line =~ /(.*?)\s+nat\s+(.*)/) { $line_after_nat = $2; }
-
- if (!$found_nat && $line_after_nat =~ /^dst ip [\d\.]+ port \d+ .*/) { # nat dst ip 10.132.160.12 port 44316 permit log
- $action_idx += 6; $found_nat = 1; $nat_type = '7 dst nat with port';
- }
- if (!$found_nat && $line_after_nat =~ /^dst ip [\d\.]+ [\d\.]+ .*/) { # nat dst ip 2.52.20.0 2.52.21.255 permit log
- $action_idx += 5; $found_nat = 1; $nat_type = '8 dst nat with ip range';
- }
- if (!$found_nat && $line_after_nat =~ /^dst ip [\d\.]+ .*/) { # nat dst ip 10.132.160.12 permit log
- $action_idx += 4; $found_nat = 1; $nat_type = '1 dst nat simple';
- }
- if (!$found_nat && $line_after_nat =~ /^src dst ip [\d\.]+ port \d+ .*/) { # nat src dst ip 192.168.0.4 port 22 permit log
- $action_idx += 7; $found_nat = 1; $nat_type = '9 src & dst & port nat';
- }
- if (!$found_nat && $line_after_nat =~ /^src dip\-id \d+ dst ip [\d\.]+ port \d+ .*/) { # nat src dip-id 32 dst ip 10.132.160.12 [port 222] permit log
- $action_idx += 9; $found_nat = 1; $nat_type = '6 src & dst & dip nat & port nat';
- }
- if (!$found_nat && $line_after_nat =~ /^src dip\-id \d+ dst ip [\d\.]+ .*/) { # nat src dip-id 32 dst ip 10.132.160.12 permit log
- $action_idx += 7; $found_nat = 1; $nat_type = '2 src & dst & dip nat';
- }
- if (!$found_nat && $line_after_nat =~ /^src dst ip [\d\.]+ .*/) {# nat src dst ip 172.20.128.1
- $action_idx += 5; $found_nat = 1; $nat_type = '3 src dst nat';
- }
- if (!$found_nat && $line_after_nat =~ /^src dip\-id \d+ .*/) {# nat src dip-id 4 permit log
- $action_idx += 4; $found_nat = 1; $nat_type = '4 src & dip-id';
- }
- if (!$found_nat && $line_after_nat =~ /^src .*/) { # nat src permit --> einfachster Fall der src nat ohne weitere Parameter
- $action_idx += 2; $found_nat = 1; $nat_type = '5 simple src nat';
- }
- }
- if (defined($params[$action_idx+0])) { $act_rule_act = $params[$action_idx+0]; }
- my $track_idx = $action_idx+1;
- if (defined($act_rule_act) && $act_rule_act eq 'tunnel') { # vpn regel
- $act_rule_act .= " $params[$action_idx+1]";
- $track_idx = $action_idx+2; # name des tunnels auslassen
- }
- if (defined($params[$track_idx]) && $params[$track_idx] =~ /auth|webauth/) {
- $act_rule_act .= " $params[$track_idx]";
- $track_idx ++;
- }
- my @track_types = qw(log alert count alarm);
- while (defined($params[$track_idx])) { # collecting track info
- my $pattern_to_find = $params[$track_idx];
- if (grep(/$pattern_to_find/, @track_types)) {
- if (defined($act_rule_track) && length($act_rule_track)>0) {
- $act_rule_track .= " " . $params[$track_idx];
- } else {
- $act_rule_track = $params[$track_idx];
- }
- }
- $track_idx ++;
- }
- if (!defined($act_rule_track) || $act_rule_track eq '') { $act_rule_track = 'none'; }
- if (length($act_rule_track)<3) {
- print ("warning, track short: <$act_rule_track>\n$line\n");
- }
- if ($debuglevel >= 5){ print "ns_rules_initial: found new policy $act_rule_id\n"; }
-=cut
- if (!defined ($rulebases{"$rulebase_name.$act_rule_id.id"})) {
- if ((($debuglevel >= 4)&&($debuglevel<=10))||(($debuglevel >= 14)&&($debuglevel<=20))){
- print "sub ns_rules_initial RULE: $act_rule_id neu definiert.\n";
- }
- }
- elsif (defined ($rulebases{"$rulebase_name.$act_rule_id.id"})) {
- if ((($debuglevel >= 4)&&($debuglevel<=10))||(($debuglevel >= 14)&&($debuglevel<=20))){
- print "sub ns_rules_initial RULE: $act_rule_id ist bereits definiert.\n";
- }
- }
- else {
- if ((($debuglevel >= 4)&&($debuglevel<=10))||(($debuglevel >= 14)&&($debuglevel<=20))){
- print "sub ns_rules_initial RULE: $act_rule_id ist undefiniert.\n";
- }
- }
-=cut
- $rulebases{"$rulebase_name.$act_rule_id.id"} = $act_rule_id;
- $rulebases{"$rulebase_name.$act_rule_id.ruleid"} = $act_rule_ruleid;
- $rulebases{"$rulebase_name.$act_rule_id.order"} = $order;
- $rulebases{"$rulebase_name.$act_rule_id.disabled"} = '0';
- $rulebases{"$rulebase_name.$act_rule_id.src.zone"} = $act_rule_Szone;
- $rulebases{"$rulebase_name.$act_rule_id.src"} = $act_rule_src;
- $rulebases{"$rulebase_name.$act_rule_id.src.refs"} = $act_rule_src_refs;
- $rulebases{"$rulebase_name.$act_rule_id.dst.zone"} = $act_rule_Dzone;
- $rulebases{"$rulebase_name.$act_rule_id.dst"} = $act_rule_dst;
- $rulebases{"$rulebase_name.$act_rule_id.dst.refs"} = $act_rule_dst_refs;
- $rulebases{"$rulebase_name.$act_rule_id.services.op"} = '0';
- $rulebases{"$rulebase_name.$act_rule_id.src.op"} = '0';
- $rulebases{"$rulebase_name.$act_rule_id.dst.op"} = '0';
- $rulebases{"$rulebase_name.$act_rule_id.services"} = $act_rule_srv;
- $rulebases{"$rulebase_name.$act_rule_id.services.refs"} = $act_rule_srv;
- $rulebases{"$rulebase_name.$act_rule_id.action"} = $act_rule_act;
- $rulebases{"$rulebase_name.$act_rule_id.track"} = $act_rule_track;
- $rulebases{"$rulebase_name.$act_rule_id.install"} = ''; # set hostname verwenden ?
- $rulebases{"$rulebase_name.$act_rule_id.name"} = ''; # kein Aequivalent zu CP rule_name
- $rulebases{"$rulebase_name.$act_rule_id.time"} = '';
- $rulebases{"$rulebase_name.$act_rule_id.comments"} = $act_rule_name;
- $rulebases{"$rulebase_name.$act_rule_id.UID"} = $act_rule_id;
- $rulebases{"$rulebase_name.$act_rule_id.header_text"} = '';
-
-}
-
-#####################################
-# ns_rules_extended
-# param1: debuglevel [integer]
-# keine Hashuebergabe - zu fehleranfaellig bei meinem stil - arbeite mit globalem hash
-# Aufruf darf erst erfolgen, wenn ns_rules_initial alle Rules erfasst hat
-# Rules werden nur ergaenzt, fall sRule-id nicht existiert -> Fehlermeldung
-#####################################
-sub ns_rules_extended {
- my $debuglevel = $_[0];
- my $line = '';
- my $act_rule_id = -1;
- my $act_rule_src = '';
- my $act_rule_dst = '';
- my $act_rule_srv = '';
- my @params;
-
- foreach $line (@config_lines) {
- if ($debuglevel >= 9) { print $line; }
- @params = split_lines_to_parameter ($line,$debuglevel);
-
- if (($line =~/^set policy id \d+/) && ($#params == 3)) {
- $act_rule_id = $params[3]*1;
- } elsif (($line =~/^exit/) && ($act_rule_id > -1)) {
- $act_rule_id = -1;
- }
- if ($act_rule_id > -1) {
- if ($line=~ /^set service / && $#params == 2) {
-# if (!defined($rulebases{"$rulebase_name.$act_rule_id.services"})) { $rulebases{"$rulebase_name.$act_rule_id.services"} = ''; }
-# if (!defined($rulebases{"$rulebase_name.$act_rule_id.services.refs"})) { $rulebases{"$rulebase_name.$act_rule_id.services.refs"} = ''; }
- $act_rule_srv = $rulebases{"$rulebase_name.$act_rule_id.services"};
- $act_rule_srv = "$act_rule_srv|$params[2]";
- $rulebases{"$rulebase_name.$act_rule_id.services"} = $act_rule_srv;
- $rulebases{"$rulebase_name.$act_rule_id.services.refs"} = $act_rule_srv;
- }
- if ($line=~ /^set src-address/ && $#params == 2) {
- if ($line =~ /set src-address\s+\"MIP\(([\d\.]+)\/?(\d+)?\)\"/) {
- my $src_zone = $rulebases{"$rulebase_name.$act_rule_id.src.zone"};
- my $mip_ip = $1;
- my $netmask_bits = $2;
- my $netmask_string = '';
- my $is_ipv6 = ($mip_ip =~ /\:/);
- if ((!defined($netmask_bits) || $netmask_bits eq '') && $is_ipv6) { $netmask_bits = 128; }
- if ((!defined($netmask_bits) || $netmask_bits eq '') && !$is_ipv6) { $netmask_bits = 32; }
- my $netmask_dotted = &convert_mask_to_dot_notation($netmask_bits);
- if ($is_ipv6 && $netmask_bits<128) { $netmask_string = "/$netmask_bits"; }
- if (!$is_ipv6 && $netmask_bits<32) { $netmask_string = "/$netmask_bits"; }
- &ns_object_address_add("MIP($mip_ip$netmask_string)", "$mip_ip", $netmask_dotted, $src_zone, 'Virtual MIP, generated by fworch from interface MIP definition');
- }
- $act_rule_src = $rulebases{"$rulebase_name.$act_rule_id.src"};
- my $act_rule_src_refs = $rulebases{"$rulebase_name.$act_rule_id.src.refs"};
- my $zone = $rulebases{"$rulebase_name.$act_rule_id.src.zone"};
- $act_rule_src = "$act_rule_src|$params[2]";
- $act_rule_src_refs .= "|$params[2]__zone__$zone";
- $rulebases{"$rulebase_name.$act_rule_id.src"} = $act_rule_src;
- $rulebases{"$rulebase_name.$act_rule_id.src.refs"} = $act_rule_src_refs;
- # keine Konsistenzpruefung bzgl. Zone, da die Konsistenz der Konfig unter diesem Gesichtspunkt vorausgesetzt wird
- }
- if ($line=~ /^set dst-address/ && $#params == 2) {
- # Handling von dst MIP-IPs, muster: set dst-address "MIP(89.19.225.170)"
- if ($line =~ /set dst-address\s+\"MIP\(([\d\.]+)\/?(\d+)?\)\"/) {
- my $dst_zone = $rulebases{"$rulebase_name.$act_rule_id.dst.zone"};
- my $mip_ip = $1;
- my $netmask_bits = $2;
- my $netmask_string = '';
- if (!defined($netmask_bits) || $netmask_bits eq '') { $netmask_bits = 32; }
- my $netmask_dotted = &convert_mask_to_dot_notation($netmask_bits);
- if ($netmask_bits<32) { $netmask_string = "/$netmask_bits"; }
- &ns_object_address_add("MIP($mip_ip$netmask_string)", "$mip_ip", $netmask_dotted, $dst_zone, 'Virtual MIP, generated by fworch from interface MIP definition');
- }
- $act_rule_dst = $rulebases{"$rulebase_name.$act_rule_id.dst"};
- my $act_rule_dst_refs = $rulebases{"$rulebase_name.$act_rule_id.dst.refs"};
- my $zone = $rulebases{"$rulebase_name.$act_rule_id.dst.zone"};
- $act_rule_dst = "$act_rule_dst|$params[2]";
- $act_rule_dst_refs .= "|$params[2]__zone__$zone";
- $rulebases{"$rulebase_name.$act_rule_id.dst"} = $act_rule_dst;
- $rulebases{"$rulebase_name.$act_rule_id.dst.refs"} = $act_rule_dst_refs;
- # keine Konsistenzpruefung bzgl. Zone, da die Konsistenz der Konfig unter diesem Gesichtspunkt vorausgesetzt wird
- }
- }
- }
-}
-
-############################################################
-# ns_add_zone ($new_zone)
-############################################################
-sub ns_add_zone {
- my $new_zone = shift;
- my $debug_level = shift;
- my $is_there = 0;
- foreach my $elt (@zones) { if ($elt eq $new_zone) { $is_there = 1; last; } }
- if (!$is_there) {
- push @zones, $new_zone;
- if ($debug_level >= 2){ print "ns_add_zone: found new zone $new_zone\n"; }
- }
-}
-
-############################################################
-# read_predefined_services(
-############################################################
-sub read_predefined_services {
- my $device_type = shift;
- my $predefined_service_string = shift;
- my $predef_svc;
- my ($svc_name,$ip_proto,$port,$port_end,$timeout,$comment,$typ,$group_members);
-
- $predef_svc = exec_pgsql_cmd_return_value ("SELECT dev_typ_predef_svc FROM stm_dev_typ WHERE dev_typ_id=$device_type");
- my @predef_svc = split /\n/, $predef_svc;
- foreach my $svc_line (@predef_svc) {
- ($svc_name,$ip_proto,$port,$port_end,$timeout,$comment,$typ,$group_members) = split /;/, $svc_line;
- $services{"$svc_name.name"} = $svc_name;
- $services{"$svc_name.port"} = $port;
- $services{"$svc_name.port_last"} = $port_end;
- $services{"$svc_name.ip_proto"} = $ip_proto;
- $services{"$svc_name.timeout"} = $timeout;
- $services{"$svc_name.color"} = "black";
- $services{"$svc_name.comments"} = "$predefined_service_string, $comment";
- $services{"$svc_name.typ"} = $typ;
- $services{"$svc_name.type"} = "";
- $services{"$svc_name.rpc_port"} = "";
- $services{"$svc_name.UID"} = $svc_name;
- $services{"$svc_name.members"} = $group_members;
- $services{"$svc_name.member_refs"} = $group_members;
- push @services, $svc_name;
- }
- return;
-}
-############################################################
-# copy_config_from_mgm_to_iso($ssh_private_key, $ssh_user, $ssh_hostname, $management_name,
-# $obj_file_base, $cfg_dir, $rule_file_base)
-# Kopieren der Config-Daten vom Management-System zum ITSecorg-Server
-############################################################
-
-sub copy_config_from_mgm_to_iso {
- my $ssh_user = shift;
- my $ssh_hostname = shift;
- my $management_name = shift;
- my $obj_file_base = shift;
- my $cfg_dir = shift;
- my $rule_file_base = shift;
- my $workdir = shift;
- my $debug_level = shift;
- my $cmd;
- my $fehler_count = 0;
- my $result;
-
-# the scp command has to be used for tests with non-netscreen devices
-# $cmd = "$scp_bin $scp_batch_mode_switch -i $workdir/$CACTUS::FWORCH::ssh_id_basename $ssh_user\@$ssh_hostname:ns_sys_config $cfg_dir/$obj_file_base";
- $cmd = "$ssh_client_screenos -z $ssh_hostname -t netscreen -i $workdir/$CACTUS::FWORCH::ssh_id_basename -c 'get config' -u $ssh_user -d 0 -o $cfg_dir/$obj_file_base";
- if (system ($cmd)) { $fehler_count++; }
- return ($fehler_count, "$cfg_dir/$obj_file_base" );
-}
-
-sub sort_rules_and_add_zone_headers {
- my $anzahl_regeln = $rulebases{"$rulebase_name.rulecount"};
- my $count;
- my $zone_string;
- my @rule_zones = ();
-
- # Nachbereitung Regeln: Sortierung nach a) Zonen b) $ruleorder
-
- for ($count=0; $count<$anzahl_regeln; $count++) {
- $zone_string = $rulebases{"$rulebase_name.$ruleorder[$count].src.zone"};
- $zone_string .= " : ";
- $zone_string .= $rulebases{"$rulebase_name.$ruleorder[$count].dst.zone"};
- push @rule_zones, $zone_string
- }
- my @idx = ();
- my $item;
- for (@rule_zones) {
- ($item) = $_;
- push @idx, $item;
- }
-
- @ruleorder = @ruleorder[ sort { $idx[$a] cmp $idx[$b] } 0 .. $anzahl_regeln-1 ];
- @rule_zones = @rule_zones[ sort { $idx[$a] cmp $idx[$b] } 0 .. $anzahl_regeln-1 ];
-
- # Nachbereitung Regeln: Header-Regeln vor Zonenwechsel einfuegen
- my $new_zone_string;
- my $old_zone_string = "";
- my $rule_header_count = 1;
- my $rule_header_offset = &CACTUS::read_config::read_config('rule_header_offset') * 1;
- my $new_rule_id;
- for ($count = 0; $count < $anzahl_regeln; $count++) {
- $new_zone_string = $rule_zones[$count];
- if ($new_zone_string ne $old_zone_string) { # insert header rule
- $new_rule_id = $rule_header_offset+$rule_header_count++;
- (my $src_zone, my $dst_zone) = split / : /, $new_zone_string;
- splice(@ruleorder,$count,0,$new_rule_id); # fuegt neue Regel ein
- splice(@rule_zones,$count,0,$new_zone_string);
- $anzahl_regeln++;
- $rulebases{"$rulebase_name.rulecount"} = $anzahl_regeln;
- $rulebases{"$rulebase_name.$ruleorder[$count].id"} = $new_rule_id;
- $rulebases{"$rulebase_name.$ruleorder[$count].header_text"} = $new_zone_string;
- $rulebases{"$rulebase_name.$ruleorder[$count].UID"} = $new_rule_id;
- $rulebases{"$rulebase_name.$ruleorder[$count].src"} = "Any";
- $rulebases{"$rulebase_name.$ruleorder[$count].dst"} = "Any";
- $rulebases{"$rulebase_name.$ruleorder[$count].services"} = "ANY";
- $rulebases{"$rulebase_name.$ruleorder[$count].action"} = "deny";
- $rulebases{"$rulebase_name.$ruleorder[$count].src.zone"} = $src_zone;
- $rulebases{"$rulebase_name.$ruleorder[$count].dst.zone"} = $dst_zone;
- $rulebases{"$rulebase_name.$ruleorder[$count].disabled"} = '0';
- $rulebases{"$rulebase_name.$ruleorder[$count].src.op"} = '0';
- $rulebases{"$rulebase_name.$ruleorder[$count].dst.op"} = '0';
- $rulebases{"$rulebase_name.$ruleorder[$count].services.op"} = '0';
- }
- $old_zone_string = $new_zone_string;
- }
-}
-
-
-# check auf Vollstaendigkeit des Config-Files:
-sub is_cfg_file_complete {
- my $ln_cnt = $#config_lines;
- my $cfg_file_complete = 1;
- while ($config_lines[$ln_cnt] =~ /^\s*$/ ) { $ln_cnt -- ; } # ignore empty lines at the end
- if ($config_lines[$ln_cnt] !~ /^.*set multicast\-group\-policy/) {
- if ( $config_lines[$ln_cnt] =~ /^exit.?$/ || $config_lines[$ln_cnt] =~ /^set zone \"?.+?\"? ip-classification /) {
- # last non-empty line must either be "exit" or multicast-related
- $ln_cnt -- ;
- if ( $config_lines[$ln_cnt] !~ /^(un)?set .*?route.*?$/ && $config_lines[$ln_cnt] !~ /^set zone \"?.+?\"? ip-classification /) {
- # assuming that last config part deals with routing
- $cfg_file_complete = 0;
- print ("expected last line to deal with routing. Instead got: " . $config_lines[$ln_cnt] . "\n");
- }
- } else {
- $cfg_file_complete = 0;
- print ("expected last line to either be 'exit' or mutlicast-related. Instead got: " . $config_lines[$ln_cnt] . "\n");
- }
- }
- return $cfg_file_complete;
-}
-
-sub parse_users { # not implemented yet
- my $line = shift;
- if ( $line=~ /^set user.*/ ) {
- # print "found user: $line\n";
- # noch auszuprogrammieren
- # set user "oe560" uid 1
- # set user "oe560" type auth
- # set user "oe560" hash-password "asdf askdfaslkdfjalsjfd"
- # set user "oe560" "enable"
- }
-}
-
-#####################################################################################
-# MAIN
-
-sub parse_config {
- # ($obj_file, $rule_file, $rulebases, $fworch_workdir, $debug_level)
- my $in_file_main = shift;
- shift;
- shift; # $rule_file und $user nicht verwendet
- my $dev_info_hash = shift; # fuer netscreen gibt es pro management immer nur genau ein device
- my $fworch_workdir = shift;
- my $debuglevel_main = shift;
- my $mgm_name = shift;
- my $config_dir = shift;
- my $import_id = shift;
- my $debug_level = shift;
- my $line = '';
- my $ln_cnt = $#config_lines;
- my @vsys_lines = ();
- my $vsys_started = 0;
- my $dev_name = 'undefined_dev_name';
-
- # Initializing
-
- @services = ();
- @network_objects = ();
- my $device_type=3; # netscreen v5.1 fest gesetzt, TODO: move to config
- my $predefined_service_string = "netscreen_predefined_service"; # move to config
-
- if ($debuglevel_main >= 2){ print "in_file_main:$in_file_main!\ndebuglevel_main:$debuglevel_main!\n"; }
- &read_predefined_services($device_type, $predefined_service_string); # schreibt die predefined services in @services und %services
-
- open (IN, $in_file_main) || die "$in_file_main konnte nicht geoeffnet werden.\n";
- @config_lines = ;
- close (IN);
-
- &remove_unwanted_chars ($debuglevel_main);
-
- if (&ns_mode_check($debuglevel_main)) { return "unknown-netscreen-config-file-mode-$mgm_name"; }
-
- if (!&is_cfg_file_complete()) { return "incomplete-config-file-$mgm_name"; }
- else {
- foreach $line (@config_lines) { # extract all zone information (global zones can be used in vsys config)
- if ( $line =~ /^set zone id \d+ "(.+)"/ || $line =~ /^set zone "(.+)" vrouter / || $line =~ /^set interface ".+" zone "(.+)"/ ) {
- &ns_add_zone ($1, $debuglevel_main);
- }
- }
- while ( (my $key, my $value) = each %{$dev_info_hash} ) { $dev_name = $value->{'dev_name'}; } # nur ein device pro netscreen management
-# print ("searching for vsys with dev_name $dev_name, mgm_name=$mgm_name\n");
- @rulebases = ($dev_name); $rulebase_name = $dev_name;
- if ($dev_name ne '') { # vsys config erwartet
- foreach $line (@config_lines) {
- if ($line =~ /^set vsys \"?$dev_name\"? /i) { # start of right vsys definition
- $vsys_started = 1;
- } elsif ($line =~ /^set vsys \"?/) { $vsys_started = 0; }
- elsif ($vsys_started) {
- @vsys_lines = (@vsys_lines, $line);
- }
- }
- if ($#vsys_lines>0) { @config_lines = @vsys_lines; } # only copy vsys config if correct vsys has been found
- }
- LINE: foreach $line (@config_lines) {
- if ($debuglevel_main >= 9) { print "main debug line: $line"; }
- if ($line=~ /^set hostname ([\w\.\_\-]+)/) { $mgm_name = $1; @rulebases = ($mgm_name); $rulebase_name = $mgm_name; } # wird auch in ns_rules_initial verwendet
- $line=~ (/^set address/) && do {&ns_object_address ($line,$debuglevel_main);};
- $line=~ (/^set group address/) && do {&ns_object_group_address ($line,$debuglevel_main);};
- if ($line=~ /^set service.+?protocol.*/ || $line=~ /^set service.+? \+ .*/) { ns_object_service ($line, $debuglevel_main); }
- if ($line=~ /^set group service.*/) { ns_object_group_service ($line,$debuglevel_main); }
-########### Start Regelparser
- if ($line=~ /^set policy id \d+ (application|attack)/ ) { next LINE; } # ignore it
- if ($line=~ /^set policy id (\d+) (name|from)/ ) { # Standardfall: eine Regeldefinition beginnt
- if ($debuglevel_main >= 5){ print "parse_config: found new policy $1\n"; }
- &ns_rules_initial ($line,$rule_order,$debuglevel_main);
- $rule_order ++;
- }
- if ( $line=~ /^set policy id (\d+) disable.*/ ) { # Regel deaktiviert
- if (defined($rulebases{"$rulebase_name.$1.id"})) { $rulebases{"$rulebase_name.$1.disabled"} = '1'; }
- else { print ("Fehler: noch nicht definierte Regel disabled: $line\n"); }
- }
-########### Ende Regelparser
- &parse_users($line);
- }
- &ns_rules_extended ($debuglevel_main); # file noch einmal durchgehen und diesmal nach Ergaenzungen der Regeln mit svc, src oder dst suchen
- # Any-Objekte fuer alle Zonen einfuegen
- push @zones, "Global"; # Global Zone immer hinzufuegen
- foreach my $zone (@zones) {
- &ns_object_address_add("Any", "0.0.0.0", "0.0.0.0", $zone, "Any-Obj for Zone added by fworch");
- &ns_object_address_add("Any-IPv4", "0.0.0.0", "0.0.0.0", $zone, "Any-IPv4-Obj for Zone added by fworch");
- &ns_object_address_add("Any-IPv6", "::", "0", $zone, "Any-IPv6-Obj for Zone added by fworch");
- }
- &sort_rules_and_add_zone_headers ();
- # neu: Wegschreiben der Regelreihenfolge
- $rulebases{"$rulebase_name.ruleorder"} = join(',', @ruleorder);
-# print_results_monitor('objects');
-# print_results_monitor('rules');
- print_results_files_objects($fworch_workdir, $mgm_name, $import_id);
- print_results_files_rules ($fworch_workdir, $mgm_name, $import_id);
- print_results_files_zones ($fworch_workdir, $mgm_name, $import_id);
- return 0;
- }
-}
-
-1;
-__END__
-
-=head1 NAME
-
-CACTUS::FWORCH::parser - Perl extension for fworch netscreen parser
-
-=head1 SYNOPSIS
- use CACTUS::FWORCH::import::netscreen;
-
-=head1 DESCRIPTION
-
-fworch Perl Module support for importing configs into fworch Database
-
-=head2 EXPORT
-
- global variables
-
-=head1 SEE ALSO
-
- behind the door
-
-=head1 AUTHOR
-
- Cactus eSecurity, tmp@cactus.de
-
-=cut
diff --git a/roles/importer/files/importer/CACTUS/FWORCH/import/phion.pm b/roles/importer/files/importer/CACTUS/FWORCH/import/phion.pm
deleted file mode 100644
index b043b0b10c..0000000000
--- a/roles/importer/files/importer/CACTUS/FWORCH/import/phion.pm
+++ /dev/null
@@ -1,1796 +0,0 @@
-
-package CACTUS::FWORCH::import::parser;
-
-use strict;
-use warnings;
-use IO::File;
-use File::Find;
-
-use CACTUS::FWORCH;
-use CACTUS::FWORCH::import;
-use Date::Calc qw(Add_Delta_DHMS Delta_DHMS);
-
-require Exporter;
-our @ISA = qw(Exporter);
-
-our %EXPORT_TAGS = ( 'basic' => [ qw( ©_config_from_mgm_to_iso &parse_config ) ] );
-
-our @EXPORT = ( @{ $EXPORT_TAGS{'basic'} } );
-
-# variblendefinition parser
-# -------------------------------------------------------------------------------------------
-
-our $fwobj_file_extension = '.fwobj';
-our $fwobj_file_pattern = '\*\.fwobj';
-
-our $forwarding_rule_file_extension = '.fwrule';
-our $forwarding_rule_file_pattern = '\*\.fwrule';
-
-our $rev_marker = ',v'; # RCS files
-our $rev_fwobj_file_extension = '.fwobj,v';
-our $rev_fwobj_file_pattern = '\*\.fwobj\,v';
-
-our $rev_rule_file_extension = '.fwrule,v';
-our $rev_rule_file_pattern = '\*\.fwrule\,v';
-
-our $debug_level = 0;
-
-# our $local_rule_file_extension = '.lfwrule7';
-# our $local_rule_file_pattern = '\*\.lfwrule7';
-
-our $GROUPSEP = "|"; # globale Einstellung fuer Seperator in Gruppen;
-our $config_path_praefix;
-our $last_import_time;
-our $last_change_admin;
-# our @UTC_diff_identifier = ();
-our $UTC_diff;
-our $scope;
-
-# Stati und anderes des Objektparsers
-# -------------------------------------------------------------------------------------------
-our $parse_obj_state = 0; # moegliche Werte 0 kein status
- # 1 objectclass gestartet
- # 2 object gestartet
- # 3...5 diverse attribute & werte (kontextsensitiv)
-our $parse_obj_type; # State 1 - aktuelle Objektklasse
-our $parse_obj_id; # State 2 - id des aktuellen objektes ( name__uid__uid )
-our $parse_obj_name; # State 2 - name des aktuellen objektes
-our $parse_obj_attr; # State 3 - Attribut
-our $parse_obj_attr_value; # State 3 - Wert des Attributes
-our $parse_obj_attr_ext; # State 4 - Attributerweiterung (kontextsensitiv)
-our $parse_obj_attr_ext_value; # State 4 - Wert des erweiterten Attributes (kontextsensitiv)
-our $parse_obj_neglist; # Flag fuer negierte Liste
-our $collecting_group_members = 0; # flag
-our $explicit_flag = 0;
-our $create_explicit_element_flag = 0;
-our $create_explicit_element_proto;
-our $member_counter = 0; # zaehlt #Gruppenmitglieder
-our $convert_simple_to_group_flag = 0; # Flag, um bei Bedarf ein als simple angefangenes Objekt zur Gruppe zu machen
-our $proto_of_explicit_svc_group;
-
-# rule specific
-our $parserule_rulenum;
-our $parse_rule_field_details;
-our $parse_rule_field;
-our $config_files_str;
-
-our $sublist_flag = 0;
-our $rulelist_flag = 0;
-our $rulelist_name = '';
-our $indent = '';
-our @subrulebases = (); # local for checking on subrule lists
-
-## parse_audit_log Funktion fuer phion noch nicht implementiert
-sub parse_audit_log { }
-
-############################################################
-# copy_config_from_mgm_to_iso($ssh_private_key, $ssh_user, $ssh_hostname, $management_name, $obj_file_base, $cfg_dir, $rule_file_base)
-# Kopieren der Config-Daten vom Management-System zum ITSecorg-Server
-############################################################
-sub copy_config_from_mgm_to_iso {
- my $ssh_user = shift;
- my $ssh_hostname = shift;
- my $management_name = shift;
- my $obj_file_base = shift;
- my $cfg_dir = shift;
- my $rule_file_base = shift;
- my $workdir = shift;
- my $auditlog = shift; # unused in phion
- my $prev_import_time= shift; # unused in phion
- my $ssh_port = shift;
- my $config_path_on_mgmt = shift; # unused in phion
- my $debug_level = shift;
-
- my $cmd;
- my $return_code;
- my $fehler_count = 0;
-
- my $tar_archive = 'iso-phion-config.tgz';
-
- my $tar_cmd = 'tar cfz '.$tar_archive.' \`find . -type f ' .
- '-name "' . $fwobj_file_pattern . '" ' .
- '-o -name "' . $forwarding_rule_file_pattern . '" ' .
- '-o -name "' . $rev_fwobj_file_pattern . '" ' .
- '-o -name "' . $rev_rule_file_pattern . '" ' .
- ' \`';
-
- if (!defined($ssh_port) || $ssh_port eq '') {
- $ssh_port = "22";
- }
-
- $cmd = "$ssh_bin -i $workdir/$CACTUS::FWORCH::ssh_id_basename -p $ssh_port $ssh_user\@$ssh_hostname $tar_cmd";
- $fehler_count += (system ($cmd) != 0);
-
- $cmd = "$scp_bin -i $workdir/$CACTUS::FWORCH::ssh_id_basename -P $ssh_port $ssh_user\@$ssh_hostname:$tar_archive $cfg_dir";
- $fehler_count += (system ($cmd) != 0);
-
- $cmd = "$ssh_bin -i $workdir/$CACTUS::FWORCH::ssh_id_basename -p $ssh_port $ssh_user\@$ssh_hostname rm $tar_archive";
- $fehler_count += (system ($cmd) != 0);
-
- $cmd = "cd $cfg_dir; tar xfz $tar_archive; rm $tar_archive";
- $fehler_count += (system ($cmd) != 0);
-
- find(\&collect_config_files, $cfg_dir); # erzeugt einen String mit allen gefundenen Config-Files
- $UTC_diff = &get_UTC_diff_unix($ssh_user, $ssh_hostname, $ssh_port, "$workdir/$CACTUS::FWORCH::ssh_id_basename"); # hole UTC-Verschiebung mittels date-Befehl (globale Var setzen)
- return ($fehler_count,$config_files_str);
-}
-
-############################################################
-# get_UTC_diff_unix($ssh_private_key, $ssh_user, $ssh_hostname, $management_name, $work_dir)
-# liefert die Verschiebung in Stunden zu UTC
-############################################################
-sub get_UTC_diff_unix {
- my $ssh_user = shift;
- my $ssh_hostname = shift;
- my $ssh_port = shift;
- my $ssh_priv_key_file = shift;
-
- return (`$ssh_bin -i $ssh_priv_key_file -p $ssh_port $ssh_user\@$ssh_hostname date +\"%z\"` / 100); # Minuten "wegdividieren" (%z Format: +0200)
-}
-
-sub collect_config_files {
- if ( -f $File::Find::name && $File::Find::name !~ /$rev_marker/ ) {
- if (defined($config_files_str)) {
- $config_files_str .= ("," . $File::Find::name);
- } else {
- $config_files_str = $File::Find::name;
- }
- }
-}
-
-
-#####################################################################################
-# Start Parser
-#####################################################################################
-
-sub parse_config {
- shift; # ignore filename
- my $rulebase_file = shift;
- my $user_db_file = shift;
- my $rulebase_name = shift;
- my $output_dir = shift;
- my $debug = shift;
- my $mgm_name = shift;
- my $cfg_dir = shift;
- my $import_id = shift;
- my $audit_log_file= shift;
- my $last_import_time_local = shift;
- my $debug_level = shift;
-
- $debug_level = $debug;
- # setting global vars as find cannot take any parameters
- $config_path_praefix = $cfg_dir;
- $last_import_time = $last_import_time_local;
-
- # parse all files and fill structures (hashes, arrays)
- find(\&process_basic_object_files, $cfg_dir);
-# exit 1;
- find(\&process_forwarding_rule_files, $cfg_dir);
-# find(\&process_local_rule_files,$cfg_dir);
- &fix_bidir_rules();
- &add_basic_object_oids_in_rules_for_locally_defined_objects();
- &fix_rule_references();
- &link_subset_rules();
- §ion_titles_correction();
-
- # write structures to file (debug: and screen)
-# print_results_monitor('objects');
-# print_results_monitor('rules');
- &print_results_files_objects($output_dir, $mgm_name, $import_id);
-# &print_results_files_users($output_dir, $mgm_name, $import_id);
- &print_results_files_rules($output_dir, $mgm_name, $import_id);
- return 0; # done without errors
-}
-
-sub process_basic_object_files {
- if ($File::Find::name =~ /^$config_path_praefix\/(.*?)$/) {
- $scope = $1;
-# $flat_file =~ s/\//\_/g;
-# $scope = $flat_file;
- if ($scope =~ /FactoryDefault/ ||
- $scope =~ /^Revision/ ||
- $scope =~ /^zrepo/ ) { # alle FactoryDefaults, Revisions und Revisions - ignorieren
- if ($scope !~ /^Revision/) { print_debug ("ignoring basic element config file $scope ", $debug_level, 5); }
- return;
- } elsif ( -f $File::Find::name || -l $File::Find::name ) {
- if ( -e $File::Find::name ) {
- print_debug ("fwobj parser: processing file " . $File::Find::name, $debug_level, 1);
-# if ($File::Find::name =~ /^$config_path_praefix\/(.*?)$/) { $scope = $1; }
- &get_change_admin($File::Find::name, $last_import_time);
-# output_txt ("change_admin: $change_admin");
- &parse_basic_elements ($File::Find::name);
- } else {
- output_txt ("ERROR: fworch-importer: phion.pm: cannot access file $File::Find::name\n", 2);
- }
- }
- }
-}
-
-sub process_rule_files {
- my $extension = $_[0];
-
- if ($File::Find::name =~ /$extension$/) {
- if ($File::Find::name =~ /^$config_path_praefix\/(.*?)$/) {
- $scope = $1;
- $scope =~ s/\//\_/g;
-# $scope = $flat_file;
- }
- if ($scope =~ /FactoryDefault/ ||
- $scope =~ /^Revision/ ||
- $scope =~ /^zrepo/ ) { # alle FactoryDefaults, Revisions und Revisions - ignorieren
- if ($scope !~ /^Revision/) { print_debug ("ignoring ruleset file $scope", $debug_level, 3); }
- return;
- } elsif ( -f $File::Find::name || -l $File::Find::name ) {
- if ( -e $File::Find::name ) {
- print_debug ("rule parser: processing file " . $File::Find::name, $debug_level, 1);
- @rulebases = (@rulebases, $scope);
- $parserule_rulenum = 0;
- &get_change_admin($File::Find::name, $last_import_time);
-# output_txt ("change_admin: $change_admin");
- &parse_rules ($File::Find::name);
- } else {
- output_txt ("ERROR: fworch-importer: phion.pm: cannot access file $File::Find::name\n", 2);
- }
- }
- }
-}
-
-sub conv_time_UTC {
- my $UTC_diff = shift;
- my $year = shift;
- my $month = shift;
- my $day = shift;
- my $hour = shift;
- my $min = shift;
- my $sec= shift;
-
- ($year, $month, $day, $hour, $min, $sec) = Add_Delta_DHMS( $year, $month, $day, $hour, $min, $sec, 0, $UTC_diff, 0, 0 );
- return sprintf("%04d-%02d-%02d %02d:%02d:%02d", $year, $month, $day, $hour, $min, $sec);
-}
-
-sub get_change_admin { # sets name of last_change admin for current config file in $last_change_admin global var ("" if none)
- my $cfg_file = $_[0];
- my $last_ch_time = $_[1];
- my $rev_file;
- my $line;
- my %change;
- my @change_arr = ();
- my $number_of_changes_found = 0;
- my $result_lc_admin = "";
- my $fehler = 0;
-
- if ($cfg_file =~ /^$config_path_praefix\/(.*)\/([a-zA-Z0-9\_\-\.]+)$/) {
- my $path_to_file = $1;
- my $basename = $2;
- $rev_file = "$config_path_praefix/Revision/$path_to_file/RCS/$basename,v";
- open (IN, $rev_file) or $fehler = "Cannot open file $rev_file for reading: $!";
- if ($fehler) {
- print ("WARNING: $fehler\n");
- close (IN);
- } else {
- PARSE_LOOP: while ( ) {
- $line = $_; # Zeileninhalt merken
- if ( $line =~ /^date\s+(\d\d\d\d)\.(\d\d)\.(\d\d)\.(\d\d)\.(\d\d)\.(\d\d)\;\s+author\s+(\w+?)\_/ ) {
- my $year = $1; my $month = $2; my $day = $3; my $hour = $4; my $min = $5; my $sec = $6;
- my $sql_time = &conv_time_UTC ( $UTC_diff, $year, $month, $day, $hour, $min, $sec );
- if ($sql_time gt $last_ch_time) { # change time lies behind last import time - so proceed
- my $local_last_change_admin = $7;
- $change{"$sql_time.admin"} = $local_last_change_admin;
- @change_arr = (@change_arr, $sql_time);
- $number_of_changes_found ++;
- } else { last PARSE_LOOP; }
- }
- if ( $line =~ /^next\s+\;$/ ) { last PARSE_LOOP; }
- } # end of while
- close (IN);
- if ($number_of_changes_found > 0) {
- my %seen = (); # only helper variable for unique operation below
- my @list_of_admins = values %change;
- my @unique_list_of_admins = grep {! $seen{$_}++} @list_of_admins;
- my $no_of_admins = $#unique_list_of_admins + 1;
- if ($no_of_admins == 1) {
- my $time = $change_arr[0];
- $result_lc_admin = $change{"$time.admin"};
- } else {
- output_txt ("warning: found $no_of_admins (>1) change-admin in $cfg_file since last import: " . join (':', @unique_list_of_admins), 2);
- }
- }
- }
- $last_change_admin = $result_lc_admin;
- }
- return;
-}
-
-sub process_forwarding_rule_files { &process_rule_files($forwarding_rule_file_extension); }
-# sub process_local_rule_files { &process_rule_files($local_rule_file_extension); }
-
-
-sub normalize_phion_proto {
- my $proto_str_in = shift;
- my $typ = '';
- my $proto = '';
-
- if (($proto_str_in eq 'echo')) { $typ = 'simple'; $proto = 1;}
- elsif ($proto_str_in eq 'tcp') { $typ = 'simple'; $proto = 6;}
- elsif (($proto_str_in eq 'udp')) { $typ = 'simple'; $proto = 17;}
- elsif (($proto_str_in eq 'group')) { $typ = 'group'; }
-# elsif (($proto_str_in eq 'other')) { # from ServiceEntryOther: wait for proto one level below
- return ($typ, $proto);
-}
-
-sub normalize_phion_ports {
- my $port_in = shift;
- my $port = '';
- my $port_last = '';
-
- if ($port_in =~ /^\s(.*?)$/) { $port_in = $1; } # remove space at beginning
- if ($port_in =~ /^(\d+)\-(\d+)$/) {
- $port = $1; $port_last = $2;
- } elsif ($port_in =~ /^(\d+)\s(\d+)$/) { # TODO: das ist jetzt "very dirty", weil es eigentlich nur zwei Ports sind und nicht alle Ports von $1 bis $2
- $port = $1; $port_last = $2;
- } elsif ($port_in =~ /^\*$/) {
- $port = 0; $port_last = 65535;
- } else {
- $port = $port_in;
- }
- return ($port,$port_last);
-}
-
-sub analyze_phion_ip {
- my $addr_in = shift;
- my ($addr, $addr_last, $type);
- if (!defined($addr_in)) {
- return (undef,undef,undef);
- }
- my ($net,$mask);
- ($net,$mask) = split /\//, $addr_in;
-# print_debug ("analyze_phion_ip: start addr_in: '$addr_in'", $debug_level, 0);
- if (defined ($mask)) {
- my $max_mask;
- if ($net =~ /\:/) {
- $max_mask = 128;
- } else {
- $max_mask = 32;
- }
- if ($mask =~ /\d+/) {
- $mask = $max_mask - $mask; # umrechnen von verdrehter phion maske auf normale maske
- if ($mask == 2 || $mask==3) { print_debug("warning mask=2 or 3: $net/$mask", $debug_level, 3); }
- $addr = "$net/$mask";
- $type = 'network';
- } else {
- print_debug("non numeric mask found in ip $addr_in: $mask", $debug_level, 0);
- return (undef,undef,undef);
- }
- } elsif ($addr_in =~ /(\d+\.\d+\.\d+\.)(\d+)\-([\d\.]+)/) {
- $addr = "$1$2";
- $addr_last = $3;
- if ($addr_last !~ /\./) { # second ip contains only last byte
- $addr_last = "$1$3";
- }
- $type = 'host'; # keep range type for later implementation
- } elsif ($addr_in =~ /[\d\.\:a-f]+/) {
- $addr = $addr_in;
- $addr_last = undef;
- $net = undef;
- $type = 'host';
- } else {
- print_debug ("analyze_phion_ip: found invalid ip address \'$addr_in\'", $debug_level, 0);
- return (undef,undef,undef);
- }
-# print_debug ("analyze_phion_ip: end addr: '$addr', type='$type'", $debug_level, 0);
- return ($type,$addr,$addr_last);
-}
-
-#****************************
-# Verarbeitung / Aufbereitung der identifizierten Parameter und Values (kontextsensitiv)
-#****************************
-# store_results
-sub store_results {
- if (!defined($parse_obj_type)) {
- print_debug ("error store_results: parse_obj_type not defined", $debug_level, 0);
- return;
- }
- if ($parse_obj_type ne 'rules') {
- if (!defined($parse_obj_id)) {
- print_debug ("error: parse_obj_id not defined; type: " . defined($parse_obj_type)?$parse_obj_type:'undefined' . ", name: " .
- defined($parse_obj_name)?$parse_obj_name:'undefined' . ", attr: " . defined($parse_obj_attr)?$parse_obj_attr:'undefined' .
- ", value: " . defined($parse_obj_attr_value)?$parse_obj_attr_value:'undefined', $debug_level, 0);
- return;
- }
- if ($parse_obj_state < 3) {
- print_debug("Warnung: store_results mit obj_state<3 aufgerufen", $debug_level, 1);
- return;
- }
- }
- my $debug_string = "name: " . (defined($parse_obj_name)? $parse_obj_name : 'undefined') . ", attr: " .
- (defined($parse_obj_attr)? $parse_obj_attr : 'undefined') . ", value: " . (defined($parse_obj_attr_value)? $parse_obj_attr_value : 'undefined');
- print_debug ($debug_string, $debug_level, 8);
- if ($parse_obj_type eq "netobj" || $parse_obj_type eq "connobj") { &store_results_nw_objects(); }
- elsif ($parse_obj_type eq "srvobj") { &store_results_nw_services(); }
- elsif ($parse_obj_type eq "rules") { &store_results_rules(); }
- else {
- print_debug("store_results: found unknown parse_obj_type=$parse_obj_type",$debug_level,2);
- }
-}
-
-sub store_results_nw_objects {
- if (!defined ($network_objects{"$parse_obj_id.name"})) { # schon ein bekanntes network_object?
- @network_objects = (@network_objects, $parse_obj_id);
- $network_objects{"$parse_obj_id.name"} = $parse_obj_name;
- $network_objects{"$parse_obj_id.scope"} = $scope;
- $network_objects{"$parse_obj_id.typ"} = 'simple'; # setting default values (for NetESet objects)
- $network_objects{"$parse_obj_id.type"} = 'host';
- $network_objects{"$parse_obj_id.UID"} = $parse_obj_id; # default Wert nur wichtig fuer explizite Objekte
- $network_objects{"$parse_obj_id.last_change_admin"} = $last_change_admin;
- }
- if ($convert_simple_to_group_flag) {
- # altes Objekt in Gruppe umwandeln
- $network_objects{"$parse_obj_id.typ"} = 'group';
- $network_objects{"$parse_obj_id.type"} = 'group';
- # zwischenspeichern der IP des ersten Elements
- my $addr = '';
- if (defined($network_objects{"$parse_obj_id.ipaddr"})) { # Speichern der Adresse des ersten Elements
- $addr = $network_objects{"$parse_obj_id.ipaddr"};
- $network_objects{"$parse_obj_id.ipaddr"} = '';
- }
-
- # Anlegen eines neuen Objekts fuer das erste Element
- my $obj_mbr_id = $parse_obj_id . '__' . $addr;
- @network_objects = (@network_objects, $obj_mbr_id);
- $network_objects{"$obj_mbr_id.name"} = $parse_obj_name . '__' . $addr;
- $network_objects{"$obj_mbr_id.scope"} = $scope;
- $network_objects{"$obj_mbr_id.typ"} = 'simple';
- $network_objects{"$obj_mbr_id.UID"} = $parse_obj_id . '__' . $addr;
- $network_objects{"$obj_mbr_id.last_change_admin"} = $last_change_admin;
- if ($addr =~ /\/32/ || $addr =~ /\.\d+$/ || $addr =~ /\:[\da-f]+$/i || $addr =~ /\/128/) {
- print_debug ("1 rewriting type to host '$addr', name=" . $network_objects{"$obj_mbr_id.name"} . ", orig type=" . $network_objects{"$parse_obj_id.type"}, $debug_level, 7);
- $network_objects{"$obj_mbr_id.type"} = 'host';
- } elsif ($addr eq '') {
- print_debug ("2 rewriting type to group '$addr', name=" . $network_objects{"$obj_mbr_id.name"} . ", orig type=" . $network_objects{"$parse_obj_id.type"}, $debug_level, 7);
- $network_objects{"$obj_mbr_id.type"} = 'group';
- } elsif ($addr =~ /\/\d+$/) {
- print_debug ("3 rewriting type to network '$addr', name=" . $network_objects{"$obj_mbr_id.name"} . ", orig type=" . $network_objects{"$parse_obj_id.type"}, $debug_level, 7);
- $network_objects{"$obj_mbr_id.type"} = 'network';
- } else {
- print_debug ("4 leaving type as was '$addr', name=" . $network_objects{"$obj_mbr_id.name"} . ", orig type=" . $network_objects{"$parse_obj_id.type"}, $debug_level, 2);
- $network_objects{"$obj_mbr_id.type"} = $network_objects{"$parse_obj_id.type"}; ### old line
- }
- $network_objects{"$obj_mbr_id.ipaddr"} = $addr;
- $network_objects{"$obj_mbr_id.ipaddr_last"} = $network_objects{"$parse_obj_id.ipaddr_last"};
- $network_objects{"$parse_obj_id.members"} = $parse_obj_name . '__' . $addr; # jetzt das erste Element als einzigen Member hinzufuegen
- $network_objects{"$parse_obj_id.member_refs"} = $obj_mbr_id;
-
- $convert_simple_to_group_flag = 0;
- } # das zweite Element wird anschliessend (bei addr-Zeile in state 5) normal als member hinzugefuegt
- # Daten im Hash ergaenzen
- elsif ($collecting_group_members) {
- if ($parse_obj_attr eq 'ref') {
- if (defined($network_objects{"$parse_obj_id.members"})) {
- $network_objects{"$parse_obj_id.members"} .= ( $GROUPSEP . $parse_obj_attr_value );
- } else {
- $network_objects{"$parse_obj_id.members"} = $parse_obj_attr_value;
- $network_objects{"$parse_obj_id.typ"} = 'group';
- $network_objects{"$parse_obj_id.type"} = 'group';
- }
- } elsif ($parse_obj_attr eq 'refid') {
- if (defined($network_objects{"$parse_obj_id.member_refs"})) {
- $network_objects{"$parse_obj_id.member_refs"} .= ( $GROUPSEP . $parse_obj_attr_value );
- } else {
- $network_objects{"$parse_obj_id.member_refs"} = $parse_obj_attr_value;
- }
- } elsif (($parse_obj_attr eq 'addr' || $parse_obj_attr eq 'addr6') && $parse_obj_attr_value ne '0.0.0.0') { # explizite Gruppe
- # create new object
- my ($tmp_addr, $tmp_addr_last, $tmp_type);
- my $parse_obj_member_name;
- my $parse_obj_member_id;
-
- ($tmp_type,$tmp_addr,$tmp_addr_last) = analyze_phion_ip($parse_obj_attr_value);
- $parse_obj_member_name = $parse_obj_name . '__' . $tmp_addr;
- $parse_obj_member_id = $parse_obj_id . '__' . $tmp_addr;
- if (defined($tmp_addr_last)) {
- $parse_obj_member_name .= "-$tmp_addr_last";
- $parse_obj_member_id .= "-$tmp_addr_last";
- }
- $network_objects{"$parse_obj_member_id.type"} = $tmp_type;
- $network_objects{"$parse_obj_member_id.ipaddr"} = $tmp_addr;
- $network_objects{"$parse_obj_member_id.ipaddr_last"} = $tmp_addr_last;
- print_debug ("\nfound new explicit nw_obj group member: $parse_obj_member_id", $debug_level, 8);
- @network_objects = (@network_objects, $parse_obj_member_id);
- $network_objects{"$parse_obj_member_id.name"} = $parse_obj_member_name;
- $network_objects{"$parse_obj_member_id.scope"} = $scope;
- $network_objects{"$parse_obj_member_id.typ"} = 'simple';
- $network_objects{"$parse_obj_member_id.UID"} = $parse_obj_member_id;
- $network_objects{"$parse_obj_member_id.last_change_admin"} = $last_change_admin;
-
- # jetzt die Gruppenreferenzen auf das neue Objekt setzen, sind ja schon definiert, da hier #members >= 2
- $network_objects{"$parse_obj_id.member_refs"} .= ( $GROUPSEP . $parse_obj_member_id );
- $network_objects{"$parse_obj_id.members"} .= ( $GROUPSEP . $parse_obj_member_name );
- } else {
- print_debug ("WARNING: store_nw_results called to add group member without match: $parse_obj_attr", $debug_level, 7);
- }
- } elsif (defined($parse_obj_attr_value)) {
- # Attribute aufarbeiten
- SWITCH_OBJ_ATTR: {
- if ($parse_obj_attr eq 'oid') {
- $network_objects{"$parse_obj_id.UID"} = $parse_obj_attr_value;
- last SWITCH_OBJ_ATTR;
- }
- if (($parse_obj_attr eq 'addr' || $parse_obj_attr eq 'addr6') && $parse_obj_attr_value ne '0.0.0.0') {
- $network_objects{"$parse_obj_id.typ"} = 'simple';
- ($network_objects{"$parse_obj_id.type"},$network_objects{"$parse_obj_id.ipaddr"},$network_objects{"$parse_obj_id.ipaddr_last"}) =
- analyze_phion_ip($parse_obj_attr_value);
- $parse_obj_attr_value = $network_objects{"$parse_obj_id.ipaddr"}; # necessary?
- last SWITCH_OBJ_ATTR;
- }
- if ($parse_obj_attr eq 'comment') {
- $network_objects{"$parse_obj_id.$parse_obj_attr"} = $parse_obj_attr_value; # stores only comments
- }
- }
- }
-}
-
-sub store_results_nw_services {
- if (!defined ($services{"$parse_obj_id.name"})) { # schon ein bekannter dienst?
- @services = (@services, $parse_obj_id);
- $services{"$parse_obj_id.name"} = $parse_obj_name;
- $services{"$parse_obj_id.scope"} = $scope;
- $services{"$parse_obj_id.type"} = 'group';
- $services{"$parse_obj_id.typ"} = 'group';
- $services{"$parse_obj_id.UID"} = $parse_obj_id; # default Wert nur wichtig fuer explizite Dienste
- $services{"$parse_obj_id.last_change_admin"} = $last_change_admin;
- }
-
- if ($convert_simple_to_group_flag) {
- # altes Objekt in Gruppe umwandeln
- $services{"$parse_obj_id.typ"} = 'group';
- $services{"$parse_obj_id.type"} = 'group';
- # zwischenspeichern des Zielports des ersten Elements
- my $proto = '';
- my $dport = '';
- my $dport_last = '';
- my $type = '';
- if (defined($services{"$parse_obj_id.port"})) { # Speichern der Daten des ersten Elements
- $dport = $services{"$parse_obj_id.port"};
- $services{"$parse_obj_id.port"} = '';
- }
- if (defined($services{"$parse_obj_id.port_last"})) {
- $dport_last = $services{"$parse_obj_id.port_last"};
- $services{"$parse_obj_id.port_last"} = '';
- }
- if (defined($services{"$parse_obj_id.ip_proto"})) {
- $proto = $services{"$parse_obj_id.ip_proto"};
- $services{"$parse_obj_id.ip_proto"} = '';
- }
- if (defined($services{"$parse_obj_id.type"})) {
- $type = $services{"$parse_obj_id.type"};
- $services{"$parse_obj_id.type"} = '';
- }
- my $svc_ext = "/$proto/$dport"; if ($dport_last ne '') { $svc_ext .= "-$dport_last"; }
-
- # Anlegen eines neuen Objekts fuer das erste Element
- my $obj_mbr_id = "$parse_obj_id/$scope/$svc_ext";
- @services = (@services, $obj_mbr_id);
- $services{"$obj_mbr_id.name"} = $parse_obj_name . $svc_ext;
- $services{"$obj_mbr_id.scope"} = $scope;
- $services{"$obj_mbr_id.typ"} = 'simple';
- $services{"$obj_mbr_id.type"} = $type;
- $services{"$obj_mbr_id.UID"} = $obj_mbr_id;
- $services{"$obj_mbr_id.port"} = $dport;
- $services{"$obj_mbr_id.port_last"} = $dport_last;
- $services{"$obj_mbr_id.ip_proto"} = $proto;
- $services{"$obj_mbr_id.last_change_admin"} = $last_change_admin;
-
- # jetzt das erste Element als einzigen Member hinzufuegen
- $services{"$parse_obj_id.members"} = $parse_obj_name . $svc_ext;
- $services{"$parse_obj_id.member_refs"} = $obj_mbr_id;
-
- $convert_simple_to_group_flag = 0;
- } # das zweite Element wird anschliessend (bei addr-Zeile in state 5) normal als member hinzugefuegt
- # Daten im Hash ergaenzen
- elsif ($collecting_group_members) {
- if ($parse_obj_attr eq 'ref') {
- if (defined($services{"$parse_obj_id.members"})) {
- $services{"$parse_obj_id.members"} .= ( $GROUPSEP . $parse_obj_attr_value );
- } else {
- $services{"$parse_obj_id.members"} = $parse_obj_attr_value;
- $services{"$parse_obj_id.typ"} = 'group';
- $services{"$parse_obj_id.type"} = 'group';
- }
- } elsif ($parse_obj_attr eq 'refid') {
- if (defined($services{"$parse_obj_id.member_refs"})) {
- $services{"$parse_obj_id.member_refs"} .= ( $GROUPSEP . $parse_obj_attr_value );
- } else {
- $services{"$parse_obj_id.member_refs"} = $parse_obj_attr_value;
- }
- } elsif ($parse_obj_attr eq 'portLimit') { # explizite Gruppe
- # create new object
- my ($new_port, $new_port_last, $new_svc_ext, $new_proto);
- $new_proto = &normalize_phion_proto($proto_of_explicit_svc_group);
- ($new_port, $new_port_last) = &normalize_phion_ports($parse_obj_attr_value);
- $new_svc_ext = "/$new_proto/$new_port"; if ($new_port_last ne '') { $new_svc_ext .= "-$new_port_last"; }
- my $parse_obj_member_name = $parse_obj_name . $new_svc_ext;
- my $parse_obj_member_id = "$parse_obj_id/$scope/$new_svc_ext";
- if (!defined($services{"$parse_obj_member_id.name"})) {
- @services = (@services, $parse_obj_member_id);
- $services{"$parse_obj_member_id.name"} = $parse_obj_member_name;
- $services{"$parse_obj_member_id.scope"} = $scope;
- $services{"$parse_obj_member_id.typ"} = 'simple';
- $services{"$parse_obj_member_id.type"} = (($parse_obj_attr_value =~ /\//)?'network':'host');
- $services{"$parse_obj_member_id.UID"} = $parse_obj_member_id;
- $services{"$parse_obj_member_id.port"} = $new_port;
- $services{"$parse_obj_member_id.port_last"} = $new_port_last;
- $services{"$parse_obj_member_id.ip_proto"} = $new_proto;
- $services{"$parse_obj_member_id.last_change_admin"} = $last_change_admin;
- }
-
- # jetzt die Gruppenreferenzen auf das neue Objekt setzen, sind ja schon definiert, da hier #members >= 2
- $services{"$parse_obj_id.member_refs"} .= ( $GROUPSEP . $parse_obj_member_id );
- $services{"$parse_obj_id.members"} .= ( $GROUPSEP . $parse_obj_member_name );
- } else {
- print_debug ("WARNING: store_nw_results called to add group member without match: $parse_obj_attr", $debug_level, 7);
- }
- } elsif (defined($parse_obj_attr_value)) {
- # Attribute aufarbeiten
- SWITCH_OBJ_ATTR: {
- if ($parse_obj_attr eq 'oid') {
- $services{"$parse_obj_id.UID"} = $parse_obj_attr_value;
- last SWITCH_OBJ_ATTR;
- }
- if ($parse_obj_attr eq 'portLimit') {
- ($services{"$parse_obj_id.port"}, $services{"$parse_obj_id.port_last"}) =
- &normalize_phion_ports($parse_obj_attr_value);
- last SWITCH_OBJ_ATTR;
- }
- if (($parse_obj_attr eq 'botClientPort')) {
- $services{"$parse_obj_id.src_port"} = $parse_obj_attr_value;
- last SWITCH_OBJ_ATTR;
- }
- if (($parse_obj_attr eq 'topClientPort')) {
- $services{"$parse_obj_id.src_port_last"} = $parse_obj_attr_value;
- last SWITCH_OBJ_ATTR;
- }
- if ($parse_obj_attr eq 'sessionTimeout') {
- $services{"$parse_obj_id.timeout"} = $parse_obj_attr_value;
- last SWITCH_OBJ_ATTR;
- }
- if (($parse_obj_attr eq 'proto')) {
- $services{"$parse_obj_id.typ"} = 'simple';
- $services{"$parse_obj_id.ip_proto"} = $parse_obj_attr_value;
- }
- if (($parse_obj_attr eq 'type')) {
- $services{"$parse_obj_id.$parse_obj_attr"} = $parse_obj_attr_value;
- ($services{"$parse_obj_id.typ"},$services{"$parse_obj_id.ip_proto"}) =
- &normalize_phion_proto($parse_obj_attr_value);
- last SWITCH_OBJ_ATTR;
- }
- # Als default das Attribut und den Wert sichern
- $services{"$parse_obj_id.$parse_obj_attr"} = $parse_obj_attr_value;
- }
- }
-}
-
-sub store_results_maplist { # ($nat_group_name, $nat_group_oid, $nat_group_comment, $mapping_details, $debug_level);
- my $nat_obj_name = shift;
- my $nat_obj_uid = shift;
- my $nat_obj_comment = shift;
- my $list_of_nat = shift;
- my $debug_level = shift;
- my $member_count = 0;
- my $remaining_nat_ip_line = $list_of_nat;
-
- if (!defined($debug_level)) { $debug_level = 0; }
-
- if (!defined($nat_obj_name) || !defined($nat_obj_uid)) { return; }
- if (!defined($nat_obj_comment)) { $nat_obj_comment = ''; }
- print_debug("entering store_results_maplist: nat obj name=$nat_obj_name, nat obj uid=$nat_obj_uid, comment=$nat_obj_comment, mapList=$list_of_nat", $debug_level, 5);
- while ($remaining_nat_ip_line =~ /^\s*(\d+\.\d+\.\d+\.\d+)\-\>\d+\.\d+\.\d+\.\d+\s?(.*)/) { # multiple ip nattings
- $parse_obj_attr_value = $1; # incoming ip address
- $remaining_nat_ip_line = $2;
- $parse_obj_attr = 'addr';
- if ($member_count==0) {
- $parse_obj_name = $nat_obj_name;
-# $parse_obj_id = $nat_obj_uid;
-# problem: UID != oid but just the name of the object, needs to be this way for rule ref to work
- $parse_obj_id = $nat_obj_name;
- print_debug(" nat map storing nat group name=$parse_obj_name, value=$parse_obj_attr_value, attr=$parse_obj_attr, id=$parse_obj_id", $debug_level, 5);
- &store_results(); # store group object as single ip host
- # turn object into group:
- $network_objects{"$nat_obj_name.typ"} = 'group';
- $network_objects{"$nat_obj_name.type"} = 'group';
- }
- $parse_obj_name = "$nat_obj_name" . "_nat_ip_$parse_obj_attr_value";
- $parse_obj_id = $parse_obj_name . "_" . $nat_obj_uid;
- print_debug(" nat map storing nat single ip name=$parse_obj_name, value=$parse_obj_attr_value, attr=$parse_obj_attr, id=$parse_obj_id", $debug_level, 5);
- &store_results(); # store object as single ip host
- # add object to group:
- if ($member_count==0) {
- $network_objects{"$nat_obj_name.member_refs"} = $parse_obj_id;
- $network_objects{"$nat_obj_name.members"} = $parse_obj_name;
- undef ($network_objects{"$nat_obj_name.ipaddr"});
- } else {
- $network_objects{"$nat_obj_name.member_refs"} .= ( $GROUPSEP . $parse_obj_id );
- $network_objects{"$nat_obj_name.members"} .= ( $GROUPSEP . $parse_obj_name );
- }
- $member_count ++;
- }
- return;
-}
-
-#----------------------------------------
-# Funktion parse_basic_elements
-# Parameter: in_file: config file # Parameter: rulesetname, um nicht alle rulesets zu parsen
-# zeilenweises Einlesen der Konfigurationsdatei (nw-services und nw-objects)
-# Resultat: keins
-#----------------------------------------
-
-sub parse_basic_elements {
- my $in_file = $_[0];
- my $ln = 0; # line number
- my $line = '';
- my $last_line = '';
- my ($nat_group_name, $nat_group_oid, $nat_group_comment);
-
- $parse_obj_state = 0;
- open (IN, $in_file) || die "$in_file konnte nicht geoeffnet werden.\n";
- LINE: while ( ) {
- $line = $_; # Zeileninhalt merken
-# print ("line: $line\n");
- $last_line = $line; # fuer end of line check
- $line =~ s/\x0D/\\r/g; # literal carriage return entfernen
- $line =~ s/\r\n/\n/g; # handle cr,nl
- chomp ($line);
- $ln++; # Zeilennummer merken
-
-########## parse obj state 1: main level for dividing into obj,svc, ... ################################################
- # state 1 start of type, z.B. " netobj={"
-# if ( /^\t([\w\-\.\_]+)\=\{$/ ) {
- if ( /^\t(netobj)\=\{$/ || /^\t(srvobj)\=\{$/ || /^\t(connobj)\=\{$/ ) {
- $parse_obj_state = 1;
- $parse_obj_type = $1;
- print_debug("up 0 (type: $parse_obj_type) => 1 at line no. $ln: $line", $debug_level, 3);
- } elsif ( /^\t\}$/ ) {
- $parse_obj_state = 0;
- undef ($parse_obj_type);
- }
-########## parse obj state 2: main level for (new) element ################################################
- if (defined ($parse_obj_type)) {
- if ((/^\t\t([\w]+)\{$/) && ( ($parse_obj_state == 2) || ($parse_obj_state == 1))) {
- $parse_obj_state = 2;
- }
- if (/^\t\t\}$/) { # Ende der Element-Definition
- $parse_obj_state = 1;
- undef ($parse_obj_name);
- undef ($parse_obj_id);
- }
- ########## parse obj state 3: get element base data (name, comment, ...) ################################################
- if ((/^\t{3}(\w+)\=\{(.*?)$/) && ( ($parse_obj_state == 3) || ($parse_obj_state == 2))) {
- $parse_obj_state = 3;
- $parse_obj_attr = $1;
- if ($2 ne '') { # Ist der Wert in der aktuellen Zeile nicht leer?
- # value zwischenspeichern und untersuchen
- my $local_line = $2;
- # Ist der Wert in einer Zeile mit dem Parameter angegeben und nicht leer?
- if ($local_line =~(/^(.+)\}$/)) {
- $parse_obj_attr_value = $1;
-### maplist handling
- if ($parse_obj_type eq 'connobj') {
- if ($parse_obj_attr eq 'name') { $nat_group_name = $parse_obj_attr_value; }
- if ($parse_obj_attr eq 'oid') { $nat_group_oid = $parse_obj_attr_value; }
- if ($parse_obj_attr eq 'comment') { $nat_group_comment = $parse_obj_attr_value; }
- if ($parse_obj_attr eq 'mapList') {
-# storing dest nat object(s) from connection obj definition, assuming that mapList is always last statement of connobj definition
- &store_results_maplist($nat_group_name, $nat_group_oid, $nat_group_comment, $parse_obj_attr_value, $debug_level);
- }
- next LINE;
- }
-### end of maplist handling
- if ($parse_obj_attr eq 'name') {
- $parse_obj_name = $parse_obj_attr_value;
- $parse_obj_id = $parse_obj_attr_value; # Default-Wert fuer den Fall, dass keine OID vorhanden ist
- } # found name of element
- if ($parse_obj_attr eq 'oid') { $parse_obj_id = $parse_obj_attr_value; }
- if ( ($parse_obj_attr eq 'comment') || ($parse_obj_attr eq 'oid') ) {
- store_results();
- }
- # Keine Gruppe/Liste geoeffnt, daher parse_obj_state auf Level 3 belassen
- undef ($parse_obj_attr);
- }
- } elsif ($parse_obj_attr eq 'list' || $parse_obj_attr eq 'neglist') {
- $parse_obj_neglist = 0;
- $member_counter = 0;
- if ($parse_obj_attr eq 'neglist') {
- $parse_obj_neglist = 1;
- }
- $parse_obj_state = 4; # jetzt koennen beliebige Listen kommen (Laenge >=1)
- }
- }
- if ((/^\t{3}\}$/)) { # Ende des state 3
- $collecting_group_members = 0; # jetzt ist die Gruppe, sofern angefangen, zu Ende
- undef($parse_obj_attr);
- }
- ########## parse obj state 4: Zwischenebene mit wenig Configdaten ################################################
- if ( (/^\t{4}(\w+)\{$/) ) {
- $parse_obj_state = 4;
- $parse_obj_attr_ext = $1;
- $convert_simple_to_group_flag = 0;
- $member_counter ++;
- if ($member_counter == 2 && !$collecting_group_members) { # doch eine "explizite" Gruppe
- if (defined($parse_obj_name)) { print_debug ("found explicit group: $parse_obj_name", $debug_level, 5); }
- # rename existing object (group_name + addr / destport)
- # create group
- $convert_simple_to_group_flag = 1;
- &store_results(); # convert to group and add first (already read) group member
- $collecting_group_members = 1;
- }
- if (!((($parse_obj_attr_ext eq 'NetEntry' || $parse_obj_attr_ext eq 'NetRef') && $parse_obj_type eq 'netobj') ||
- (($parse_obj_attr_ext eq 'ServiceEntryTCP' || $parse_obj_attr_ext eq 'ServiceEntryUDP' ||
- $parse_obj_attr_ext eq 'ServiceRef' || $parse_obj_attr_ext eq 'ServiceEntryEcho' ||
- $parse_obj_attr_ext eq 'ServiceEntryOther') # for other ip protos
- && $parse_obj_type eq 'srvobj') )) {
- print_debug ("WARNING: found unknown or misplaced element $parse_obj_attr_ext in section $parse_obj_type", $debug_level, 7);
- }
- if (!$collecting_group_members && ($parse_obj_attr_ext eq 'NetRef' || $parse_obj_attr_ext eq 'ServiceRef')) {
- $collecting_group_members = 1; # Gruppe faengt an
- }
- if ($parse_obj_attr_ext =~ 'Entry') {
- if ($parse_obj_attr_ext =~ /^ServiceEntry(.*?)$/ ) {
- # das IP-Protokoll extrahieren
- $parse_obj_attr = 'type';
- $parse_obj_attr_value = lc($1);
- $proto_of_explicit_svc_group = $parse_obj_attr_value;
- &store_results();
- $parse_obj_attr = ''; $parse_obj_attr_value = '';
- }
- }
- }
- # Ende des state 4
- if ((/^\t{4}\}$/) && ($parse_obj_state < 10)) {
- $parse_obj_state = 3;
- undef ($parse_obj_attr_ext); undef ($parse_obj_attr_ext_value);
- }
- ########## parse obj state 5: get element details ################################################
- if ((/^\t{5}\s?(\w+)\=\{(.*?)\}$/) && ( ($parse_obj_state == 5) || ($parse_obj_state == 4))) {
- $parse_obj_state = 5;
- if ($2 ne '') {
- $parse_obj_attr = $1;
- $parse_obj_attr_value = $2;
- if ($parse_obj_attr eq 'addr' || $parse_obj_attr eq 'addr6' || $parse_obj_attr eq 'comment' ||
- $parse_obj_attr eq 'sessionTimeout' || $parse_obj_attr eq 'botClientPort' ||
- $parse_obj_attr eq 'topClientPort' || $parse_obj_attr eq 'portLimit' ||
- $parse_obj_attr eq 'proto' ||
- $parse_obj_attr eq 'ref' || $parse_obj_attr eq 'refid' ) {
- &store_results();
- }
- }
- }
- # Ende des state 5
- if ((/^\t{5}\}$/)) {
- $parse_obj_state = 4;
- }
- }
- } # end of while
- close (IN);
- print_debug ("$in_file closed",$debug_level,8);
- # check auf Vollstaendigkeit des Config-Files:
- if ($last_line =~ m/^\}(\n)?$/) { return 0; }
- else { return "ERROR: last line of config-file $in_file not correct: <$last_line>"; }
-}
-
-#####################################################
-
-sub store_results_rules {
- if ($parse_obj_state==0) { # nur am Ende eines Regelsatzes: Regelreihenfolge wegschreiben
- $rulebases{"$scope.ruleorder"} = join (',', @ruleorder);
- undef @ruleorder;
- # last change admin fuer gesamtes Regelwerk setzen
-# $rulebases{"$scope.last_change_admin"} = $last_change_admin; # wird derzeit nicht ausgewertet, nur pro Regel
- } elsif ($parse_obj_state==1) { # nur am Ende einer Regel, ggf. UID setzen
- $ruleorder[$parserule_rulenum] = $parserule_rulenum; # Regeln sind einfach von 1-n durchnummeriert
- if (!defined ($rulebases{"$scope.$parserule_rulenum.UID"})) { # UID setzen, falls noch nicht geschehen
- $rulebases{"$scope.$parserule_rulenum.UID"} = "$scope:" . $rulebases{"$scope.$parserule_rulenum.name"};
- }
- $rulebases{"$scope.$parserule_rulenum.last_change_admin"} = $last_change_admin; # last_change_admin pro Regel setzen
- $parserule_rulenum++;
- undef ($parse_obj_name);
- undef ($parse_obj_id);
- } else { # schon eine bekannte Regel?
- if (!defined ($rulebases{"$scope.$parserule_rulenum.name"})) {
- if (!defined($rulebases{"$scope.rulecount"})) {
- $rulebases{"$scope.rulecount"} = 1;
- } else {
- $rulebases{"$scope.rulecount"} += 1;
- }
- $rulebases{"$scope.$parserule_rulenum.name"} = $parse_obj_name;
- $rulebases{"$scope.$parserule_rulenum.src.op"} = '0';
- $rulebases{"$scope.$parserule_rulenum.dst.op"} = '0';
- $rulebases{"$scope.$parserule_rulenum.services.op"} = '0';
- }
- # Daten im Hash ergaenzen
- if (defined($parse_rule_field)) {
- if ($parse_obj_attr eq 'ref') {
- if (defined($rulebases{"$scope.$parserule_rulenum.$parse_rule_field"})) {
- $rulebases{"$scope.$parserule_rulenum.$parse_rule_field"} .= ( $GROUPSEP . $parse_obj_attr_value );
- $rulebases{"$scope.$parserule_rulenum.$parse_rule_field.refs"} .= $GROUPSEP; # ref_Feld ebenfalls erweitern um ein Element (falls ref_id leer)
- } else {
- $rulebases{"$scope.$parserule_rulenum.$parse_rule_field"} = $parse_obj_attr_value;
- $rulebases{"$scope.$parserule_rulenum.$parse_rule_field.refs"} = ''
- }
- } elsif ($parse_obj_attr eq 'refid') { # Annahme: ref_id kommt immer nach ref
- $rulebases{"$scope.$parserule_rulenum.$parse_rule_field.refs"} .= $parse_obj_attr_value;
- } elsif ($parse_obj_attr eq 'action') {
- print_debug ("found action: $parse_obj_attr_value", $debug_level, 7);
- if ($parse_obj_attr_value eq 'ActionPass') { $parse_obj_attr_value = 'accept'; }
- elsif ($parse_obj_attr_value eq 'ActionDeny') { $parse_obj_attr_value = 'reject'; }
- elsif ($parse_obj_attr_value eq 'ActionBlock') { $parse_obj_attr_value = 'drop'; }
- elsif ($parse_obj_attr_value eq 'ActionRedirect') { $parse_obj_attr_value = 'redirect'; }
- elsif ($parse_obj_attr_value eq 'ActionMap') { $parse_obj_attr_value = 'map'; }
- $rulebases{"$scope.$parserule_rulenum.action"} = $parse_obj_attr_value;
- } elsif ($parse_obj_attr eq 'subsetName') {
- print_debug ("found action subsetName: $parse_obj_attr_value", $debug_level,7);
- $rulebases{"$scope.$parserule_rulenum.action.subsetName"} = $parse_obj_attr_value;
- } else {
- print_debug ("group member attribute: $parse_obj_attr/$parse_obj_attr_value - ignoring", $debug_level, 7);
- }
- } elsif (defined($parse_obj_attr_value)) {
- if ($parse_obj_state < 5) { # rule top level
- # Attribute aufarbeiten
- SWITCH_OBJ_ATTR: {
- if ($parse_obj_attr eq 'oid') {
- $rulebases{"$scope.$parserule_rulenum.UID"} = $parse_obj_attr_value;
- last SWITCH_OBJ_ATTR;
- }
- if ($parse_obj_attr eq 'deactivated') {
- $rulebases{"$scope.$parserule_rulenum.disabled"} = $parse_obj_attr_value;
- last SWITCH_OBJ_ATTR;
- }
- if ($parse_obj_attr eq 'noLog') {
- if ($parse_obj_attr_value eq '0') {
- $rulebases{"$scope.$parserule_rulenum.track"} = 'log';
- } elsif ($parse_obj_attr_value eq '1') {
- $rulebases{"$scope.$parserule_rulenum.track"} = 'none';
- }
- last SWITCH_OBJ_ATTR;
- }
- if ($parse_obj_attr eq 'timeAllow') {
- $rulebases{"$scope.$parserule_rulenum.time"} = $parse_obj_attr_value;
- last SWITCH_OBJ_ATTR;
- }
- if ($parse_obj_attr eq 'comment') {
- $rulebases{"$scope.$parserule_rulenum.comments"} = $parse_obj_attr_value;
- last SWITCH_OBJ_ATTR;
- }
- $rulebases{"$scope.$parserule_rulenum.$parse_obj_attr"} = $parse_obj_attr_value;
- }
- }
- if ($parse_obj_attr eq 'bothWays' && $parse_obj_attr_value eq '1') { # bidirectional rule
- $rulebases{"$scope.$parserule_rulenum.bidir"} = $parse_obj_attr_value;
- }
- }
- }
-}
-
-#----------------------------------------
-# Funktion section_titles_correction
-# Parameter: none
-#----------------------------------------
-# for each rule:
-# check if src/dst/srv are empty
-# add header_text
-# add dummy src/dst/srv objects
-
-sub section_titles_correction {
- my ($count, $rulebase_name);
- my @ruleorder;
-
- sub get_any_nwobj_refs {
- my $obj_id;
- foreach $obj_id (@network_objects) { if ($network_objects{"$obj_id.name"} eq 'World') { return $obj_id; } }
- return undef;
- }
-
- sub get_any_srvobj_refs {
- my $svc_id;
- foreach $svc_id (@services) { if ($services{"$svc_id.name"} eq 'ALL') { return $svc_id; } }
- return undef;
- }
-
- sub is_phion_section_title {
- my $rulebase_name = shift;
- my $rule_number = shift;
-
- if ( !defined($rulebases{"$rulebase_name.$rule_number.src"}) &&
- !defined($rulebases{"$rulebase_name.$rule_number.dst"}) &&
- !defined($rulebases{"$rulebase_name.$rule_number.services"})) {
- return 1;
- } else {
- return 0;
- }
- }
-
- my $any_nwobj_ref = &get_any_nwobj_refs();
- my $any_srvobj_ref = &get_any_srvobj_refs();
-
- if (!defined($any_nwobj_ref) || !defined($any_srvobj_ref)) {
- print_debug ('error: no any objects found in config', $debug_level, 1);
- } else {
- foreach $rulebase_name (@rulebases) {
- if (defined($rulebases{"$rulebase_name.rulecount"}) && defined ($rulebases{"$rulebase_name.ruleorder"}) ) {
- @ruleorder = split(/\,/, $rulebases{"$rulebase_name.ruleorder"});
- $count = 0;
- while ($count < $rulebases{"$rulebase_name.rulecount"}) {
- my $rule_number = $ruleorder[$count];
- print_debug ("rulebase: $rulebase_name, rule_no: $rule_number", $debug_level, 8);
- if (&is_phion_section_title($rulebase_name, $rule_number)) {
- # Section-Title vervollstaendigen
- $rulebases{"$rulebase_name.$ruleorder[$count].header_text"} = $rulebases{"$rulebase_name.$ruleorder[$count].name"};
- if (defined($rulebases{"$rulebase_name.$ruleorder[$count].UID"})) {
- $rulebases{"$rulebase_name.$ruleorder[$count].UID"} .= $rulebases{"$rulebase_name.$ruleorder[$count].name"};
- } else {
- $rulebases{"$rulebase_name.$ruleorder[$count].UID"} = $rulebase_name . "." . $rulebases{"$rulebase_name.$ruleorder[$count].name"};
- }
- $rulebases{"$rulebase_name.$ruleorder[$count].action"} = 'drop';
- $rulebases{"$rulebase_name.$ruleorder[$count].src"} = 'World';
- $rulebases{"$rulebase_name.$ruleorder[$count].src.refs"} = $any_nwobj_ref;
- $rulebases{"$rulebase_name.$ruleorder[$count].dst"} = 'World';
- $rulebases{"$rulebase_name.$ruleorder[$count].dst.refs"} = $any_nwobj_ref;
- $rulebases{"$rulebase_name.$ruleorder[$count].services"} = 'ALL';
- $rulebases{"$rulebase_name.$ruleorder[$count].services.refs"} = $any_srvobj_ref;
- }
- $count ++;
- }
- }
- }
- }
-}
-
-sub fix_single_field_references {
- my $rulebase_name = shift;
- my $field = shift;
- my $rule_count = shift;
- my (@names, @names_original);
- my @refs = ();
-
- if (defined ($rulebases{"$rulebase_name.$rule_count.$field"}) && $rulebases{"$rulebase_name.$rule_count.$field"} ne '') {
- @names = split (/\|/, $rulebases{"$rulebase_name.$rule_count.$field"});
- @names_original = @names;
- my $idx=0; my $changed=0;
- if (defined $rulebases{"$rulebase_name.$rule_count.$field.refs"}) {
- @refs = split (/\|/, $rulebases{"$rulebase_name.$rule_count.$field.refs"}); # explicit use of | instead of $GROUPSEP
- } else {
- print_debug ("phion.pm WARNING (0): $rulebase_name, all refs undefined for rule $rule_count.$field.refs, name: " . $rulebases{"$rulebase_name.$rule_count.$field"}, $debug_level, 8);
- }
- ELEMENT: while ($idx assume explicite object definition
- if ($field eq 'services') {
- if (defined ($services{"$names[$idx].name"}) && $services{"$names[$idx].name"} ne '') {
- print_debug ("phion.pm fixref WARNING (1): service in $rulebase_name, repairing ref to explicite $field object " . $names[$idx], $debug_level, 6);
- $refs[$idx] = $names[$idx];
- $changed=1;
- } else { # even name is not set: delete element
- print_debug ("phion.pm WARNING (2): service in $rulebase_name, deleting broken references to incomplete $field object " . $names[$idx], $debug_level, 6);
- splice (@refs, $idx, 1);
- splice (@names, $idx, 1);
-# $idx--;
- next ELEMENT;
- }
- } else { # network_objects
- if (defined ($network_objects{"$names[$idx].name"}) && $network_objects{"$names[$idx].name"} ne '') { # no references are defined --> assume explicite object definition
- if ((defined ($network_objects{"$names[$idx].ipaddr"}) && $network_objects{"$names[$idx].ipaddr"} ne '') ||
- (defined ($network_objects{"$names[$idx].members"}) && $network_objects{"$names[$idx].members"} ne '')) {
- $refs[$idx] = $names[$idx];
- print_debug ("phion.pm fixref WARNING (3): $rulebase_name, fixing ref to $field object " . $names[$idx], $debug_level, 6);
- } else { # even name is not set: delete element
- print_debug ("phion.pm WARNING (4): $rulebase_name, deleting broken references to incomplete $field object " . $names[$idx], $debug_level, 6);
- splice (@refs, $idx, 1);
- splice (@names, $idx, 1);
-# $idx--;
- next ELEMENT;
- }
- } else { # network objects: network_object in rule is not defined
- if ($names[$idx] =~ /(.*?)\:bwd$/) { # NAT network objects in dst
- $names[$idx] = $1;
- if (defined($network_objects{"$names[$idx].name"})) {
- $refs[$idx] = $network_objects{"$names[$idx].UID"};
- print_debug ("phion.pm fixref WARNING (6): $rulebase_name, fixing ref to $field NAT object " . $names[$idx], $debug_level, 6);
- } else {
- if (defined($network_objects{"$names[$idx].name"}) || defined($network_objects{"$names[$idx].UID"})) {
- print_debug ("phion.pm fixref WARNING (7): undefined NAT object " . $names[$idx] . ", nwobj-name: ". $network_objects{"$names[$idx].name"} .
- ", uid=" . $network_objects{"$names[$idx].UID"}, $debug_level, 6);
- } else {
- print_debug ("phion.pm fixref WARNING (8): undefined NAT object " . $names[$idx], $debug_level, 6);
- }
- }
- $changed = 1;
- } else { # if no nat object and not defined as nw object, then delete it
- print_debug ("phion.pm WARNING (5): $rulebase_name, deleting broken references to non-existant $field object " . $names[$idx], $debug_level, 6);
- splice (@refs, $idx, 1);
- splice (@names, $idx, 1);
-# $idx--;
- next ELEMENT;
- }
- }
- $changed=1;
- }
- } else {
- # refs are defined - but are they pointing anyhere?
- }
-=POD
- if ((defined($refs[$idx]) && $refs[$idx] ne '' && $field eq 'services' && !defined ($services{"$refs[$idx].UID"}))) { # ref ist defined but points to nowhere
- print ("phion.pm WARNING (10): $rulebase_name, removing broken references to incomplete $field object " . $names[$idx] . "\n");
- splice (@refs, $idx, 1);
- splice (@names, $idx, 1);
- $idx--;
- $changed=1;
- next ELEMENT;
- }
- if ((defined($refs[$idx]) && $refs[$idx] ne '' && ($field eq 'src' || $field eq 'dst')) && !defined($network_objects{"$refs[$idx].UID"})) { # ref ist defined but points to nowhere
- print ("phion.pm WARNING (10): $rulebase_name, removing broken references to incomplete network $field object " . $names[$idx] . "\n");
- splice (@refs, $idx, 1);
- splice (@names, $idx, 1);
- $idx--;
- $changed=1;
- next ELEMENT;
- }
-=cut
- $idx++;
- }
- if ($changed) {
- print_debug ("changed list from " . join("$GROUPSEP", @names_original) . "\nto " . join("$GROUPSEP", @names), $debug_level, 6);
- if (scalar(@names) == 0) { # if a whole field was deleted then delete the whole rule
- print_debug ("phion.pm fixref ERROR (6): $rulebase_name, deleted whole field $field object of rule no. $idx:\n" .
- "src: " . $rulebases{"$rulebase_name.$rule_count.src"} . "\n" .
- "dst: " . $rulebases{"$rulebase_name.$rule_count.dst"} . "\n" .
- "svc: " . $rulebases{"$rulebase_name.$rule_count.services"}, $debug_level, 7);
- return ("\n");
- }
- }
- return join("$GROUPSEP", @names) . "\n" . join("$GROUPSEP", @refs);
- } else {
- return ("\n");
- }
-}
-
-sub delete_whole_rule_if_a_relevant_field_is_empty {
- my $rulebase_name = shift;
- my $rule_count = shift;
- my $to_be_deleted=0;
- my @ruleorder = split(/\,/, $rulebases{"$rulebase_name.ruleorder"});
-
- if (!defined($rulebases{"$rulebase_name.$ruleorder[$rule_count].src"}) || !defined($rulebases{"$rulebase_name.$ruleorder[$rule_count].dst"}) ||
- !defined($rulebases{"$rulebase_name.$ruleorder[$rule_count].services"})) {
- $to_be_deleted = 1;
- } elsif ($rulebases{"$rulebase_name.$ruleorder[$rule_count].src"} eq '' || $rulebases{"$rulebase_name.$ruleorder[$rule_count].dst"} eq '' || $rulebases{"$rulebase_name.$ruleorder[$rule_count].services"} eq '') {
- $to_be_deleted = 1;
- }
- if ($to_be_deleted) {
- splice(@ruleorder, $rule_count, 1);
- print_debug("delete_whole_rule_if_a_relevant_field_is_empty: deleting in rulebase $rulebase_name rule no $rule_count, id=" .
- $rulebases{"$rulebase_name.$ruleorder[$rule_count].UID"}, $debug_level, 3);
-# $rule_count--;
- $rulebases{"$rulebase_name.rulecount"} = $rulebases{"$rulebase_name.rulecount"} - 1;
- $rulebases{"$rulebase_name.ruleorder"} = join (',', @ruleorder);
- }
- return $to_be_deleted;
-}
-
-sub fix_bidir_rules {
-
- sub switch_rule_src_dst {
- my $dstkey = shift;
- my $src_tmp;
-
- $src_tmp = $rulebases{"$dstkey.src"};
- $rulebases{"$dstkey.src"} = $rulebases{"$dstkey.dst"};
- $rulebases{"$dstkey.dst"} = $src_tmp;
- $src_tmp = $rulebases{"$dstkey.src.refs"};
- $rulebases{"$dstkey.src.refs"} = $rulebases{"$dstkey.dst.refs"};
- $rulebases{"$dstkey.dst.refs"} = $src_tmp;
- $src_tmp = $rulebases{"$dstkey.src.op"};
- $rulebases{"$dstkey.src.op"} = $rulebases{"$dstkey.dst.op"};
- $rulebases{"$dstkey.dst.op"} = $src_tmp;
- if (defined($rulebases{"$dstkey.name"})) {
- $rulebases{"$dstkey.name"} .= ".bothWays_reverse";
- } else {
- $rulebases{"$dstkey.name"} = "bothWays_reverse";
- }
- }
-
- my ($rule_count, $rulebase_name, $new_rule_number, $dstkey, $srckey, @ruleorder);
-
- foreach $rulebase_name (@rulebases) {
- if (defined($rulebases{"$rulebase_name.ruleorder"})) {
- @ruleorder = split(/\,/, $rulebases{"$rulebase_name.ruleorder"});
- $rule_count = 0;
- if (defined($rulebases{"$rulebase_name.rulecount"})) {
- while ($rule_count < $rulebases{"$rulebase_name.rulecount"}) {
- if (defined($rulebases{"$rulebase_name.$ruleorder[$rule_count].bidir"})) {
- print_debug ("debug: fix_bidir_rules found bidir rule: $rulebase_name.$ruleorder[$rule_count]", $debug_level, 3);
- $new_rule_number = $ruleorder[$rule_count]."reverse";
- $rulebases{"$rulebase_name.rulecount"} ++; # Gesamtzahl der Regeln um eins erhoehen
- $rule_count ++;
- splice(@ruleorder, $rule_count, 0, $new_rule_number); # fuegt neue Regel in ruleorder-array ein
- $srckey = "$rulebase_name." . $ruleorder[$rule_count-1];
- $dstkey = "$rulebase_name." . $new_rule_number;
- print_debug ("debug: phion.pm rulebase=$rulebase_name, srckey=$srckey, dstkey=$dstkey, rule_count=$rule_count, new_rule_number=$new_rule_number", $debug_level, 1);
- ©_rule($srckey, $dstkey);
- &switch_rule_src_dst($dstkey);
- # now setting UID to a unique value for the case that a sublist is called more then once
- $rulebases{"$dstkey.UID"} = $rulebases{"$dstkey.UID"} . '.' . 'reverse_direction_of_bothWays_rule';
- }
- $rule_count++;
- }
- } else {
- print_debug ("warning: phion.pm found undefined rulecount for $rulebase_name", $debug_level, 3);
- }
- $rulebases{"$rulebase_name.ruleorder"} = join(',', @ruleorder);
- } else {
- print_debug ("warning: phion.pm empty ruleset for rulebase $rulebase_name", $debug_level, 3);
- }
- }
-}
-
-#----------------------------------------
-# for each rulebase:
-# search basic objects in rulebase that do not contain an OID
-# check if this object was defined anywhere
-# if yes, add name as OID
-# if no: delete object from rule and issue warning about broken ref
-sub fix_rule_references {
- my ($rule_count, $rulebase_name);
-
- foreach $rulebase_name (@rulebases) {
- if (defined($rulebases{"$rulebase_name.ruleorder"})) {
- @ruleorder = split(/\,/, $rulebases{"$rulebase_name.ruleorder"});
- $rule_count = 0;
- if (defined($rulebases{"$rulebase_name.rulecount"})) {
- while ($rule_count < $rulebases{"$rulebase_name.rulecount"}) {
- ($rulebases{"$rulebase_name.$ruleorder[$rule_count].src"},$rulebases{"$rulebase_name.$ruleorder[$rule_count].src.refs"}) =
- split(/\n/, &fix_single_field_references ($rulebase_name, "src", $ruleorder[$rule_count]));
- ($rulebases{"$rulebase_name.$ruleorder[$rule_count].dst"},$rulebases{"$rulebase_name.$ruleorder[$rule_count].dst.refs"}) =
- split(/\n/, &fix_single_field_references ($rulebase_name, "dst", $ruleorder[$rule_count]));
- ($rulebases{"$rulebase_name.$ruleorder[$rule_count].services"},$rulebases{"$rulebase_name.$ruleorder[$rule_count].services.refs"}) =
- split(/\n/, &fix_single_field_references ($rulebase_name, "services", $ruleorder[$rule_count]));
- if (!&delete_whole_rule_if_a_relevant_field_is_empty($rulebase_name, $rule_count)) {
- $rule_count++;
- }
- }
- } else {
- print_debug ("warning: phion.pm found undefined rulecount for $rulebase_name", $debug_level, 3);
- }
- } else {
- print_debug ("warning: phion.pm empty ruleset for rulebase $rulebase_name", $debug_level, 3);
- }
- }
-}
-
-#----------------------------------------
-# Funktion add_basic_object_oids_in_rules_for_locally_defined_objects
-# Parameter: none
-#----------------------------------------
-# for each rulebase:
-# search basic objects in rulebase that do not contain an OID
-# check if this object was defined locally
-# if yes, add name as OID
-sub add_basic_object_oids_in_rules_for_locally_defined_objects {
- my ($rule_count, $rulebase_name);
-
- sub fix_references {
- my $rulebase_name = shift;
- my $field = shift;
- my $rule_count = shift;
- my @names;
- my @refs = ();
-
- if (defined ($rulebases{"$rulebase_name.$rule_count.$field"}) && $rulebases{"$rulebase_name.$rule_count.$field"} ne '') {
- @names = split (/\|/, $rulebases{"$rulebase_name.$rule_count.$field"});
- my $idx=0; my $changed=0;
- if (defined $rulebases{"$rulebase_name.$rule_count.$field.refs"}) {
- @refs = split (/\|/, $rulebases{"$rulebase_name.$rule_count.$field.refs"}); # explicit use of | instead of $GROUPSEP
- } else {
- print_debug ("WARNING (1): $rulebase_name, ref undefined for rule $rule_count.$field.refs, name: " . $rulebases{"$rulebase_name.$rule_count.$field", $debug_level, 6});
- }
- while ($idx ) {
- $line = $_; # Zeileninhalt merken
- $last_line = $line; # fuer end of line check
- $line =~ s/\x0D/\\r/g; # literal carriage return entfernen
- $line =~ s/\r\n/\n/g; # handle cr,nl
- chomp ($line);
- print_debug ("Zeile $ln, line: $line", $debug_level, 13);
- $ln++;
-
-########## parse rule state 1: main level for dividing into obj,svc, ... ################################################
- # state 1 start of rule section rules={"
-
- if ( /^\tsublists\=\{$/ ) {
- $sublist_flag = 1;
- $indent = '\t\t'; # Variabler Einzug (leer bei rules=, enthaelt zwei Tabs in sublist=)
- }
- if ( $sublist_flag && /^\t\tRuleList\{$/ ) { $rulelist_flag = 1; $parserule_rulenum = 0; }
- if ( $rulelist_flag && /^\t\t\tname\=\{(.+?)\}$/ ) {
- $rulelist_name = $1;
- $rulebases{"$scope.$parserule_rulenum.UID"} = "$scope" . "__sublist__$rulelist_name";
- $scope_bu = $scope;
- $scope .= "__sublist__$rulelist_name";
- }
-
- if ( $rulelist_flag && /^${indent}\}$/ ) { # Ende einer Rulelist
- $rulelist_flag = 0;
- $parse_obj_state = 0;
- $parse_obj_type = 'rules';
- &store_results();
- $scope = $scope_bu;
- $rulelist_name = '';
- }
-
- if ( /^${indent}\trules\=\{$/ ) { # Beginn der Regel-Section (sowohl main als auch sublist)
- $parse_obj_state = 1;
- $parse_obj_type = 'rules';
- }
- if ( defined($parse_obj_state) && $parse_obj_state>=1 && /^${indent}\t\}$/ ) { # end of 'rules=' or end of 'sublists=' resetting sublist flags and values
- $parse_obj_state = 0;
- &store_results();
- undef ($parse_obj_type);
- }
- if ( defined($parse_obj_state) && $parse_obj_state>=1) { # alle anderen Bereiche ausser "rules" ueberspringen
-########## parse rule state 2: main level for (new) rule ################################################
- if ((/^${indent}\t\tRule\{$/) && ( ($parse_obj_state == 2) || ($parse_obj_state == 1))) {
- $parse_obj_state = 2;
- }
- if (/^${indent}\t\t\}$/) { # Ende der Element-Definition
- $parse_obj_state = 1;
- &store_results();
- }
-########## parse rule state 3: get element base data (name, comment, ...) ################################################
- if ((/^${indent}\t{3}(\w+)\=\{(.*?)$/) && ( ($parse_obj_state == 3) || ($parse_obj_state == 2))) {
- $parse_obj_state = 3;
- $parse_obj_attr = $1;
- $explicit_flag = 0;
- if ($2 ne '') { # einzeilige Definition
- # value zwischenspeichern und untersuchen
- my $local_line = $2;
- # Ist der Wert in einer Zeile mit dem Parameter angegeben und nicht leer?
- if ($local_line =~(/^(.+)\}$/)) {
- $parse_obj_attr_value = $1;
- if ($parse_obj_attr eq 'name') { $parse_obj_name = $parse_obj_attr_value; }
- if ($parse_obj_attr eq 'oid') { $parse_obj_id = $parse_obj_name . '__uid__' . $parse_obj_attr_value; }
- if (
- ($parse_obj_attr eq 'comment')
- || ($parse_obj_attr eq 'oid')
- || ($parse_obj_attr eq 'deactivated')
- || ($parse_obj_attr eq 'timeAllow')
- || ($parse_obj_attr eq 'name')
- || ($parse_obj_attr eq 'noLog')
- ) {
- &store_results();
- }
- # Keine Gruppe/Liste geoeffnt, daher parse_obj_state auf Level 3 belassen
- undef ($parse_obj_attr);
- }
- } else {
- $parse_rule_field_details = $parse_obj_attr;
- if ($parse_rule_field_details eq 'src' || $parse_rule_field_details eq 'srcExplicit') {
- $parse_rule_field = 'src';
- }
- elsif ($parse_rule_field_details eq 'dst' || $parse_rule_field_details eq 'dstExplicit') {
- $parse_rule_field = 'dst';
- }
- elsif ($parse_rule_field_details eq 'srv' || $parse_rule_field_details eq 'srvExplicit') {
- $parse_rule_field = 'services';
- }
- elsif ($parse_rule_field_details eq 'action') {
- $parse_rule_field = 'action';
- }
- else {
- undef ($parse_rule_field);
- }
- if ($parse_rule_field_details =~ /^...Explicit$/) {
- $explicit_flag = 1;
- }
- $parse_obj_state = 4; # jetzt koennen beliebige Listen kommen (Laenge >=1)
- }
- }
- if ((/^${indent}\t{3}\}$/)) { # Ende des state 3
- undef ($parse_rule_field); # jetzt ist ein Gruppe, sofern angefangen, zu Ende
- undef($parse_obj_attr);
- }
-########## parse rule state 4: Zwischenebene mit wenig Configdaten ################################################
- if ( (/^${indent}\t{4}(\w+)\{$/) ) {
- $parse_obj_state = 4;
- if (!defined($parse_rule_field)) { # wenn in State 4 kein Feld gesetzt wurde: ignorieren
- if ($1 ne 'FilterGroupRef' && $1 ne 'DevGroupRef' && $1 ne 'PARPRef') {
- print_debug ("warning parsing rule state 4 - ignoring $1", $debug_level, 6);
- }
- } else {
- $parse_obj_attr_ext = $1;
- if (!(
- (
- ($parse_rule_field eq 'src' || $parse_rule_field eq 'dst') &&
- ($parse_obj_attr_ext eq 'NetEntry' || $parse_obj_attr_ext eq 'NetRef' ||
- $parse_obj_attr_ext eq 'NetSet' || $parse_obj_attr_ext eq 'NetESet')
- ) ||
- (
- ($parse_rule_field eq 'services') &&
- ($parse_obj_attr_ext eq 'ServiceRef' || $parse_obj_attr_ext eq 'ServiceSet')
- ) ||
- (
- ($parse_rule_field eq 'action') && ($parse_obj_attr_ext =~ /^Action/ )
- )
- )) {
- print_debug ("rule state 4 error: found unknown or misplaced element $parse_obj_attr_ext in rule section $parse_rule_field, " .
- "line: $ln, field: $parse_rule_field, attr_ext: $parse_obj_attr_ext", $debug_level, 1);
- } else { # extracting rule details
- $parse_rule_field_details = $1;
- $parse_obj_state = 5;
- if ($parse_rule_field eq 'action') { # action sofort auswerten
- $parse_obj_attr = 'action';
- $parse_obj_attr_value = $parse_obj_attr_ext;
- &store_results();
- } # alles andere in state 5 eins tiefer
- }
- }
- }
- # Ende des state 4
- if ( (/^${indent}\t{4}\}$/) ) {
- $parse_obj_state = 3;
- undef ($parse_rule_field);
- undef ($parse_obj_attr_ext); undef ($parse_obj_attr_ext_value);
- }
- ########## parse rule state 5: get element details ################################################
-# if ( /^${indent}\t{5}(\w+)\=\{(.*?)\}?$/ && $parse_obj_state==5 ) {
- if ( /^${indent}\t{5}(\w+)\=\{(.*?)\}?$/ ) {
- if ($2 ne '') { # einzeilige Definition - direkt auslesen
- $parse_obj_attr = $1;
- $parse_obj_attr_value = $2;
- if ($parse_obj_attr eq 'bothWays' && $parse_obj_attr_value eq '1') { # bidirectional rule
-# print_debug ("notice parsing rule state 5 - found bidir rule in line # $ln: $line", $debug_level, 2);
- &store_results();
- }
- if ($parse_obj_attr eq 'name' || $parse_obj_attr eq 'comment' ||
- $parse_obj_attr eq 'subsetName' ||
- $parse_obj_attr eq 'ref' || $parse_obj_attr eq 'refid' ) {
- &store_results();
- }
- } else {
- $parse_obj_attr = $1;
- if ($parse_obj_attr eq 'list' || $parse_obj_attr eq 'neglist') {
- $parse_obj_state = 6;
- }
- }
- }
- # Ende des state 5
- if ((/^${indent}\t{5}\}$/)) {
- $parse_obj_state = 4;
- undef ($parse_rule_field);
- }
-########## parse rule state 6: Zwischenebene mit wenig Configdaten ################################################
- if ( /^${indent}\t{6}(\w+)\{$/ && ($parse_obj_state == 5 || $parse_obj_state == 6) ) {
- $create_explicit_element_flag = 0;
- $parse_obj_attr_ext = $1;
- if (!defined($parse_rule_field)) { $parse_rule_field = ''; }
- if (!defined($parse_obj_attr_ext)) { $parse_obj_attr_ext = ''; }
- if (!(
- (
- ($parse_rule_field eq 'src' || $parse_rule_field eq 'dst') &&
- ($parse_obj_attr_ext eq 'NetEntry' || $parse_obj_attr_ext eq 'NetRef' ||
- $parse_obj_attr_ext eq 'NetSet')
- ) ||
- (
- ($parse_rule_field eq 'services') &&
-# ($parse_obj_attr_ext eq 'ServiceRef' || $parse_obj_attr_ext eq 'ServiceSet')
- ($parse_obj_attr_ext eq 'ServiceRef' || $parse_obj_attr_ext eq 'ServiceEntryTCP' ||
- $parse_obj_attr_ext eq 'ServiceEntryUDP' || $parse_obj_attr_ext eq 'ServiceEntryEcho' ||
- $parse_obj_attr_ext eq 'ServiceEntryOther'
- )
- )
- )) {
- if ( $parse_obj_attr_ext eq 'ConnRef' || $parse_obj_attr_ext eq 'ConnStd') {
- } else {
- print_debug ("state 6 error: found unknown or misplaced element $parse_obj_attr_ext in rule section $parse_rule_field" .
- "line: $ln, field: $parse_rule_field, attr_ext: $parse_obj_attr_ext", $debug_level, 1);
- }
- } else { # extracting rule details
- if ($explicit_flag && $parse_obj_attr_ext !~ /Ref$/) { # jetzt muss ein Basiselement angelegt werden
- $create_explicit_element_flag = 1;
- if ($parse_obj_attr_ext =~ /^ServiceEntry(.*?)$/ ) {
- # das IP-Protokoll extrahieren
- $create_explicit_element_proto = lc($1);
- }
- }
- $parse_obj_state = 7;
- }
- }
- # Ende des state 6
- if ( (/^${indent}\t{6}\}$/) ) {
- $parse_obj_state = 5;
-# undef ($parse_rule_field);
- $create_explicit_element_flag = 0;
- undef ($parse_obj_attr_ext); undef ($parse_obj_attr_ext_value);
- }
- ########## parse rule state 7: get element list details ################################################
- if ( /^${indent}\t{7}(\w+)\=\{\s?(.*?)\}$/ && $parse_obj_state==7 ) {
- if ($2 ne '') { # einzeilige Definition - direkt auslesen
- $parse_obj_attr = $1;
- $parse_obj_attr_value = $2;
- if ($create_explicit_element_flag) {
- if ((($parse_obj_attr eq 'addr' || $parse_obj_attr eq 'addr6') && $parse_obj_attr_value ne '0.0.0.0') || $parse_obj_attr eq 'portLimit') {
- # adding new base element to structure
- $scope =~ /^(\d+)_(.+?)_(.+?)_(.+?)_(.+?)_(.+?)/;
- my $clusterserver; if (defined($4)) { $clusterserver = "-$4"; } else { $clusterserver = ""; }
- $parse_obj_name = $parse_obj_attr_value;
- if (defined($create_explicit_element_proto)) { # only use this for svc nor for ip
- $parse_obj_name = $create_explicit_element_proto . '_' . $parse_obj_name;
- }
- $parse_obj_name =~ s/\//_/ ; # replace '/' with '_' in name
- $parse_obj_id = $parse_obj_name . '__uid__' . $parse_obj_name. $clusterserver;
- if ($parse_obj_attr eq 'addr' || $parse_obj_attr eq 'addr6') {
- $parse_obj_type = "netobj";
- &store_results(); # Element abspeichern
- # hier werden vereinfachend alle anderen Parameter (comment, etc) ignoriert
- } elsif ($parse_obj_attr eq 'portLimit'
- # || $parse_obj_attr eq 'botClientPort' || $parse_obj_attr eq 'topClientPort')
- ) {
- $parse_obj_type = "srvobj";
- &store_results(); # Element abspeichern
- if (defined($create_explicit_element_proto)) {
- $parse_obj_attr = 'type';
- $parse_obj_attr_value = $create_explicit_element_proto;
- &store_results(); # Protokoll hinterher
- undef($create_explicit_element_proto);
- }
- # hier werden vereinfachend alle anderen Parameter (Client_Port, Timeout) ignoriert
- }
- $parse_obj_type = "rules"; # und zurueck zum Regelparser
-
- # now storing the reference to the new element in the rule
- $parse_obj_attr = 'ref'; $parse_obj_attr_value = $parse_obj_name;
- &store_results();
- $parse_obj_attr = 'refid'; $parse_obj_attr_value = $parse_obj_id;
- &store_results();
- }
- } elsif ($parse_obj_attr eq 'ref' || $parse_obj_attr eq 'refid' ) {
- &store_results();
- }
- }
- }
-##########################################################################################################
- }
- } # end of while
- close (IN);
- print_debug ("$in_file closed.",$debug_level, 8);
-
- # check auf Vollstaendigkeit des Config-Files:
- if ($last_line =~ m/^\}(\n)?$/) { return 0; }
- else { return "ERROR: last line of config-file $in_file not correct: <$last_line>"; }
-}
-
-1;
-__END__
-
-=head1 NAME
-
-CACTUS::FWORCH::parser - Perl extension for fworch phion parser
-
-=head1 SYNOPSIS
-
- use CACTUS::FWORCH::import::phion;
-
-=head1 DESCRIPTION
-
-fworch Perl Module support for importing configs into fworch Database
-
-=head2 EXPORT
-
- ©_config_from_mgm_to_iso - transfer phion MC rangetree fw rules to fworch
- &parse_config - parse phion MC config rangetree
-
-=head1 SEE ALSO
-
- behind the door
-
-=head1 AUTHOR
-
- Cactus eSecurity, tmp@cactus.de
-
-=cut
diff --git a/roles/importer/files/importer/CACTUS/read_config.pm b/roles/importer/files/importer/CACTUS/read_config.pm
deleted file mode 100644
index 36d67b5f99..0000000000
--- a/roles/importer/files/importer/CACTUS/read_config.pm
+++ /dev/null
@@ -1,86 +0,0 @@
-package CACTUS::read_config;
-
-use strict;
-use warnings;
-use IO::File;
-require Exporter;
-our @ISA = qw(Exporter);
-
-our %EXPORT_TAGS = (
- 'basic' => [
- qw( &read_config )
- ]
-);
-
-our @EXPORT = ( @{ $EXPORT_TAGS{'basic'} } );
-our $VERSION = '1.2';
-
-############################################################
-# read one file into string
-############################################################
-sub read_file_into_string {
- my $conf_file = shift;
- my ($line, $lines);
-
- my $INFILE = new IO::File ("< $conf_file") or die "cannot open file $conf_file\n";
- $lines = '';
-
- while ($line = <$INFILE>) { $lines .= $line; }
- $INFILE->close;
- return $lines;
-}
-############################################################
-# read parameter from config file
-############################################################
-sub read_config {
- my $param = shift;
- my $confdir = '/usr/local/fworch/etc';
- my $result;
-
- my $global_conf_lines = &read_file_into_string ("$confdir/iso.conf");
- my $import_conf_lines = &read_file_into_string ("$confdir/import.conf");
- my $all_conf_lines = $global_conf_lines . '\n' . $import_conf_lines;
-
- my @config_lines = split (/\n/, $all_conf_lines);
-
- foreach my $line (@config_lines) {
- if ($line =~ /(.*?)\#/) { $line = $1; } # remove comments
- if ($line !~ /^$/ && $line =~ /^\s*$param\s+(.*?)\s*$/) {
- $result = $1;
-# print ("found matching config line for $param. Param: .$1.\n");
- }
- }
- if (!defined($result)) { print ("warning: config parameter $param neither found in global nor in import config file\n"); }
- return $result;
-}
-
-1;
-__END__
-
-=head1 NAME
-
-read_config - Perl extension for fworch
-
-=head1 SYNOPSIS
-
- use CACTUS::read_config;
-
-=head1 DESCRIPTION
-
-fworch Perl Module support for reading config files
-
-=head2 EXPORT
-
- Basic functions
- read_config(parameter to read from file)
-
-=head1 SEE ALSO
-
- behind the door
-
-
-=head1 AUTHOR
-
- Cactus eSecurity, tmp@cactus.de
-
-=cut
diff --git a/roles/importer/files/importer/azure2022ff/azure_base.py b/roles/importer/files/importer/azure2022ff/azure_base.py
deleted file mode 100644
index 113e58e1d7..0000000000
--- a/roles/importer/files/importer/azure2022ff/azure_base.py
+++ /dev/null
@@ -1 +0,0 @@
-azure_api_version_str = '?api-version=2022-07-01'
diff --git a/roles/importer/files/importer/azure2022ff/azure_getter.py b/roles/importer/files/importer/azure2022ff/azure_getter.py
deleted file mode 100644
index b7202cb756..0000000000
--- a/roles/importer/files/importer/azure2022ff/azure_getter.py
+++ /dev/null
@@ -1,105 +0,0 @@
-# library for API get functions
-import base64
-from typing import Dict
-from fwo_log import getFwoLogger
-import requests.packages
-import requests
-import json
-import fwo_globals
-from fwo_exception import FwLoginFailed
-
-
-def api_call(url, params = {}, headers = {}, data = {}, azure_jwt = '', show_progress=False, method='get'):
- logger = getFwoLogger()
- request_headers = {}
- if not 'Content-Type' in headers:
- request_headers = {'Content-Type': 'application/json'}
- for header_key in headers:
- request_headers[header_key] = headers[header_key]
- if azure_jwt != '':
- request_headers["Authorization"] = 'Bearer {azure_jwt}'.format(azure_jwt=azure_jwt)
- if request_headers['Content-Type'] == 'application/json':
- data=json.dumps(data)
- else: # login only
- data=data
-
- if method == "post":
- response = requests.post(url, params=params, data=data, headers=request_headers, verify=fwo_globals.verify_certs)
- elif method == "get":
- response = requests.get(url, params=params, headers=request_headers, verify=fwo_globals.verify_certs)
- else:
- raise Exception("unknown HTTP method found in azure_getter")
-
- # error handling:
- exception_text = ''
- if response is None:
- if 'password' in json.dumps(data):
- exception_text = "error while sending api_call containing credential information to url '" + \
- str(url)
- else:
- exception_text = "error while sending api_call to url '" + str(url) + "' with payload '" + json.dumps(
- data, indent=2) + "' and headers: '" + json.dumps(request_headers, indent=2)
- if not response.ok:
- exception_text = 'error code: {error_code}, error={error}'.format(error_code=response.status_code, error=response.content)
- #logger.error(response.content)
- if (len(response.content) == 0):
- exception_text = 'empty response content'
-
- if exception_text != '':
- raise Exception(exception_text)
-
- # no errors found
- body_json = response.json()
-
- if fwo_globals.debug_level > 5:
- if 'pass' in json.dumps(data):
- logger.debug("api_call containing credential information to url '" +
- str(url) + " - not logging query")
- else:
- logger.debug("api_call to url '" + str(url) + "' with payload '" + json.dumps(
- data, indent=2) + "' and headers: '" + json.dumps(request_headers, indent=2))
-
- return response.headers, body_json
-
-
-def login(azure_user, azure_password, tenant_id, client_id, client_secret):
- base_url = 'https://login.microsoftonline.com/{tenant_id}/oauth2/token'.format(tenant_id=tenant_id)
- try:
- headers, body = api_call(base_url, method="post",
- headers={'Content-Type': 'application/x-www-form-urlencoded'},
- data={
- "grant_type" : "client_credentials",
- "client_id": client_id,
- "client_secret": client_secret,
- "resource": "https://management.azure.com/",
- "username": str(base64.b64encode((azure_user).encode('utf-8')), 'utf-8'),
- "password": str(base64.b64encode((azure_password).encode('utf-8')), 'utf-8')
- })
- except Exception as e:
- raise FwLoginFailed("Azure login ERROR for client_id id=" + str(client_id) + " Message: " + str(e)) from None
-
- if body.get("access_token") == None: # leaving out payload as it contains pwd
- raise FwLoginFailed("Azure login ERROR for client_id=" + str(client_id) + " Message: " + str(e)) from None
-
- if fwo_globals.debug_level > 2:
- logger = getFwoLogger()
- logger.debug("Login successful. Received JWT: " + body["access_token"])
-
- return body["access_token"]
-
-
-def update_config_with_azure_api_call(azure_jwt, api_base_url, config, api_path, key, parameters={}, payload={}, show_progress=False, limit: int=1000, method="get"):
- offset = 0
- limit = 1000
- returned_new_data = True
-
- full_result = []
- #while returned_new_data:
- # parameters["offset"] = offset
- # parameters["limit"] = limit
- result = api_call(api_base_url + api_path, azure_jwt=azure_jwt, params=parameters, data=payload, show_progress=show_progress, method=method)[1]
- returned_new_data = len(result['value'])>0
- if returned_new_data:
- full_result.extend(result["value"])
-# offset += limit
- config.update({key: full_result})
diff --git a/roles/importer/files/importer/azure2022ff/azure_network.py b/roles/importer/files/importer/azure2022ff/azure_network.py
deleted file mode 100644
index 7180b453d9..0000000000
--- a/roles/importer/files/importer/azure2022ff/azure_network.py
+++ /dev/null
@@ -1,121 +0,0 @@
-from asyncio.log import logger
-from fwo_log import getFwoLogger
-from fwo_const import list_delimiter
-import ipaddress
-
-
-def normalize_nwobjects(full_config, config2import, import_id, jwt=None, mgm_id=None):
- nw_objects = []
- for obj_orig in full_config["networkObjects"]:
- nw_objects.append(parse_object(obj_orig, import_id, config2import, nw_objects))
- for obj_grp_orig in full_config["networkObjectGroups"]:
- obj_grp = extract_base_object_infos(obj_grp_orig, import_id, config2import, nw_objects)
- obj_grp["obj_typ"] = "group"
- obj_grp["obj_member_refs"], obj_grp["obj_member_names"] = parse_obj_group(obj_grp_orig, import_id, nw_objects, config2import)
- nw_objects.append(obj_grp)
- config2import['network_objects'] = nw_objects
-
-
-def extract_base_object_infos(obj_orig, import_id, config2import, nw_objects):
- obj = {}
-
- if "type" in obj_orig:
- obj["obj_name"] = obj_orig["name"]
- obj["obj_uid"] = obj_orig["id"]
- if 'description' in obj_orig:
- obj["obj_comment"] = obj_orig["description"]
- if 'etag' in obj_orig and not 'obj_comment' in obj:
- obj["obj_comment"] = obj_orig["etag"]
- obj['control_id'] = import_id
- return obj
-
-
-def parse_obj_group(orig_grp, import_id, nw_objects, config2import, id = None):
- refs = []
- names = []
- if "properties" in orig_grp:
- if 'ipAddresses' in orig_grp['properties']:
- for ip in orig_grp['properties']['ipAddresses']:
- new_obj = parse_object(add_network_object(config2import, ip=ip), import_id, config2import, nw_objects)
- names.append(new_obj['obj_name'])
- refs.append(new_obj['obj_uid'])
- nw_objects.append(new_obj)
- return list_delimiter.join(refs), list_delimiter.join(names)
-
-
-def parse_obj_list(ip_list, import_id, config, id):
- refs = []
- names = []
- for ip in ip_list:
- # TODO: lookup ip in network_objects and re-use
- ip_obj = {}
- ip_obj['obj_name'] = ip
- ip_obj['obj_uid'] = ip_obj['obj_name'] + "_" + id
- try:
- ipaddress.ip_network(ip)
- # valid ip
- ip_obj['obj_ip'] = ip
- except:
- # no valid ip - asuming azureTag
- ip_obj['obj_ip'] = '0.0.0.0/0'
- ip = '0.0.0.0/0'
- ip_obj['obj_name'] = "#"+ip_obj['obj_name']
- ip_obj['obj_type'] = 'simple'
- ip_obj['obj_typ'] = 'host'
- if "/" in ip:
- ip_obj['obj_typ'] = 'network'
-
- if "-" in ip: # ip range
- ip_obj['obj_typ'] = 'ip_range'
- ip_range = ip.split("-")
- ip_obj['obj_ip'] = ip_range[0]
- ip_obj['obj_ip_end'] = ip_range[1]
-
- ip_obj['control_id'] = import_id
-
- config.append(ip_obj)
- refs.append(ip_obj['obj_uid'])
- names.append(ip_obj['obj_name'])
- return list_delimiter.join(refs), list_delimiter.join(names)
-
-
-def parse_object(obj_orig, import_id, config2import, nw_objects):
- obj = extract_base_object_infos(obj_orig, import_id, config2import, nw_objects)
- if obj_orig["type"] == "network": # network
- obj["obj_typ"] = "network"
- cidr = obj_orig["value"].split("/")
- if str.isdigit(cidr[1]):
- obj['obj_ip'] = cidr[0] + "/" + cidr[1]
- else: # not real cidr (netmask after /)
- obj['obj_ip'] = cidr[0] + "/" + str(IPAddress(cidr[1]).netmask_bits())
- elif obj_orig["type"] == "host": # host
- obj["obj_typ"] = "host"
- obj["obj_ip"] = obj_orig["ip"]
- if obj_orig["ip"].find(":") != -1: # ipv6
- obj["obj_ip"] += "/128"
- else: # ipv4
- obj["obj_ip"] += "/32"
- elif obj_orig["type"] == "ip_range": # ip range
- obj['obj_typ'] = 'ip_range'
- ip_range = obj_orig['ip'].split("-")
- obj['obj_ip'] = ip_range[0]
- obj['obj_ip_end'] = ip_range[1]
- elif obj_orig["type"] == "FQDN": # fully qualified domain name
- obj['obj_typ'] = 'network'
- obj['obj_ip'] = "0.0.0.0/0"
- obj['obj_uid'] = obj_orig["id"]
- else: # unknown type
- obj["obj_name"] = obj["obj_name"] + " [not supported]"
- obj['obj_typ'] = 'network'
- obj['obj_ip'] = "0.0.0.0/0"
- obj['control_id'] = import_id
-
- return obj
-
-
-def add_network_object(config2import, ip=None):
- if "-" in str(ip):
- type = 'ip_range'
- else:
- type = 'host'
- return {'ip': ip, 'name': ip, 'id': ip, 'type': type}
diff --git a/roles/importer/files/importer/azure2022ff/azure_rule.py b/roles/importer/files/importer/azure2022ff/azure_rule.py
deleted file mode 100644
index 8ac6ab9266..0000000000
--- a/roles/importer/files/importer/azure2022ff/azure_rule.py
+++ /dev/null
@@ -1,100 +0,0 @@
-from azure_service import parse_svc_list
-from azure_network import parse_obj_list
-from fwo_log import getFwoLogger
-import hashlib
-import base64
-
-
-def make_hash_sha256(o):
- hasher = hashlib.sha256()
- hasher.update(repr(make_hashable(o)).encode())
- return base64.b64encode(hasher.digest()).decode()
-
-
-def make_hashable(o):
- if isinstance(o, (tuple, list)):
- return tuple((make_hashable(e) for e in o))
-
- if isinstance(o, dict):
- return tuple(sorted((k,make_hashable(v)) for k,v in o.items()))
-
- if isinstance(o, (set, frozenset)):
- return tuple(sorted(make_hashable(e) for e in o))
-
- return o
-
-# rule_access_scope_v4 = ['rules_global_header_v4',
-# 'rules_adom_v4', 'rules_global_footer_v4']
-# rule_access_scope_v6 = ['rules_global_header_v6',
-# 'rules_adom_v6', 'rules_global_footer_v6']
-# rule_access_scope = rule_access_scope_v6 + rule_access_scope_v4
-# rule_nat_scope = ['rules_global_nat', 'rules_adom_nat']
-# rule_scope = rule_access_scope + rule_nat_scope
-
-
-def normalize_access_rules(full_config, config2import, import_id, mgm_details={}):
- rules = []
-
- nw_obj_names = []
- for o in config2import['network_objects']:
- nw_obj_names.append(o["obj_name"])
-
- for device in full_config["devices"]:
- rule_number = 0
- for policy_name in full_config['devices'].keys():
- for rule_prop in full_config['devices'][policy_name]['rules']:
- rule_coll_container = rule_prop['properties']
- if 'ruleCollections' in rule_coll_container:
- for rule_coll in rule_coll_container['ruleCollections']:
- if 'ruleCollectionType' in rule_coll and rule_coll['ruleCollectionType'] == 'FirewallPolicyFilterRuleCollection':
- rule_action = "accept"
- if rule_coll['action']['type'] == 'Deny':
- rule_action = "deny"
-
- for rule_orig in rule_coll['rules']:
- rule = {'rule_src': 'any', 'rule_dst': 'any', 'rule_svc': 'any',
- 'rule_src_refs': 'any_obj_placeholder', 'rule_dst_refs': 'any_obj_placeholder',
- 'rule_svc_refs': 'any_svc_placeholder'}
- rule['rulebase_name'] = policy_name
- rule["rule_name"] = rule_orig["name"]
- rule['rule_type'] = "access"
- rule['rule_num'] = rule_number
- rule['rule_installon'] = None
- rule['parent_rule_id'] = None
- rule['rule_time'] = None
- rule['rule_implied'] = False
- rule["rule_comment"] = None
- rule["rule_action"] = rule_action
- rule["rule_track"] = "None"
- rule["rule_disabled"] = False
- rule["rule_uid"] = make_hash_sha256(rule) # generate uid from invariable rule parts without import_id
- rule['control_id'] = import_id
-
- if "sourceAddresses" in rule_orig:
- rule['rule_src_refs'], rule["rule_src"] = parse_obj_list(rule_orig["sourceAddresses"], import_id, config2import['network_objects'], rule["rule_uid"])
- if "destinationAddresses" in rule_orig:
- undefObjects = []
-
- for obj in rule_orig['destinationAddresses']:
- if "obj_name" in obj:
- if obj["obj_name"] not in nw_obj_names:
- undefObjects.append(obj["obj_name"])
- elif obj not in nw_obj_names: # just a string with obj name
- undefObjects.append(obj)
- # rule['rule_src_refs'], rule["rule_src"] = parse_obj_list(undefObjects, import_id, config2import['network_objects'], rule["rule_uid"])
- rule['rule_dst_refs'], rule["rule_dst"] = parse_obj_list(rule_orig["destinationAddresses"], import_id, config2import['network_objects'], rule["rule_uid"])
- if "destinationPorts" in rule_orig:
- rule['rule_svc_refs'], rule['rule_svc'] = parse_svc_list(rule_orig["destinationPorts"], rule_orig["ipProtocols"], import_id, config2import['service_objects'], rule["rule_uid"])
- # TODO:
- # ipProtocols!!
- # sourceIpGroups
- # destinationIpGroups
- # destinationFqdns
- rule["rule_src_neg"] = False
- rule["rule_dst_neg"] = False
- rule["rule_svc_neg"] = False
-
- rule_number += 1
- rules.append(rule)
-
- config2import['rules'] = rules
diff --git a/roles/importer/files/importer/azure2022ff/azure_service.py b/roles/importer/files/importer/azure2022ff/azure_service.py
deleted file mode 100644
index d537e314a5..0000000000
--- a/roles/importer/files/importer/azure2022ff/azure_service.py
+++ /dev/null
@@ -1,106 +0,0 @@
-import random
-from fwo_const import list_delimiter
-
-
-def normalize_svcobjects(full_config, config2import, import_id):
- svc_objects = []
- for svc_orig in full_config["serviceObjects"]:
- svc_objects.append(parse_svc(svc_orig, import_id))
- for svc_grp_orig in full_config["serviceObjectGroups"]:
- svc_grp = extract_base_svc_infos(svc_grp_orig, import_id)
- svc_grp["svc_typ"] = "group"
- svc_grp["svc_member_refs"] , svc_grp["svc_member_names"] = parse_svc_group(svc_grp_orig, import_id, svc_objects)
- svc_objects.append(svc_grp)
- config2import['service_objects'] = svc_objects
-
-
-def extract_base_svc_infos(svc_orig, import_id):
- svc = {}
- if "id" in svc_orig:
- svc["svc_uid"] = svc_orig["id"]
- else:
- svc["svc_uid"] = svc_orig["protocol"]
- if "port" in svc_orig:
- svc["svc_uid"] += "_" + svc_orig["port"]
- if "name" in svc_orig:
- svc["svc_name"] = svc_orig["name"]
- else:
- svc["svc_name"] = svc_orig["protocol"]
- if "port" in svc_orig:
- svc["svc_name"] += "_" + svc_orig["port"]
- if "svc_comment" in svc_orig:
- svc["svc_comment"] = svc_orig["comment"]
- svc["svc_timeout"] = None
- svc["svc_color"] = None
- svc["control_id"] = import_id
- return svc
-
-
-def parse_svc(orig_svc, import_id):
- svc = extract_base_svc_infos(orig_svc, import_id)
- svc["svc_typ"] = "simple"
- parse_port(orig_svc, svc)
- if orig_svc["type"] == "ProtocolPortObject":
- if orig_svc["protocol"] == "TCP":
- svc["ip_proto"] = 6
- elif orig_svc["protocol"] == "UDP":
- svc["ip_proto"] = 17
- elif orig_svc["protocol"] == "ESP":
- svc["ip_proto"] = 50
- else:
- svc["svc_name"] += " [Protocol \"" + orig_svc["protocol"] + "\" not supported]"
- # TODO Icmp
- # TODO add all protocols
- elif orig_svc["type"] == "PortLiteral":
- svc["ip_proto"] = orig_svc["protocol"]
- else:
- svc["svc_name"] += " [Not supported]"
- return svc
-
-
-def parse_port(orig_svc, svc):
- if "port" in orig_svc:
- if orig_svc["port"].find("-") != -1: # port range
- port_range = orig_svc["port"].split("-")
- svc["svc_port"] = port_range[0]
- svc["svc_port_end"] = port_range[1]
- else: # single port
- svc["svc_port"] = orig_svc["port"]
- svc["svc_port_end"] = None
-
-
-def parse_svc_list(ports, ip_protos, import_id, svc_objects, id = None):
- refs = []
- names = []
- for port in ports:
- for ip_proto in ip_protos:
- # TODO: lookup port in svc_objects and re-use
- svc = {}
-
-
-
- if id == None:
- id = str(random.random())
-
- svc['svc_name'] = ip_proto + "_" + port
-
- svc['svc_uid'] = svc['svc_name'] + "_" + id
- svc['svc_port'] = port
- svc['svc_port_end'] = port
- svc['svc_typ'] = 'simple'
- if ip_proto == "TCP":
- svc["ip_proto"] = 6
- elif ip_proto == "UDP":
- svc["ip_proto"] = 17
- elif ip_proto == "ESP":
- svc["ip_proto"] = 50
- elif ip_proto == "ICMP":
- svc["ip_proto"] = 1
- else:
- svc["svc_name"] += " [Protocol \"" + ip_proto + "\" not supported]"
- svc['control_id'] = import_id
-
- svc_objects.append(svc)
- refs.append(svc['svc_uid'])
- names.append(svc['svc_name'])
- return list_delimiter.join(refs), list_delimiter.join(names)
diff --git a/roles/importer/files/importer/azure2022ff/fwcommon.py b/roles/importer/files/importer/azure2022ff/fwcommon.py
deleted file mode 100644
index 0e77c454a8..0000000000
--- a/roles/importer/files/importer/azure2022ff/fwcommon.py
+++ /dev/null
@@ -1,134 +0,0 @@
-import sys
-from common import importer_base_dir
-sys.path.append(importer_base_dir + '/azure2022ff')
-from azure_service import normalize_svcobjects
-from azure_rule import normalize_access_rules
-from azure_network import normalize_nwobjects
-from azure_getter import login, update_config_with_azure_api_call
-from fwo_log import getFwoLogger
-from azure_base import azure_api_version_str
-
-def has_config_changed(full_config, mgm_details, force=False):
- # dummy - may be filled with real check later on
- return True
-
-
-def get_config(config2import, full_config, current_import_id, mgm_details, limit=1000, force=False, jwt=''):
- logger = getFwoLogger()
- if full_config == {}: # no native config was passed in, so getting it from Azzure
- parsing_config_only = False
- else:
- parsing_config_only = True
-
- if not parsing_config_only: # no native config was passed in, so getting it from Azure
- azure_client_id = mgm_details["import_credential"]['cloudClientId']
- azure_client_secret = mgm_details["import_credential"]['cloudClientSecret']
- azure_password = mgm_details["import_credential"]['secret']
- azure_user = mgm_details["import_credential"]['user']
- azure_tenant_id = mgm_details['cloudTenantId']
- azure_subscription_id = mgm_details['cloudSubscriptionId']
- azure_resource_group = mgm_details['configPath']
- azure_api_worker_base_url = 'https://management.azure.com/subscriptions/{subscription_id}/'.format(subscription_id=azure_subscription_id)
-
- full_config["networkObjects"] = []
- full_config["networkObjectGroups"] = []
-
- full_config["serviceObjects"] = []
- full_config["serviceObjectGroups"] = []
-
- full_config["userObjects"] = []
- full_config["userObjectGroups"] = []
-
- # login
- azure_jwt = login(azure_user, azure_password, azure_tenant_id, azure_client_id, azure_client_secret)
- if azure_jwt == None or azure_jwt == "":
- logger.error('Did not succeed in logging in to Azure API, no jwt returned.')
- return 1
-
- # get objects:
- # network objects
- # network groups
- api_path = 'resourceGroups/{resourceGroupName}/providers/Microsoft.Network/ipGroups{azure_api_version_str}'.format(
- resourceGroupName=azure_resource_group, azure_api_version_str=azure_api_version_str)
- update_config_with_azure_api_call(azure_jwt, azure_api_worker_base_url, full_config,
- api_path, "networkObjectGroups")
-
- # network services
- # network serivce groups
-
- # users
-
- # get rules
- full_config.update({'devices': {}})
- for device in mgm_details["devices"]:
- azure_policy_name = device['name']
- full_config['devices'].update({ device['name']: {} })
-
- api_path = 'resourceGroups/{resourceGroupName}/providers/Microsoft.Network/firewallPolicies/{firewallPolicyName}/ruleCollectionGroups{azure_api_version_str}'.format(
- resourceGroupName=azure_resource_group, firewallPolicyName=azure_policy_name, azure_api_version_str=azure_api_version_str)
-
- update_config_with_azure_api_call(azure_jwt, azure_api_worker_base_url, full_config['devices'][device['name']], api_path, "rules")
- ##azure_rule.getNatPolicy(sessionId, azure_api_worker_base_url, full_config, domain, device, limit) TODO
-
- # extract objects from rules
- for device in mgm_details["devices"]:
- azure_policy_name = device['name']
- for policy_name in full_config['devices'].keys():
- extract_nw_objects(policy_name, full_config)
- extract_svc_objects(policy_name, full_config)
- extract_user_objects(policy_name, full_config)
-
- # now we normalize relevant parts of the raw config and write the results to config2import dict
-
- # normalize_network_data(full_config, config2import, mgm_details)
-
- # azure_user.normalize_users(
- # full_config, config2import, current_import_id, user_scope)
- normalize_nwobjects(full_config, config2import, current_import_id, jwt=jwt, mgm_id=mgm_details['id'])
- normalize_svcobjects(full_config, config2import, current_import_id)
-
- any_nw_svc = {"svc_uid": "any_svc_placeholder", "svc_name": "Any", "svc_comment": "Placeholder service.",
- "svc_typ": "simple", "ip_proto": -1, "svc_port": 0, "svc_port_end": 65535, "control_id": current_import_id}
- any_nw_object = {"obj_uid": "any_obj_placeholder", "obj_name": "Any", "obj_comment": "Placeholder object.",
- "obj_typ": "network", "obj_ip": "0.0.0.0/0", "control_id": current_import_id}
- config2import["service_objects"].append(any_nw_svc)
- config2import["network_objects"].append(any_nw_object)
-
- normalize_access_rules(full_config, config2import, current_import_id, mgm_details=mgm_details)
- # azure_rule.normalize_nat_rules(
- # full_config, config2import, current_import_id, jwt=jwt)
- # azure_network.remove_nat_ip_entries(config2import)
- return 0
-
-
-def extract_nw_objects(rule, config):
- pass
-
-
-def extract_svc_objects(rule, config):
- pass
-
-
-def extract_user_objects(rule, config):
- pass
-
-
-# def getDevices(azure_jwt, api_url, config, limit, devices):
-# https://management.azure.com/subscriptions/{{ _.subscriptionId }}/providers/Microsoft.Network/applicationGateways?api-version=2022-05-01
-
-# but this does not return firewalls!
-
-# logger = getFwoLogger()
-# # get all devices
-# config["devices"] = update_config_with_azure_api_call(azure_jwt, api_url, "fmc_config/v1/domain/" + "/devices/devicerecords", parameters={"expanded": True}, limit=limit)
-# # filter for existent devices
-# for cisco_api_device in config["devices"]:
-# found = False
-# for device in devices:
-# if device["name"] == cisco_api_device["name"] or device["name"] == cisco_api_device["id"]:
-# found = True
-# break
-# # remove device if not in fwo api
-# if found == False:
-# config["devices"].remove(cisco_api_device)
-# logger.info("Device \"" + cisco_api_device["name"] + "\" was found but it is not registered in FWO. Ignoring it.")
diff --git a/roles/importer/files/importer/checkpointR8x/cp_const.py b/roles/importer/files/importer/checkpointR8x/cp_const.py
deleted file mode 100644
index 450ea7b77f..0000000000
--- a/roles/importer/files/importer/checkpointR8x/cp_const.py
+++ /dev/null
@@ -1,45 +0,0 @@
-details_level = "standard"
-details_level_objects = "standard"
-details_level_group_objects = "full"
-use_object_dictionary = True
-with_hits = True
-
-dummy_ip = '0.0.0.0/32'
-
-# the following is the static across all installations unique any obj uid
-# cannot fetch the Any object via API (<=1.7) at the moment
-# therefore we have a workaround adding the object manually (as svc and nw)
-any_obj_uid = "97aeb369-9aea-11d5-bd16-0090272ccb30"
-none_obj_uid = "97aeb36a-9aea-11d5-bd16-0090272ccb30"
-internet_obj_uid = 'f99b1488-7510-11e2-8668-87656188709b'
-# todo: read this from config (from API 1.6 on it is fetched)
-
-original_obj_uid = "85c0f50f-6d8a-4528-88ab-5fb11d8fe16c"
-# used for nat only (both svc and nw obj)
-
-nw_obj_table_names = [
- 'hosts', 'networks', 'groups', 'address-ranges', 'multicast-address-ranges', 'groups-with-exclusion',
- 'gateways-and-servers', 'simple-gateways',
- 'dns-domains', 'updatable-objects-repository-content',
- 'interoperable-devices', 'security-zones', 'Global', 'access-roles', 'updatable-objects'
-]
-
-# simple as in: no groups
-simple_svc_obj_types = ['services-tcp', 'services-udp', 'services-dce-rpc', 'services-rpc', 'services-other',
- 'services-icmp', 'services-icmp6', 'services-sctp', 'services-gtp', 'Global']
-group_svc_obj_types = ['service-groups', 'application-site-categories', 'application-sites']
-
-svc_obj_table_names = group_svc_obj_types + simple_svc_obj_types + [ 'CpmiAnyObject' ]
-# usr_obj_table_names : do not exist yet - not fetchable via API
-
-simple_user_obj_types = ['users']
-
-api_obj_types = nw_obj_table_names + svc_obj_table_names + simple_user_obj_types # all obj table names to look at during import
-
-obj_types_full_fetch_needed = ['groups', 'groups-with-exclusion', 'updatable-objects', 'gateways-and-servers', 'services-other'] + group_svc_obj_types
-
-cp_specific_object_types = [ # used for fetching enrichment data via "get object" separately (no specific API call)
- 'simple-gateway', 'simple-cluster', 'CpmiVsClusterNetobj', 'CpmiVsxClusterNetobj', 'CpmiVsxClusterMember', 'CpmiVsNetobj',
- 'CpmiAnyObject', 'CpmiVsxNetobj', 'CpmiClusterMember', 'CpmiGatewayPlain', 'CpmiHostCkp', 'CpmiGatewayCluster', 'checkpoint-host',
- 'cluster-member', 'CpmiVoipSipDomain', 'CpmiVoipMgcpDomain'
-]
diff --git a/roles/importer/files/importer/checkpointR8x/cp_getter.py b/roles/importer/files/importer/checkpointR8x/cp_getter.py
deleted file mode 100644
index 8779d57f4b..0000000000
--- a/roles/importer/files/importer/checkpointR8x/cp_getter.py
+++ /dev/null
@@ -1,467 +0,0 @@
-# library for API get functions
-from asyncio.log import logger
-import json
-import re
-import requests, requests.packages
-import time
-from common import FwLoginFailed
-from fwo_log import getFwoLogger
-import fwo_globals
-import cp_network
-import cp_const
-
-
-def cp_api_call(url, command, json_payload, sid, show_progress=False):
- url += command
- request_headers = {'Content-Type' : 'application/json'}
- if sid != '': # only not set for login
- request_headers.update({'X-chkp-sid' : sid})
-
- if fwo_globals.debug_level>4:
- logger.debug(f"api call '{command}'")
- if fwo_globals.debug_level>9:
- if command!='login': # do not log passwords
- logger.debug("json_payload: " + str(json_payload) )
-
- try:
- r = requests.post(url, json=json_payload, headers=request_headers, verify=fwo_globals.verify_certs)
- except requests.exceptions.RequestException as e:
- raise Exception("error, url: " + str(url))
-
- if r is None:
- if 'password' in json.dumps(json_payload):
- exception_text = "\nerror while sending api_call containing credential information to url '" + str(url)
- else:
- exception_text = "\nerror while sending api_call to url '" + str(url) + "' with payload '" + json.dumps(json_payload, indent=2) + "' and headers: '" + json.dumps(request_headers, indent=2)
- raise Exception (exception_text)
- if show_progress:
- print ('.', end='', flush=True)
-
- try:
- json_response = r.json()
- except:
- raise Exception("checkpointR8x:api_call: response is not in valid json format: " + r.text)
- return json_response
-
-
-def login(user, password, api_host, api_port, domain):
- logger = getFwoLogger()
- payload = {'user': user, 'password': password}
- if domain is not None and domain != '':
- payload.update({'domain': domain})
- base_url = 'https://' + api_host + ':' + str(api_port) + '/web_api/'
- if int(fwo_globals.debug_level)>2:
- logger.debug("auto-discover - login to url " + base_url + " with user " + user)
- response = cp_api_call(base_url, 'login', payload, '')
- if "sid" not in response:
- exception_text = "\ngetter ERROR: did not receive a sid during login, " + \
- "api call: api_host: " + str(api_host) + ", api_port: " + str(api_port) + ", base_url: " + str(base_url) + \
- ", ssl_verification: " + str(fwo_globals.verify_certs)
- raise FwLoginFailed(exception_text)
- return response["sid"]
-
-
-def logout(url, sid):
- logger = getFwoLogger()
- if int(fwo_globals.debug_level)>2:
- logger.debug("logout from url " + url)
- response = cp_api_call(url, 'logout', {}, sid)
- return response
-
-
-def get_api_url(sid, api_host, api_port, user, base_url, limit, test_version, ssl_verification, debug_level=0):
- logger = getFwoLogger()
-
- v_url = ''
- if test_version == 'off':
- v_url = base_url
- else:
- api_versions = cp_api_call(base_url, 'show-api-versions', {}, sid)
- api_version = api_versions["current-version"]
- api_supported = api_versions["supported-versions"]
-
- if debug_level>3:
- logger.debug ("current version: " + api_version + "; supported versions: "+ ', '.join(api_supported) + "; limit:"+ str(limit) )
- logger.debug ("getter - login:" + user + "; sid:" + sid )
- if re.search(r'^\d+[\.\d+]+$', test_version) or re.search(r'^\d+$', test_version):
- if test_version in api_supported :
- v_url = base_url + 'v' + test_version + '/'
- else:
- raise Exception("api version " + test_version + " not supported")
- else:
- logger.debug ("not a valid version")
- raise Exception("\"" + test_version +"\" - not a valid version")
- logger.debug ("test_version: " + test_version + " - url: "+ v_url)
- return v_url
-
-
-def set_api_url(base_url,testmode,api_supported,hostname, debug_level=0):
- logger = getFwoLogger()
- url = ''
- if testmode == 'off':
- url = base_url
- else:
- if re.search(r'^\d+[\.\d+]+$', testmode) or re.search(r'^\d+$', testmode):
- if testmode in api_supported :
- url = base_url + 'v' + testmode + '/'
- else:
- raise Exception("api version " + testmode + " is not supported by the manager " + hostname + " - Import is canceled")
- else:
- logger.debug ("not a valid version")
- raise Exception("\"" + testmode +"\" - not a valid version")
- logger.debug ("testmode: " + testmode + " - url: "+ url)
- return url
-
-
-def get_changes(sid,api_host,api_port,fromdate):
- logger = getFwoLogger()
- payload = {'from-date' : fromdate, 'details-level' : 'uid'}
- logger.debug ("payload: " + json.dumps(payload))
- base_url = 'https://' + api_host + ':' + str(api_port) + '/web_api/'
- task_id = cp_api_call(base_url, 'show-changes', payload, sid)
-
- logger.debug ("task_id: " + json.dumps(task_id))
- sleeptime = 1
- status = 'in progress'
- while (status == 'in progress'):
- time.sleep(sleeptime)
- tasks = cp_api_call(base_url, 'show-task', task_id, sid)
- if 'tasks' in tasks:
- for task in tasks['tasks']:
- if fwo_globals.debug_level>5:
- logger.debug ("task: " + json.dumps(task))
- if 'status' in task:
- status = task['status']
- if 'succeeded' in status:
- for detail in task['task-details']:
- if detail['changes']:
- logger.debug ("status: " + status + " -> changes found")
- return 1
- else:
- logger.debug ("status: " + status + " -> but no changes found")
- elif status == 'failed':
- logger.debug ("show-changes - status: failed -> no changes found")
- elif status == 'in progress':
- logger.debug ("status: in progress")
- else:
- logger.error ("unknown status: " + status)
- return -1
- else:
- logger.error ("no status in task")
- return -1
- sleeptime += 2
- if sleeptime > 40:
- logger.error ("task took too long, aborting")
- return -1
- return 0
-
-
-def get_layer_from_api_as_dict (api_v_url, sid, show_params_rules, layerUid=None, layerName=None, access_type='access', collection_type='rulebase', nativeConfig={}):
- # access_type: access / nat
- # collection_type: rulebase / layer
- logger = getFwoLogger()
- if layerUid is not None:
- current_layer_json = { "layerid": layerUid, "layerchunks": [] }
- elif layerName is not None:
- current_layer_json = { "layername": layerName, "layerchunks": [] }
- else:
- logger.exception('must provide either layerUid or layerName')
-
- current=0
- total=current+1
- while (current5:
- logger.debug ( "found inline layer " + inline_layer_uid )
- inline_layer = get_layer_from_api_as_dict (api_v_url, sid, show_params_rules, layerUid=inline_layer_uid, access_type=access_type, collection_type=collection_type, nativeConfig=nativeConfig)
- rulebase['rulebase'][rulebase_idx+1:rulebase_idx+1] = inline_layer['layerchunks'] #### insert inline layer here
- rulebase_idx += len(inline_layer['layerchunks'])
-
- if 'name' in rule and rule['name'] == "Placeholder for domain rules":
- logger.debug ("getter - found domain rules reference with uid " + rule["uid"])
- rulebase_idx += 1
-
-
-def get_nat_rules_from_api_as_dict (api_v_url, sid, show_params_rules, nativeConfig={}):
- logger = getFwoLogger()
- nat_rules = { "nat_rule_chunks": [] }
- current=0
- total=current+1
- while (current5:
- logger.debug("result:\n" + json.dumps(top_ruleset_json, indent=2))
- return top_ruleset_json
-
-
-# def createUidIndex(array, key='uid'):
-# """
-# Create an index for the array of dictionaries based on the specified key.
-
-# :param array: List of dictionaries
-# :param key: The key to index the dictionaries by
-# :return: A dictionary with key values mapped to dictionaries
-# """
-# uidIndex = {item[key]: item for item in array}
-# return uidIndex
-
-
-def findElementByUid(array, uid):
- for el in array:
- if 'uid' in el and el['uid']==uid:
- return el
- return None
-
- # if not hasattr(findElementByUid, "uidIndex"):
- # # Create the index on the first call
- # findElementByUid.uidIndex = createUidIndex(array)
-
- # # Look up the uid in the index
- # return findElementByUid.uidIndex.get(uid)
-
-
-def resolveRefFromObjectDictionary(id, objDict, nativeConfig={}, sid='', base_url='', rule4debug={}):
- matchedObj = findElementByUid(objDict, id)
-
-
- if matchedObj is None: # object not in dict - neet to fetch it from API
- logger.warning(f"did not find object with uid {id} in object dictionary")
- return None
- else:
- # there are some objects (at least CpmiVoipSipDomain) which are not API-gettable with show-objects (only with show-object "UID")
- # these must be added to the (network) objects tables
- if matchedObj['type'] in ['CpmiVoipSipDomain', 'CpmiVoipMgcpDomain']:
- logger.info(f"adding voip domain '{matchedObj['name']}' object manually, because it is not retrieved by show objects API command")
- if 'object_tables' in nativeConfig:
- nativeConfig['object_tables'].append({
- "object_type": "hosts", "object_chunks": [ {
- "objects": [ {
- 'uid': matchedObj['uid'], 'name': matchedObj['name'], 'color': matchedObj['color'],
- 'type': matchedObj['type']
- } ] } ] } )
- else:
- logger.warning(f"found no existing object_tables while adding voip domain '{matchedObj['name']}' object")
-
- return matchedObj
-
-
-# resolving all uid references using the object dictionary
-# dealing with a single chunk
-def resolveRefListFromObjectDictionary(rulebase, value, objDict={}, nativeConfig={}, location='network', sid='', base_url=''):
- if 'objects-dictionary' in rulebase:
- objDict = rulebase['objects-dictionary']
- if isinstance(rulebase, list): # found a list of rules
- for rule in rulebase:
- if value in rule:
- valueList = []
- if isinstance(rule[value], str): # assuming single uid
- rule[value] = resolveRefFromObjectDictionary(rule[value], objDict, nativeConfig=nativeConfig, sid=sid, base_url=base_url, rule4debug=rule)
- else:
- if 'type' in rule[value]: # e.g. track
- rule[value] = resolveRefFromObjectDictionary(rule[value]['type'], objDict, nativeConfig=nativeConfig, sid=sid, base_url=base_url, rule4debug=rule)
- else: # assuming list of rules
- for id in rule[value]:
- valueList.append(resolveRefFromObjectDictionary(id, objDict, nativeConfig=nativeConfig, sid=sid, base_url=base_url, rule4debug=rule))
- rule[value] = valueList # replace ref list with object list
- if 'rulebase' in rule:
- resolveRefListFromObjectDictionary(rule['rulebase'], value, objDict=objDict, nativeConfig=nativeConfig, sid=sid, base_url=base_url)
- elif 'rulebase' in rulebase:
- resolveRefListFromObjectDictionary(rulebase['rulebase'], value, objDict=objDict, nativeConfig=nativeConfig, sid=sid, base_url=base_url)
-
-
-def getObjectDetailsFromApi(uid_missing_obj, sid='', apiurl=''):
- logger.debug(f"getting {uid_missing_obj} from API")
-
- show_params_host = {'details-level':'full','uid':uid_missing_obj} # need to get the full object here
- try:
- obj = cp_api_call(apiurl, 'show-object', show_params_host, sid)
- except Exception as e:
- logger.exception(f"error while trying to get details for object with uid {uid_missing_obj}: {e}")
- return None
- else:
- if obj is not None:
- if 'object' in obj:
- obj = obj['object']
- if (obj['type'] == 'CpmiAnyObject'):
- if (obj['name'] == 'Any'):
- return { "object_type": "hosts", "object_chunks": [ {
- "objects": [ {
- 'uid': obj['uid'], 'name': obj['name'], 'color': obj['color'],
- 'comments': 'any nw object checkpoint (hard coded)',
- 'type': 'network', 'ipv4-address': '0.0.0.0/0'
- } ] } ] }
- elif (obj['name'] == 'None'):
- return { "object_type": "hosts", "object_chunks": [ {
- "objects": [ {
- 'uid': obj['uid'], 'name': obj['name'], 'color': obj['color'],
- 'comments': 'any nw object checkpoint (hard coded)',
- 'type': 'group'
- } ] } ] }
- elif (obj['type'] in [ 'simple-gateway', obj['type'], 'CpmiGatewayPlain', obj['type'] == 'interop' ]):
- return { "object_type": "hosts", "object_chunks": [ {
- "objects": [ {
- 'uid': obj['uid'], 'name': obj['name'], 'color': obj['color'],
- 'comments': obj['comments'], 'type': 'host', 'ipv4-address': cp_network.get_ip_of_obj(obj),
- } ] } ] }
- elif obj['type'] == 'multicast-address-range':
- return {"object_type": "hosts", "object_chunks": [ {
- "objects": [ {
- 'uid': obj['uid'], 'name': obj['name'], 'color': obj['color'],
- 'comments': obj['comments'], 'type': 'host', 'ipv4-address': cp_network.get_ip_of_obj(obj),
- } ] } ] }
- elif (obj['type'] in ['CpmiVsClusterMember', 'CpmiVsxClusterMember', 'CpmiVsxNetobj']):
- return {"object_type": "hosts", "object_chunks": [ {
- "objects": [ {
- 'uid': obj['uid'], 'name': obj['name'], 'color': obj['color'],
- 'comments': obj['comments'], 'type': 'host', 'ipv4-address': cp_network.get_ip_of_obj(obj),
- } ] } ] }
- elif (obj['type'] == 'Global'):
- return {"object_type": "hosts", "object_chunks": [ {
- "objects": [ {
- 'uid': obj['uid'], 'name': obj['name'], 'color': obj['color'],
- 'comments': obj['comments'], 'type': 'host', 'ipv4-address': '0.0.0.0/0',
- } ] } ] }
- elif (obj['type'] in [ 'updatable-object', 'CpmiVoipSipDomain', 'CpmiVoipMgcpDomain' ]):
- return {"object_type": "hosts", "object_chunks": [ {
- "objects": [ {
- 'uid': obj['uid'], 'name': obj['name'], 'color': obj['color'],
- 'comments': obj['comments'], 'type': 'group'
- } ] } ] }
- elif (obj['type'] in ['Internet', 'security-zone']):
- return {"object_type": "hosts", "object_chunks": [ {
- "objects": [ {
- 'uid': obj['uid'], 'name': obj['name'], 'color': obj['color'],
- 'comments': obj['comments'], 'type': 'network', 'ipv4-address': '0.0.0.0/0',
- } ] } ] }
- elif (obj['type'] == 'access-role'):
- return obj
- elif obj['type'] in cp_const.api_obj_types: # standard objects with proper ip
- return obj
- else:
- logger.warning ( "missing nw obj of unexpected type '" + obj['type'] + "': " + uid_missing_obj )
- else:
- if 'code' in obj:
- logger.warning("broken ref in CP DB uid=" + uid_missing_obj + ": " + obj['code'])
- else:
- logger.warning("broken ref in CP DB uid=" + uid_missing_obj)
- else:
- logger.warning("got 'None' from CP API for uid=" + uid_missing_obj)
diff --git a/roles/importer/files/importer/checkpointR8x/cp_network.py b/roles/importer/files/importer/checkpointR8x/cp_network.py
deleted file mode 100644
index 8ddb233b7e..0000000000
--- a/roles/importer/files/importer/checkpointR8x/cp_network.py
+++ /dev/null
@@ -1,292 +0,0 @@
-from fwo_log import getFwoLogger
-import json
-import cp_const
-from fwo_const import list_delimiter
-import fwo_alert, fwo_api
-import ipaddress
-
-
-def normalize_network_objects(full_config, config2import, import_id, mgm_id=0):
- nw_objects = []
- logger = getFwoLogger()
-
- for obj_table in full_config['object_tables']:
- collect_nw_objects(obj_table, nw_objects, mgm_id=mgm_id)
- for nw_obj in nw_objects:
- nw_obj.update({'control_id': import_id})
- if nw_obj['obj_typ'] == 'interoperable-device':
- nw_obj.update({'obj_typ': 'external-gateway'})
- if nw_obj['obj_typ'] == 'CpmiVoipSipDomain':
- logger.info("found VOIP object - tranforming to empty group")
- nw_obj.update({'obj_typ': 'group'})
- # set a dummy IP address for objects without IP addreses
- if nw_obj['obj_typ']!='group' and (nw_obj['obj_ip'] is None or nw_obj['obj_ip'] == ''):
- logger.warning("found object without IP :" + nw_obj['obj_name'] + " (type=" + nw_obj['obj_typ'] + ") - setting dummy IP")
- nw_obj.update({'obj_ip': cp_const.dummy_ip})
-
- for idx in range(0, len(nw_objects)-1):
- if nw_objects[idx]['obj_typ'] == 'group':
- add_member_names_for_nw_group(idx, nw_objects)
-
- config2import.update({'network_objects': nw_objects})
-
-
-# collect_nw_objects from object tables and write them into global nw_objects dict
-def collect_nw_objects(object_table, nw_objects, mgm_id=0):
- logger = getFwoLogger()
-
- if object_table['object_type'] not in cp_const.nw_obj_table_names:
- return
-
- for chunk in object_table['object_chunks']:
- if 'objects' not in chunk:
- break
- for obj in chunk['objects']:
- if 'comments' not in obj or obj['comments'] == '':
- obj['comments'] = None
-
- if 'uid' in obj and obj['uid']=='e9ba0c50-ddd7-4aa8-9df6-1c4045ba10bb':
- logger.debug(f"found SIP nw object with uid {obj['uid']} in object dictionary")
-
- member_refs, member_names = set_members(obj)
-
- first_ip, last_ip, obj_type = ip_and_type_handling(obj, mgm_id=mgm_id)
-
- obj_to_add = {
- 'uid': obj['uid'],
- 'name': obj['name'],
- 'typ': obj_type,
- 'color': obj.get('color', 'black'),
- 'comments': obj.get('comments', None),
- 'ip': first_ip,
- 'ip_end': last_ip,
- 'member_refs': member_refs,
- 'member_names': member_names
- }
-
- update_or_add_nw_object(nw_objects, obj_to_add)
-
-
-def ip_and_type_handling(obj, mgm_id=0):
- logger = getFwoLogger()
- ip_addr = get_ip_of_obj(obj, mgm_id=mgm_id)
- ip_array = cidrToRange(ip_addr)
-
- if len(ip_array)==2:
- first_ip = ip_array[0]
- last_ip = ip_array[1]
- elif len(ip_array)==1:
- first_ip = ip_array[0]
- last_ip = None
- else:
- logger.warning(f"found strange ip: {ip_addr}")
-
- obj_type = 'undef'
- if 'type' in obj:
- obj_type = obj['type']
- if 'uid-in-updatable-objects-repository' in obj:
- obj_type = 'group'
- obj['name'] = obj['name-in-updatable-objects-repository']
- obj['uid'] = obj['uid-in-updatable-objects-repository']
- obj['color'] = 'black'
- if obj_type in ['updatable-object', 'access-role', 'group-with-exclusion', 'security-zone', 'dns-domain']:
- obj_type = 'group'
-
- if obj_type == 'group-with-exclusion':
- first_ip = None
- last_ip = None
- obj_type = 'group'
-
- if obj_type == 'security-zone':
- first_ip = cp_const.dummy_ip
- last_ip = '255.255.255.255/32'
- obj_type = 'network'
-
- if obj_type == 'group':
- first_ip = None
- last_ip = None
-
- if obj_type == 'address-range' or obj_type == 'multicast-address-range':
- obj_type = 'ip_range'
- if '-' in str(ip_addr):
- first_ip, last_ip = str(ip_addr).split('-')
- else:
- logger.warning("parse_network::collect_nw_objects - found range object '" +
- obj['name'] + "' without hyphen: " + ip_addr)
- elif obj_type in cp_const.cp_specific_object_types:
- obj_type = 'host'
-
- return first_ip, last_ip, obj_type
-
-
-def set_members(obj):
- member_refs = None
- member_names = None
- if 'members' in obj:
- member_refs = ''
- member_names = ''
- for member in obj['members']:
- member_refs += member + list_delimiter
- member_refs = member_refs[:-1]
- if obj['members'] == '':
- obj['members'] = None
-
- return member_refs, member_names
-
-
-def update_or_add_nw_object(nw_objects, obj): # obj_uid, obj_name, obj_typ, obj_color, obj_comment, obj_ip, obj_ip_end=None, obj_member_refs=None, obj_member_names=None):
- """
- Update an existing network object in the nw_objects list or add it if it does not exist.
- """
- for existing_obj in nw_objects:
- if existing_obj['obj_uid'] == obj['uid']:
- if obj['typ'] == 'host' and obj['ip'] is not None and obj['ip'] != cp_const.dummy_ip:
- # Update existing gateway object, ignore all other caess of duplicate objects
- existing_obj.update({
- 'obj_uid': obj['uid'],
- 'obj_name': obj['name'],
- 'obj_color': obj['color'],
- 'obj_comment': obj['comments'],
- 'obj_typ': obj['typ'],
- 'obj_ip': obj['ip'],
- 'obj_ip_end': obj['ip_end'],
- 'obj_member_refs': obj['member_refs'],
- 'obj_member_names': obj['member_names']
- })
- return
-
- # If not found, append new object
- nw_objects.append({
- 'obj_uid': obj['uid'],
- 'obj_name': obj['name'],
- 'obj_color': obj['color'],
- 'obj_comment': obj['comments'],
- 'obj_typ': obj['typ'],
- 'obj_ip': obj['ip'],
- 'obj_ip_end': obj['ip_end'],
- 'obj_member_refs': obj['member_refs'],
- 'obj_member_names': obj['member_names']
- })
-
-
-# for members of groups, the name of the member obj needs to be fetched separately (starting from API v1.?)
-def resolve_nw_uid_to_name(uid, nw_objects):
- # return name of nw_objects element where obj_uid = uid
- for obj in nw_objects:
- if obj['obj_uid'] == uid:
- return obj['obj_name']
- return 'ERROR: uid "' + uid + '" not found'
-
-
-def add_member_names_for_nw_group(idx, nw_objects):
- group = nw_objects.pop(idx)
- if group['obj_member_refs'] == '' or group['obj_member_refs'] == None:
- #member_names = None
- #obj_member_refs = None
- group['obj_member_names'] = None
- group['obj_member_refs'] = None
- else:
- member_names = ''
- obj_member_refs = group['obj_member_refs'].split(list_delimiter)
- for ref in obj_member_refs:
- member_name = resolve_nw_uid_to_name(ref, nw_objects)
- member_names += member_name + list_delimiter
- group['obj_member_names'] = member_names[:-1]
- nw_objects.insert(idx, group)
-
-
-def validate_ip_address(address):
- try:
- # ipaddress.ip_address(address)
- ipaddress.ip_network(address)
- return True
- # print("IP address {} is valid. The object returned is {}".format(address, ip))
- except ValueError:
- return False
- # print("IP address {} is not valid".format(address))
-
-
-def get_ip_of_obj(obj, mgm_id=None):
- if 'ipv4-address' in obj:
- ip_addr = obj['ipv4-address']
- elif 'ipv6-address' in obj:
- ip_addr = obj['ipv6-address']
- elif 'subnet4' in obj:
- ip_addr = obj['subnet4'] + '/' + str(obj['mask-length4'])
- elif 'subnet6' in obj:
- ip_addr = obj['subnet6'] + '/' + str(obj['mask-length6'])
- elif 'ipv4-address-first' in obj and 'ipv4-address-last' in obj:
- ip_addr = obj['ipv4-address-first'] + '-' + str(obj['ipv4-address-last'])
- elif 'ipv6-address-first' in obj and 'ipv6-address-last' in obj:
- ip_addr = obj['ipv6-address-first'] + '-' + str(obj['ipv6-address-last'])
- else:
- ip_addr = None
-
- ## fix malformed ip addresses (should not regularly occur and constitutes a data issue in CP database)
- if ip_addr is None or ('type' in obj and (obj['type'] == 'address-range' or obj['type'] == 'multicast-address-range')):
- pass # ignore None and ranges here
- elif not validate_ip_address(ip_addr):
- alerter = fwo_alert.getFwoAlerter()
- alert_description = "object is not a valid ip address (" + str(ip_addr) + ")"
- fwo_api.create_data_issue(alerter['fwo_api_base_url'], alerter['jwt'], severity=2, obj_name=obj['name'], object_type=obj['type'], description=alert_description, mgm_id=mgm_id)
- alert_description = "object '" + obj['name'] + "' (type=" + obj['type'] + ") is not a valid ip address (" + str(ip_addr) + ")"
- fwo_api.setAlert(alerter['fwo_api_base_url'], alerter['jwt'], title="import error", severity=2, role='importer', \
- description=alert_description, source='import', alertCode=17, mgm_id=mgm_id)
- ip_addr = cp_const.dummy_ip # set dummy ip address if the ip address is not valid
- return ip_addr
-
-
-def make_host(ip_in):
- ip_obj = ipaddress.ip_address(ip_in)
-
- # If it's a valid address, append the appropriate CIDR notation
- if isinstance(ip_obj, ipaddress.IPv4Address):
- return f"{ip_in}/32"
- elif isinstance(ip_obj, ipaddress.IPv6Address):
- return f"{ip_in}/128"
-
-
-def cidrToRange(ip):
- logger = getFwoLogger()
-
- if isinstance(ip, str):
- if ip.startswith('5002:abcd:1234:2800'):
- logger.debug("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! found test ip " + ip)
-
- # dealing with ranges:
- if '-' in ip:
- return '-'.split(ip)
-
- ipVersion = validIPAddress(ip)
- if ipVersion=='Invalid':
- logger.warning("error while decoding ip '" + ip + "'")
- return [ip]
- elif ipVersion=='IPv4':
- net = ipaddress.IPv4Network(ip)
- elif ipVersion=='IPv6':
- net = ipaddress.IPv6Network(ip)
- return [make_host(str(net.network_address)), make_host(str(net.broadcast_address))]
-
- return [ip]
-
-
-def validIPAddress(IP: str) -> str:
- try:
- t = type(ipaddress.ip_address(IP))
- if t is ipaddress.IPv4Address:
- return "IPv4"
- elif t is ipaddress.IPv6Address:
- return "IPv6"
- else:
- return 'Invalid'
- except:
- try:
- t = type(ipaddress.ip_network(IP))
- if t is ipaddress.IPv4Network:
- return "IPv4"
- elif t is ipaddress.IPv6Network:
- return "IPv6"
- else:
- return 'Invalid'
- except:
- return "Invalid"
diff --git a/roles/importer/files/importer/checkpointR8x/cp_rule.py b/roles/importer/files/importer/checkpointR8x/cp_rule.py
deleted file mode 100644
index d34b0ded86..0000000000
--- a/roles/importer/files/importer/checkpointR8x/cp_rule.py
+++ /dev/null
@@ -1,434 +0,0 @@
-from asyncio.log import logger
-from fwo_log import getFwoLogger
-import json
-import cp_const
-import fwo_const
-import fwo_globals
-from fwo_const import list_delimiter, default_section_header_text
-from fwo_base import sanitize
-from fwo_exception import ImportRecursionLimitReached
-
-uid_to_name_map = {}
-
-
-def normalize_rulebases_top_level (full_config, current_import_id, config2import):
- logger = getFwoLogger()
- target_rulebase = []
- rule_num = 0
- parent_uid=None
- section_header_uids=[]
-
- # fill uid_to_name_map:
- for nw_obj in config2import['network_objects']:
- uid_to_name_map[nw_obj['obj_uid']] = nw_obj['obj_name']
-
- rb_range = range(len(full_config['rulebases']))
- for rb_id in rb_range:
- # if current_layer_name == args.rulebase:
- if fwo_globals.debug_level>3:
- logger.debug("parsing layer " + full_config['rulebases'][rb_id]['layername'])
-
- # parse access rules
- rule_num = parse_rulebase(
- full_config['rulebases'][rb_id], target_rulebase, full_config['rulebases'][rb_id]['layername'],
- current_import_id, rule_num, section_header_uids, parent_uid, config2import)
- # now parse the nat rulebase
-
- # parse nat rules
- if len(full_config['nat_rulebases'])>0:
- if len(full_config['nat_rulebases']) != len(rb_range):
- logger.warning('get_config - found ' + str(len(full_config['nat_rulebases'])) +
- ' nat rulebases and ' + str(len(rb_range)) + ' access rulebases')
- else:
- rule_num = parse_nat_rulebase(
- full_config['nat_rulebases'][rb_id], target_rulebase, full_config['rulebases'][rb_id]['layername'],
- current_import_id, rule_num, section_header_uids, parent_uid, config2import)
- return target_rulebase
-
-
-def acceptMalformedParts(objects, part=''):
- # logger.debug('about to accept malformed rule part (' + part + '): ' + str(objects))
-
- # if we are dealing with a list with one element, resolve the list
- if isinstance(objects, list) and len(objects)==1:
- objects = objects[0]
-
- if part == 'action' and 'name' in objects:
- return { 'action': objects['name'] }
- elif part == 'install-on' and 'name' in objects:
- return { 'install-on': objects['name'] }
- elif part == 'time' and 'name' in objects:
- return { 'time': objects['name'] }
- elif part == 'track' and 'type' in objects and 'name' in objects['type']:
- return { 'track': objects['type']['name'] }
- else:
- logger.warning('found no uid or name in rule part (' + part + '): ' + str(objects))
-
-
-def parseRulePart (objects, part='source'):
- addressObjects = {}
-
- if 'object_chunks' in objects: # for chunks of actions?!
- return addressObjects.update(parseRulePart(objects['object_chunks'], part=part)) # need to parse chunk first
-
- if isinstance(objects, dict): # a single address object
- if 'uid' in objects and 'name' in objects:
- addressObjects[objects['uid']] = objects['name']
- return addressObjects
- else:
- return acceptMalformedParts(objects, part=part)
-
- else: # assuming list of objects
- if objects is None:
- logger.error("rule part " + part + " is None: " + str(objects))
- return None
- else:
- for obj in objects:
- if obj is not None:
- # if 'name' in obj:
- # logger.debug(f"handling obj without uid {obj['name']}, part={part}")
- if 'object_chunks' in obj:
- addressObjects.update(parseRulePart(obj['object_chunks'], part=part)) # need to parse chunk first
- elif 'objects' in obj:
- for o in obj['objects']:
- addressObjects.update(parseRulePart(o, part=part)) # need to parse chunk first
- return addressObjects
- else:
- if 'type' in obj: # found checkpoint object
-
- if obj['type'] == 'LegacyUserAtLocation':
- addressObjects[obj['uid']] = obj['name']
-
- elif obj['type'] == 'access-role':
- if 'networks' in obj:
- if isinstance(obj['networks'], str): # just a single source
- if obj['networks'] == 'any':
- addressObjects[obj['uid']] = obj['name'] + '@' + 'Any'
- else:
- addressObjects[obj['uid']] = obj['name'] + '@' + obj['networks']
- else: # more than one source
- for nw in obj['networks']:
- nw_resolved = resolve_uid_to_name(nw)
- if nw_resolved == "":
- addressObjects[obj['uid']] = obj['name']
- else:
- addressObjects[obj['uid']] = obj['name'] + '@' + nw_resolved
- else:
- addressObjects[obj['uid']] = obj['name'] # adding IA without IP info, TODO: get full networks details here!
- else: # standard object
- addressObjects[obj['uid']] = obj['name']
- else:
- return acceptMalformedParts(objects, part=part)
- else:
- logger.warning(f"found list with a single None obj")
-
-
- if '' in addressObjects.values():
- logger.warning('found empty name in one rule part (' + part + '): ' + str(addressObjects))
-
- return addressObjects
-
-
-def parse_single_rule(nativeRule, rulebase, layer_name, import_id, rule_num, parent_uid, config2import, debug_level=0):
- logger = getFwoLogger()
- # reference to domain rule layer, filling up basic fields
- if 'type' in nativeRule and nativeRule['type'] != 'place-holder':
- if 'rule-number' in nativeRule: # standard rule, no section header
-
- # the following objects might come in chunks:
- sourceObjects = parseRulePart (nativeRule['source'], 'source')
- rule_src_ref = list_delimiter.join(sourceObjects.keys())
- rule_src_name = list_delimiter.join(sourceObjects.values())
-
- destObjects = parseRulePart (nativeRule['destination'], 'destination')
- rule_dst_ref = list_delimiter.join(destObjects.keys())
- rule_dst_name = list_delimiter.join(destObjects.values())
-
- svcObjects = parseRulePart (nativeRule['service'], 'service')
- rule_svc_ref = list_delimiter.join(svcObjects.keys())
- rule_svc_name = list_delimiter.join(svcObjects.values())
-
- targetObjects = parseRulePart (nativeRule['install-on'], 'install-on')
- rule_installon = list_delimiter.join(targetObjects.values())
-
- if isinstance(nativeRule['track'],str):
- rule_track = nativeRule['track']
- else:
- trackObjects = parseRulePart (nativeRule['track'], 'track')
- if trackObjects is None:
- rule_track = 'none'
- else:
- rule_track = list_delimiter.join(trackObjects.values())
-
- actionObjects = parseRulePart (nativeRule['action'], 'action')
- if actionObjects is not None:
- rule_action = list_delimiter.join(actionObjects.values()) # expecting only a single action
- else:
- rule_action = None
- logger.warning('found rule without action: ' + str(nativeRule))
-
- timeObjects = parseRulePart (nativeRule['time'], 'time')
- rule_time = list_delimiter.join(timeObjects.values()) # only considering the first time object
-
- # starting with the non-chunk objects
- rule_name = nativeRule.get('name', None)
-
- # new in v8.0.3:
- rule_custom_fields = nativeRule.get('custom-fields', None)
-
- if 'meta-info' in nativeRule and 'last-modifier' in nativeRule['meta-info']:
- rule_last_change_admin = nativeRule['meta-info']['last-modifier']
- else:
- rule_last_change_admin = None
-
- # new in v5.1.17:
- if 'parent_rule_uid' in nativeRule:
- logger.debug(
- 'found rule (uid=' + nativeRule['uid'] + ') with parent_rule_uid set: ' + nativeRule['parent_rule_uid'])
- parent_rule_uid = nativeRule['parent_rule_uid']
- else:
- parent_rule_uid = parent_uid
- if parent_rule_uid == '':
- parent_rule_uid = None
-
- # new in v5.5.1:
- rule_type = nativeRule.get('rule_type', 'access')
-
- comments = nativeRule.get('comments', None)
- if comments == '':
- comments = None
-
- if 'hits' in nativeRule and 'last-date' in nativeRule['hits'] and 'iso-8601' in nativeRule['hits']['last-date']:
- last_hit = nativeRule['hits']['last-date']['iso-8601']
- else:
- last_hit = None
-
- rule = {
- "control_id": int(import_id),
- "rule_num": int(rule_num),
- "rulebase_name": sanitize(layer_name),
- # rule_ruleid
- "rule_disabled": not bool(nativeRule['enabled']),
- "rule_src_neg": bool(nativeRule['source-negate']),
- "rule_src": sanitize(rule_src_name),
- "rule_src_refs": sanitize(rule_src_ref),
- "rule_dst_neg": bool(nativeRule['destination-negate']),
- "rule_dst": sanitize(rule_dst_name),
- "rule_dst_refs": sanitize(rule_dst_ref),
- "rule_svc_neg": bool(nativeRule['service-negate']),
- "rule_svc": sanitize(rule_svc_name),
- "rule_svc_refs": sanitize(rule_svc_ref),
- "rule_action": sanitize(rule_action),
- # "rule_track": sanitize(nativeRule['track']['type']),
- "rule_track": sanitize(rule_track),
- "rule_installon": sanitize(rule_installon),
- "rule_time": sanitize(rule_time),
- "rule_name": sanitize(rule_name),
- "rule_uid": sanitize(nativeRule['uid']),
- "rule_custom_fields": sanitize(rule_custom_fields),
- "rule_implied": False,
- "rule_type": sanitize(rule_type),
- # "rule_head_text": sanitize(section_name),
- # rule_from_zone
- # rule_to_zone
- "rule_last_change_admin": sanitize(rule_last_change_admin),
- "parent_rule_uid": sanitize(parent_rule_uid),
- "last_hit": sanitize(last_hit)
- }
- if comments is not None:
- rule['rule_comment'] = sanitize(comments)
- rulebase.append(rule)
-
- return rule_num + 1
- return rule_num
-
-
-def resolve_uid_to_name(nw_obj_uid):
- if nw_obj_uid in uid_to_name_map:
- return uid_to_name_map[nw_obj_uid]
- else:
- logger = getFwoLogger()
- logger.warning("could not resolve network object with uid " + nw_obj_uid)
- return ""
-
-
-def insert_section_header_rule(rulebase, section_name, layer_name, import_id, rule_uid, rule_num, section_header_uids, parent_uid):
- section_header_uids.append(sanitize(rule_uid))
- rule = {
- "control_id": int(import_id),
- "rule_num": int(rule_num),
- "rulebase_name": sanitize(layer_name),
- # rule_ruleid
- "rule_disabled": False,
- "rule_src_neg": False,
- "rule_src": "Any",
- "rule_src_refs": sanitize(cp_const.any_obj_uid),
- "rule_dst_neg": False,
- "rule_dst": "Any",
- "rule_dst_refs": sanitize(cp_const.any_obj_uid),
- "rule_svc_neg": False,
- "rule_svc": "Any",
- "rule_svc_refs": sanitize(cp_const.any_obj_uid),
- "rule_action": "Accept",
- "rule_track": "Log",
- "rule_installon": "Policy Targets",
- "rule_time": "Any",
- "rule_implied": False,
- # "rule_comment": None,
- # rule_name
- "rule_uid": sanitize(rule_uid),
- "rule_head_text": sanitize(section_name),
- # rule_from_zone
- # rule_to_zone
- # rule_last_change_admin
- "parent_rule_uid": sanitize(parent_uid)
- }
- rulebase.append(rule)
- return rule_num + 1
-
-
-def add_domain_rule_header_rule(rulebase, section_name, layer_name, import_id, rule_uid, rule_num, section_header_uids, parent_uid):
- return insert_section_header_rule(rulebase, section_name, layer_name,
- import_id, rule_uid, rule_num, section_header_uids, parent_uid)
-
-
-def check_and_add_section_header(src_rulebase, target_rulebase, layer_name, import_id, rule_num, section_header_uids, parent_uid, config2import, debug_level=0, recursion_level=1):
- # if current rulebase starts a new section, add section header, but only if it does not exist yet (can happen by chunking a section)
- if 'type' in src_rulebase and src_rulebase['type'] == 'access-section' and 'uid' in src_rulebase: # and not src_rulebase['uid'] in section_header_uids:
- section_name = default_section_header_text
- if 'name' in src_rulebase:
- section_name = src_rulebase['name']
- if 'parent_rule_uid' in src_rulebase:
- parent_uid = src_rulebase['parent_rule_uid']
- else:
- parent_uid = ""
- rule_num = insert_section_header_rule(target_rulebase, section_name, layer_name, import_id, src_rulebase['uid'], rule_num, section_header_uids, parent_uid)
- parent_uid = src_rulebase['uid']
- return rule_num
-
-
-def parse_rulebase(src_rulebase, target_rulebase, layer_name, import_id, rule_num, section_header_uids, parent_uid, config2import,
- debug_level=0, recursion_level=1, layer_disabled=False):
- logger = getFwoLogger()
- if (recursion_level > fwo_const.max_recursion_level):
- raise ImportRecursionLimitReached("parse_rulebase") from None
-
- # parse chunks
- if 'layerchunks' in src_rulebase: # found chunks of layers which need to be parsed separately
- for chunk in src_rulebase['layerchunks']:
- if 'rulebase' in chunk:
- for rules_chunk in chunk['rulebase']:
- rule_num = parse_rulebase(rules_chunk, target_rulebase, layer_name, import_id, rule_num,
- section_header_uids, parent_uid, config2import, debug_level=debug_level, recursion_level=recursion_level+1)
- else:
- rule_num = parse_rulebase(chunk, target_rulebase, layer_name, import_id, rule_num, section_header_uids, parent_uid, config2import, debug_level=debug_level, recursion_level=recursion_level+1)
-
- check_and_add_section_header(src_rulebase, target_rulebase, layer_name, import_id, rule_num, section_header_uids, parent_uid, config2import, debug_level=debug_level, recursion_level=recursion_level+1)
-
- # parse layered rulebase
- if 'rulebase' in src_rulebase:
- # layer_disabled = not src_rulebase['enabled']
- for rule in src_rulebase['rulebase']:
- if 'type' in rule:
- if rule['type'] == 'place-holder': # add domain rules
- section_name = ""
- if 'name' in src_rulebase:
- section_name = rule['name']
- rule_num = add_domain_rule_header_rule(
- target_rulebase, section_name, layer_name, import_id, rule['uid'], rule_num, section_header_uids, parent_uid)
- else: # parse standard sections
- rule_num = parse_single_rule(
- rule, target_rulebase, layer_name, import_id, rule_num, parent_uid, config2import, debug_level=debug_level)
- if 'rulebase' in rule: # alsways check if a rule contains another layer
- rule_num = parse_rulebase(rule, target_rulebase, layer_name, import_id, rule_num, section_header_uids, parent_uid, config2import, debug_level=debug_level, recursion_level=recursion_level+1)
-
- if 'type' in src_rulebase and src_rulebase['type'] == 'place-holder': # add domain rules
- logger.debug('found domain rule ref: ' + src_rulebase['uid'])
- section_name = ""
- if 'name' in src_rulebase:
- section_name = src_rulebase['name']
- rule_num = add_domain_rule_header_rule(
- target_rulebase, section_name, layer_name, import_id, src_rulebase['uid'], rule_num, section_header_uids, parent_uid)
-
- if 'rule-number' in src_rulebase: # rulebase is just a single rule
- rule_num = parse_single_rule(src_rulebase, target_rulebase, layer_name, import_id, rule_num, parent_uid, config2import)
-
- return rule_num
-
-
-def parse_nat_rulebase(src_rulebase, target_rulebase, layer_name, import_id, rule_num, section_header_uids, parent_uid, config2import, debug_level=0, recursion_level=1):
-
- if (recursion_level > fwo_const.max_recursion_level):
- raise ImportRecursionLimitReached(
- "parse_nat_rulebase_json") from None
-
- logger = getFwoLogger()
- if 'nat_rule_chunks' in src_rulebase:
- for chunk in src_rulebase['nat_rule_chunks']:
- if 'rulebase' in chunk:
- for rules_chunk in chunk['rulebase']:
- rule_num = parse_nat_rulebase(rules_chunk, target_rulebase, layer_name, import_id, rule_num,
- section_header_uids, parent_uid, config2import, debug_level=debug_level, recursion_level=recursion_level+1)
- else:
- logger.warning(
- "parse_rule: found no rulebase in chunk:\n" + json.dumps(chunk, indent=2))
- else:
- if 'rulebase' in src_rulebase:
- check_and_add_section_header(src_rulebase, target_rulebase, layer_name, import_id, rule_num, section_header_uids, parent_uid, config2import, debug_level=debug_level, recursion_level=recursion_level+1)
-
- for rule in src_rulebase['rulebase']:
- (rule_match, rule_xlate) = parse_nat_rule_transform(rule, rule_num)
- rule_num = parse_single_rule(
- rule_match, target_rulebase, layer_name, import_id, rule_num, parent_uid, config2import)
- parse_single_rule( # do not increase rule_num here
- rule_xlate, target_rulebase, layer_name, import_id, rule_num, parent_uid, config2import)
-
- if 'rule-number' in src_rulebase: # rulebase is just a single rule (xlate rules do not count)
- (rule_match, rule_xlate) = parse_nat_rule_transform(
- src_rulebase, rule_num)
- rule_num = parse_single_rule(
- rule_match, target_rulebase, layer_name, import_id, rule_num, parent_uid, config2import)
- parse_single_rule( # do not increase rule_num here (xlate rules do not count)
- rule_xlate, target_rulebase, layer_name, import_id, rule_num, parent_uid, config2import)
- return rule_num
-
-
-def parse_nat_rule_transform(xlate_rule_in, rule_num):
- # todo: cleanup certain fields (install-on, ....)
- rule_match = {
- 'uid': xlate_rule_in['uid'],
- 'source': [xlate_rule_in['original-source']],
- 'destination': [xlate_rule_in['original-destination']],
- 'service': [xlate_rule_in['original-service']],
- 'action': {'name': 'Drop'},
- 'track': {'type': {'name': 'None'}},
- 'type': 'nat',
- 'rule-number': rule_num,
- 'source-negate': False,
- 'destination-negate': False,
- 'service-negate': False,
- 'install-on': [{'name': 'Policy Targets'}],
- 'time': [{'name': 'Any'}],
- 'enabled': xlate_rule_in['enabled'],
- 'comments': xlate_rule_in['comments'],
- 'rule_type': 'original'
- }
- rule_xlate = {
- 'uid': xlate_rule_in['uid'],
- 'source': [xlate_rule_in['translated-source']],
- 'destination': [xlate_rule_in['translated-destination']],
- 'service': [xlate_rule_in['translated-service']],
- 'action': {'name': 'Drop'},
- 'track': {'type': {'name': 'None'}},
- 'type': 'nat',
- 'rule-number': rule_num,
- 'enabled': True,
- 'source-negate': False,
- 'destination-negate': False,
- 'service-negate': False,
- 'install-on': [{'name': 'Policy Targets'}],
- 'time': [{'name': 'Any'}],
- 'rule_type': 'xlate'
- }
- return (rule_match, rule_xlate)
-
diff --git a/roles/importer/files/importer/checkpointR8x/cp_service.py b/roles/importer/files/importer/checkpointR8x/cp_service.py
deleted file mode 100644
index 96f18327fd..0000000000
--- a/roles/importer/files/importer/checkpointR8x/cp_service.py
+++ /dev/null
@@ -1,135 +0,0 @@
-import re
-import cp_const
-from fwo_const import list_delimiter
-
-
-# collect_svcobjects writes svc info into global users dict
-def collect_svc_objects(object_table, svc_objects):
- proto_map = {
- 'service-tcp': 6,
- 'service-udp': 17,
- 'service-icmp': 1
- }
-
- if object_table['object_type'] in cp_const.svc_obj_table_names:
- session_timeout = ''
- typ = 'undef'
- if object_table['object_type'] in cp_const.group_svc_obj_types:
- typ = 'group'
- if object_table['object_type'] in cp_const.simple_svc_obj_types:
- typ = 'simple'
- for chunk in object_table['object_chunks']:
- if 'objects' in chunk:
- for obj in chunk['objects']:
- if 'type' in obj and obj['type'] in ['service-dce-rpc', 'service-rpc']:
- typ = 'rpc'
- if 'type' in obj and obj['type'] in proto_map:
- proto = proto_map[obj['type']]
- elif 'ip-protocol' in obj:
- proto = obj['ip-protocol']
- else:
- proto = None
- member_refs = ''
- port = ''
- port_end = ''
- rpc_nr = None
- member_refs = None
- if 'members' in obj:
- member_refs = ''
- for member in obj['members']:
- member_refs += member + list_delimiter
- member_refs = member_refs[:-1]
- if 'session-timeout' in obj:
- session_timeout = str(obj['session-timeout'])
- else:
- session_timeout = None
- if 'interface-uuid' in obj:
- rpc_nr = obj['interface-uuid']
- if 'program-number' in obj:
- rpc_nr = obj['program-number']
- if 'port' in obj:
- port = str(obj['port'])
- port_end = port
- pattern = re.compile(r'^\>(\d+)$')
- match = pattern.match(port)
- if match:
- port = str(int(match.group()[1:]) + 1)
- port_end = str(65535)
- else:
- pattern = re.compile(r'^\<(\d+)$')
- match = pattern.match(port)
- if match:
- port = str(1)
- port_end = str(int(match.group()[1:]) - 1)
- else:
- pattern = re.compile(r'^(\d+)\-(\d+)$')
- match = pattern.match(port)
- if match:
- port, port_end = match.group().split('-')
- else: # standard port without "<>-"
- pattern = re.compile(r'^(\d+)$')
- match = pattern.match(port)
- if match:
- # port stays unchanged
- port_end = port
- else: # Any
- pattern = re.compile(r'^(Any)$')
- match = pattern.match(port)
- if match:
- port = str(1)
- port_end = str(65535)
- else: # e.g. suspicious cases
- port = None
- port_end = None
- else:
- # rpc, group - setting ports to 0
- port = None
- port_end = None
- if not 'color' in obj:
- # print('warning: no color found for service ' + obj['name'])
- obj['color'] = 'black'
- if not 'comments' in obj or obj['comments'] == '':
- obj['comments'] = None
- svc_objects.extend([{'svc_uid': obj['uid'], 'svc_name': obj['name'], 'svc_color': obj['color'],
- 'svc_comment': obj['comments'],
- 'svc_typ': typ, 'svc_port': port, 'svc_port_end': port_end,
- 'svc_member_refs': member_refs,
- 'svc_member_names': None,
- 'ip_proto': proto,
- 'svc_timeout': session_timeout,
- 'rpc_nr': rpc_nr
- }])
-
-
-# return name of nw_objects element where obj_uid = uid
-def resolve_svc_uid_to_name(uid, svc_objects):
- for obj in svc_objects:
- if obj['svc_uid'] == uid:
- return obj['svc_name']
- return 'ERROR: uid ' + uid + ' not found'
-
-
-def add_member_names_for_svc_group(idx, svc_objects):
- member_names = ''
- group = svc_objects.pop(idx)
-
- if 'svc_member_refs' in group and group['svc_member_refs'] is not None:
- svc_member_refs = group['svc_member_refs'].split(list_delimiter)
- for ref in svc_member_refs:
- member_name = resolve_svc_uid_to_name(ref, svc_objects)
- member_names += member_name + list_delimiter
- group['svc_member_names'] = member_names[:-1]
-
- svc_objects.insert(idx, group)
-
-
-def normalize_service_objects(full_config, config2import, import_id, debug_level=0):
- svc_objects = []
- for svc_table in full_config['object_tables']:
- collect_svc_objects(svc_table, svc_objects)
- for obj in svc_objects:
- obj.update({'control_id': import_id})
- for idx in range(0, len(svc_objects)-1):
- if svc_objects[idx]['svc_typ'] == 'group':
- add_member_names_for_svc_group(idx, svc_objects)
- config2import.update({'service_objects': svc_objects})
diff --git a/roles/importer/files/importer/checkpointR8x/cp_user.py b/roles/importer/files/importer/checkpointR8x/cp_user.py
deleted file mode 100644
index 099a47991a..0000000000
--- a/roles/importer/files/importer/checkpointR8x/cp_user.py
+++ /dev/null
@@ -1,74 +0,0 @@
-
-from fwo_log import getFwoLogger
-import json
-# from checkpointR8x.cp_getter import ParseUidToName
-
-def collect_users_from_rule(rule, users): #, objDict):
- if 'rule-number' in rule: # standard rule
- logger = getFwoLogger()
- if 'type' in rule and rule['type'] != 'place-holder':
- for src in rule["source"]:
-# srcObj = ParseUidToName(src, objDict)
-
- # need to get all details for the user first!
- if 'type' in src:
- if src['type'] == 'access-role' or src['type'] == 'LegacyUserAtLocation':
- if src['type'] == 'access-role':
- user_name = src['name']
- user_uid = src['uid']
- user_typ = 'group'
- user_comment = src.get('comments', None)
- user_color = src['color']
- if 'users' in src:
- user_typ = 'simple'
- elif src['type'] == 'LegacyUserAtLocation':
- user_str = src["name"]
- user_ar = user_str.split('@')
- user_name = user_ar[0]
- user_uid = src.get('userGroup', None)
- user_typ = 'group'
- user_comment = src.get('comments', None)
- user_color = src.get('color', None)
- else:
- break
- if user_comment == '':
- user_comment = None
- users.update({user_name: {'user_uid': user_uid, 'user_typ': user_typ,
- 'user_comment': user_comment, 'user_color': user_color}})
- else:
- logger.warning("found src user without type field: " + json.dumps(src))
- if 'name' in src and 'uid' in src:
- users.update({src["name"]: {'user_uid': src["uid"], 'user_typ': 'simple'}})
-
- else: # section
- collect_users_from_rulebase(rule["rulebase"], users)
-
-
-# collect_users writes user info into global users dict
-def collect_users_from_rulebase(rulebase, users):
- if 'layerchunks' in rulebase:
- for chunk in rulebase['layerchunks']:
- if 'rulebase' in chunk:
- for rule in chunk['rulebase']:
- collect_users_from_rule(rule, users)
- else:
- for rule in rulebase:
- collect_users_from_rule(rule, users)
-
-
-# the following is only used within new python-only importer:
-def parse_user_objects_from_rulebase(rulebase, users, import_id):
- collect_users_from_rulebase(rulebase, users)
- for user_name in users.keys():
- # TODO: get user info via API
- userUid = getUserUidFromCpApi(user_name)
- # finally add the import id
- users[user_name]['control_id'] = import_id
-
-
-
-def getUserUidFromCpApi (userName):
- # show-object with UID
- # dummy implementation returning the name as uid
- return userName
-
diff --git a/roles/importer/files/importer/checkpointR8x/fwcommon.py b/roles/importer/files/importer/checkpointR8x/fwcommon.py
deleted file mode 100644
index 86bca437af..0000000000
--- a/roles/importer/files/importer/checkpointR8x/fwcommon.py
+++ /dev/null
@@ -1,278 +0,0 @@
-import sys
-import json
-import copy
-from common import importer_base_dir
-from fwo_log import getFwoLogger
-sys.path.append(importer_base_dir + '/checkpointR8x')
-import time
-import cp_rule
-import cp_const, cp_network, cp_service
-import cp_getter
-from fwo_exception import FwLoginFailed, FwLogoutFailed
-from cp_user import parse_user_objects_from_rulebase
-
-
-def has_config_changed (full_config, mgm_details, force=False):
-
- if full_config != {}: # a config was passed in (read from file), so we assume that an import has to be done (simulating changes here)
- return 1
-
- domain, _ = prepare_get_vars(mgm_details)
-
- session_id = login_cp(mgm_details, domain)
-
- last_change_time = ''
- if 'import_controls' in mgm_details:
- for importctl in mgm_details['import_controls']:
- if 'starttime' in importctl:
- last_change_time = importctl['starttime']
-
- if last_change_time==None or last_change_time=='' or force:
- # if no last import time found or given or if force flag is set, do full import
- result = 1
- else: # otherwise search for any changes since last import
- result = (cp_getter.get_changes(session_id, mgm_details['hostname'], str(mgm_details['port']),last_change_time) != 0)
-
- logout_cp("https://" + mgm_details['hostname'] + ":" + str(mgm_details['port']) + "/web_api/", session_id)
-
- return result
-
-
-def get_config(config2import, full_config, current_import_id, mgm_details, limit=150, force=False, jwt=None):
- logger = getFwoLogger()
- logger.debug ( "starting checkpointR8x/get_config" )
-
- if full_config == {}: # no native config was passed in, so getting it from FW-Manager
- parsing_config_only = False
- else:
- parsing_config_only = True
-
- if not parsing_config_only: # get config from cp fw mgr
- starttime = int(time.time())
-
- if 'users' not in full_config:
- full_config.update({'users': {}})
-
- domain, base_url = prepare_get_vars(mgm_details)
-
- sid = login_cp(mgm_details, domain)
-
- starttimeTemp = int(time.time())
- logger.debug ( "checkpointR8x/get_config/getting objects ...")
-
- result_get_objects = get_objects (full_config, mgm_details, base_url, sid, force=force, limit=str(limit), details_level=cp_const.details_level, test_version='off')
- if result_get_objects>0:
- logger.warning ( "checkpointR8x/get_config/error while gettings objects")
- return result_get_objects
- logger.debug ( "checkpointR8x/get_config/fetched objects in " + str(int(time.time()) - starttimeTemp) + "s")
-
- starttimeTemp = int(time.time())
- logger.debug ( "checkpointR8x/get_config/getting rules ...")
- result_get_rules = get_rules (full_config, mgm_details, base_url, sid, force=force, limit=str(limit), details_level=cp_const.details_level, test_version='off')
- if result_get_rules>0:
- logger.warning ( "checkpointR8x/get_config/error while gettings rules")
- return result_get_rules
- logger.debug ( "checkpointR8x/get_config/fetched rules in " + str(int(time.time()) - starttimeTemp) + "s")
-
- duration = int(time.time()) - starttime
- logger.debug ( "checkpointR8x/get_config - fetch duration: " + str(duration) + "s" )
-
- cp_network.normalize_network_objects(full_config, config2import, current_import_id, mgm_id=mgm_details['id'])
- cp_service.normalize_service_objects(full_config, config2import, current_import_id)
- parse_users_from_rulebases(full_config, full_config['rulebases'], full_config['users'], config2import, current_import_id)
- config2import.update({'rules': cp_rule.normalize_rulebases_top_level(full_config, current_import_id, config2import) })
- if not parsing_config_only: # get config from cp fw mgr
- logout_cp("https://" + mgm_details['hostname'] + ":" + str(mgm_details['port']) + "/web_api/", sid)
- return 0
-
-
-def prepare_get_vars(mgm_details):
- # from 5.8 onwards: preferably use domain uid instead of domain name due to CP R81 bug with certain installations
- if mgm_details['domainUid'] != None:
- domain = mgm_details['domainUid']
- else:
- domain = mgm_details['configPath']
- api_host = mgm_details['hostname']
- api_port = str(mgm_details['port'])
- base_url = 'https://' + api_host + ':' + str(api_port) + '/web_api/'
-
- return domain, base_url
-
-
-def login_cp(mgm_details, domain, ssl_verification=True):
- try: # top level dict start, sid contains the domain information, so only sending domain during login
- login_result = cp_getter.login(mgm_details['import_credential']['user'], mgm_details['import_credential']['secret'], mgm_details['hostname'], str(mgm_details['port']), domain)
- return login_result
- except:
- raise FwLoginFailed
-
-
-def logout_cp(url, sid):
- try:
- logout_result = cp_getter.logout(url, sid)
- return logout_result
- except:
- raise FwLogoutFailed
-
-
-def get_rules (config_json, mgm_details, v_url, sid, force=False, config_filename=None,
- limit=150, details_level=cp_const.details_level, test_version='off', debug_level=0, ssl_verification=True):
-
- logger = getFwoLogger()
- config_json.update({'rulebases': [], 'nat_rulebases': [] })
- show_params_rules = {'limit':limit,'use-object-dictionary':cp_const.use_object_dictionary,'details-level':details_level, 'show-hits' : cp_const.with_hits}
-
- # read all rulebases: handle per device details
- for device in mgm_details['devices']:
- if device['global_rulebase_name'] != None and device['global_rulebase_name']!='':
- show_params_rules['name'] = device['global_rulebase_name']
- # get global layer rulebase
- logger.debug ( "getting layer: " + show_params_rules['name'] )
- current_layer_json = cp_getter.get_layer_from_api_as_dict (v_url, sid, show_params_rules, layername=device['global_rulebase_name'], nativeConfig=config_json)
- if current_layer_json is None:
- return 1
- # now also get domain rules
- show_params_rules['name'] = device['local_rulebase_name']
- current_layer_json['layername'] = device['local_rulebase_name']
- logger.debug ( "getting domain rule layer: " + show_params_rules['name'] )
- domain_rules = cp_getter.get_layer_from_api_as_dict (v_url, sid, show_params_rules, layername=device['local_rulebase_name'], nativeConfig=config_json)
- if current_layer_json is None:
- return 1
-
- # now handling possible reference to domain rules within global rules
- # if we find the reference, replace it with the domain rules
- if 'layerchunks' in current_layer_json:
- for chunk in current_layer_json["layerchunks"]:
- for rule in chunk['rulebase']:
- if "type" in rule and rule["type"] == "place-holder":
- logger.debug ("found domain rules place-holder: " + str(rule) + "\n\n")
- current_layer_json = cp_getter.insert_layer_after_place_holder(current_layer_json, domain_rules, rule['uid'])
- else: # no global rules, just get local ones
- show_params_rules['name'] = device['local_rulebase_name']
- logger.debug ( "getting layer: " + show_params_rules['name'] )
- current_layer_json = cp_getter.get_layer_from_api_as_dict (v_url, sid, show_params_rules, layerName=device['local_rulebase_name'], nativeConfig=config_json)
- if current_layer_json is None:
- return 1
-
- config_json['rulebases'].append(current_layer_json)
-
- # getting NAT rules - need package name for nat rule retrieval
- # todo: each gateway/layer should have its own package name (pass management details instead of single data?)
- if device['package_name'] != None and device['package_name'] != '':
- show_params_rules = {'limit':limit,'use-object-dictionary':cp_const.use_object_dictionary,'details-level':details_level, 'package': device['package_name'] }
- if debug_level>3:
- logger.debug ( "getting nat rules for package: " + device['package_name'] )
- nat_rules = cp_getter.get_nat_rules_from_api_as_dict (v_url, sid, show_params_rules, nativeConfig=config_json)
- if len(nat_rules)>0:
- config_json['nat_rulebases'].append(nat_rules)
- else:
- config_json['nat_rulebases'].append({ "nat_rule_chunks": [] })
- else: # always making sure we have an (even empty) nat rulebase per device
- config_json['nat_rulebases'].append({ "nat_rule_chunks": [] })
- return 0
-
-
-def get_objects(config_json, mgm_details, v_url, sid, force=False, config_filename=None,
- limit=150, details_level=cp_const.details_level, test_version='off', debug_level=0, ssl_verification=True):
-
- logger = getFwoLogger()
-
- config_json["object_tables"] = []
- show_params_objs = {'limit':limit,'details-level': cp_const.details_level}
-
- # getting Original (NAT) object (both for networks and services)
- origObj = cp_getter.getObjectDetailsFromApi(cp_const.original_obj_uid, sid=sid, apiurl=v_url)['object_chunks'][0]
- anyObj = cp_getter.getObjectDetailsFromApi(cp_const.any_obj_uid, sid=sid, apiurl=v_url)['object_chunks'][0]
- noneObj = cp_getter.getObjectDetailsFromApi(cp_const.none_obj_uid, sid=sid, apiurl=v_url)['object_chunks'][0]
- internetObj = cp_getter.getObjectDetailsFromApi(cp_const.internet_obj_uid, sid=sid, apiurl=v_url)['object_chunks'][0]
-
- for obj_type in cp_const.api_obj_types:
- if obj_type in cp_const.obj_types_full_fetch_needed:
- show_params_objs.update({'details-level': cp_const.details_level_group_objects})
- else:
- show_params_objs.update({'details-level': cp_const.details_level_objects})
- object_table = { "object_type": obj_type, "object_chunks": [] }
- current=0
- total=current+1
- show_cmd = 'show-' + obj_type
- if debug_level>5:
- logger.debug ( "obj_type: "+ obj_type )
- while (current5:
- logger.debug ( obj_type +" current:"+ str(current) + " of a total " + str(total) )
- else :
- current = total
- if debug_level>5:
- logger.debug ( obj_type +" total:"+ str(total) )
-
- # adding the uid of the Original, Any and None objects (as separate chunks):
- if obj_type == 'networks':
- object_table['object_chunks'].append(origObj)
- object_table['object_chunks'].append(anyObj)
- object_table['object_chunks'].append(noneObj)
- object_table['object_chunks'].append(internetObj)
- if obj_type == 'services-other':
- object_table['object_chunks'].append(origObj)
- object_table['object_chunks'].append(anyObj)
- object_table['object_chunks'].append(noneObj)
-
- config_json["object_tables"].append(object_table)
-
- # only write config to file if config_filename is given
- if config_filename != None and len(config_filename)>1:
- with open(config_filename, "w") as configfile_json:
- configfile_json.write(json.dumps(config_json))
- return 0
-
-
-def parse_users_from_rulebases (full_config, rulebase, users, config2import, current_import_id):
- if 'users' not in full_config:
- full_config.update({'users': {}})
-
- rb_range = range(len(full_config['rulebases']))
- for rb_id in rb_range:
- parse_user_objects_from_rulebase (full_config['rulebases'][rb_id], full_config['users'], current_import_id)
-
- # copy users from full_config to config2import
- # also converting users from dict to array:
- config2import.update({'user_objects': []})
- for user_name in full_config['users'].keys():
- user = copy.deepcopy(full_config['users'][user_name])
- user.update({'user_name': user_name})
- config2import['user_objects'].append(user)
-
-
-def ParseUidToName(myUid, myObjectDictList):
- """Help function finds name to given UID in object dict
-
- Parameters
- ----------
- myUid : str
- Checkpoint UID
- myObjectDictList : list[dict]
- Each dict represents a checkpoint object
- Notation of CP API return to 'show-access-rulebase'
- with 'details-level' as 'standard'
-
- Returns
- -------
- myReturnObject : str
- Name of object with matching UID to input parameter myUid
- """
-
- logger = getFwoLogger()
- myReturnObject = ''
- for myObject in myObjectDictList:
- if myUid == myObject['uid']:
- myReturnObject = myObject['name']
-
- if myReturnObject == '':
- logger.warning('The UID: ' + myUid + ' was not found in Object Dict')
-
- return myReturnObject
\ No newline at end of file
diff --git a/roles/importer/files/importer/ciscoAsa/fwcommon.py b/roles/importer/files/importer/ciscoAsa/fwcommon.py
deleted file mode 100644
index 5bd19481dd..0000000000
--- a/roles/importer/files/importer/ciscoAsa/fwcommon.py
+++ /dev/null
@@ -1,267 +0,0 @@
-#!/usr/bin/env python3
-"""Parser for Cisco ASA configuration files.
-
-The script converts an ASA configuration into a simplified
-normalized JSON structure which is inspired by the public
-sample configuration provided at
-https://fwodemodata.cactus.de/demo12_cpr8x_v9.json.
-
-The parser is not a complete ASA parser. It focuses on
-constructs used in the example configuration: interfaces,
-network objects and groups, service groups, time ranges and
-ACLs. Unknown lines are ignored so the script can be applied
-to larger configurations without failing.
-
-Usage:
- python asa_config_parser.py
-"""
-
-from __future__ import annotations
-from typing import Any
-
-def has_config_changed(full_config, mgm_details, force=False):
- # dummy - may be filled with real check later on
- return True
-
-
-def get_config(config2import, full_config, current_import_id, mgm_details, limit=100, force=False, jwt=''):
- config2import = parse_asa_config(full_config)
- config2import["import_id"] = current_import_id
-
-
-def _parse_address(tokens: list[str], idx: int) -> tuple[dict[str, Any], int]:
- """Parse an address specification starting at ``tokens[idx]``.
-
- Returns a tuple of (address_dict, next_index).
- """
-
- t = tokens[idx]
- if t == "any":
- return {"type": "any"}, idx + 1
- if t == "object":
- return {"type": "object", "value": tokens[idx + 1]}, idx + 2
- if t == "object-group":
- return {"type": "object-group", "value": tokens[idx + 1]}, idx + 2
- if t == "host":
- return {"type": "host", "ip": tokens[idx + 1]}, idx + 2
-
- # assume subnet form: ip mask
- return {
- "type": "subnet",
- "ip": tokens[idx],
- "netmask": tokens[idx + 1] if idx + 1 < len(tokens) else "",
- }, idx + 2
-
-
-def _parse_interface(lines: list[str], start: int, result: dict[str, Any]) -> int:
- iface = {"name": lines[start].strip().split()[1]}
- i = start + 1
- while i < len(lines) and lines[i].startswith(" "):
- sub = lines[i].strip()
- if sub.startswith("nameif "):
- iface["nameif"] = sub.split()[1]
- elif sub.startswith("security-level "):
- iface["security_level"] = int(sub.split()[1])
- elif sub.startswith("ip address "):
- parts = sub.split()
- iface["ip_address"] = parts[2]
- iface["netmask"] = parts[3]
- i += 1
- result["interfaces"].append(iface)
- return i
-
-
-def _parse_time_range(lines: list[str], start: int, result: dict[str, Any]) -> int:
- name = lines[start].strip().split()[1]
- tr: dict[str, Any] = {}
- i = start + 1
- while i < len(lines) and lines[i].startswith(" "):
- sub = lines[i].strip()
- if sub.startswith("periodic "):
- tr["type"] = "periodic"
- tr["value"] = sub[len("periodic "):]
- elif sub.startswith("absolute "):
- tr["type"] = "absolute"
- tr["value"] = sub[len("absolute "):]
- i += 1
- result["time_ranges"][name] = tr
- return i
-
-
-def _parse_network_object(lines: list[str], start: int, result: dict[str, Any]) -> int:
- name = lines[start].strip().split()[2]
- obj: dict[str, Any] = {}
- i = start + 1
- while i < len(lines) and lines[i].startswith(" "):
- sub = lines[i].strip()
- if sub.startswith("subnet "):
- _, ip, mask = sub.split()
- obj = {"type": "subnet", "ip": ip, "netmask": mask}
- elif sub.startswith("host "):
- _, ip = sub.split()
- obj = {"type": "host", "ip": ip}
- elif sub.startswith("fqdn v4 "):
- obj = {"type": "fqdn", "fqdn": sub.split()[2]}
- i += 1
- result["network_objects"][name] = obj
- return i
-
-
-def _parse_network_group(lines: list[str], start: int, result: dict[str, Any]) -> int:
- name = lines[start].strip().split()[2]
- members: list[dict[str, Any]] = []
- i = start + 1
- while i < len(lines) and lines[i].startswith(" "):
- sub = lines[i].strip()
- if sub.startswith("network-object object "):
- members.append({"type": "object", "value": sub.split()[2]})
- elif sub.startswith("network-object "):
- parts = sub.split()
- members.append({"type": "subnet", "ip": parts[1], "netmask": parts[2]})
- i += 1
- result["network_groups"][name] = members
- return i
-
-
-def _parse_service_group(lines: list[str], start: int, result: dict[str, Any]) -> int:
- tokens = lines[start].strip().split()
- name = tokens[2]
- proto = tokens[3] if len(tokens) > 3 else ""
- members: list[dict[str, Any]] = []
- i = start + 1
- while i < len(lines) and lines[i].startswith(" "):
- sub = lines[i].strip()
- if sub.startswith("service-object "):
- parts = sub.split()
- if len(parts) >= 4 and parts[2] == "eq":
- members.append({"proto": parts[1], "port": parts[3]})
- elif len(parts) >= 5 and parts[2] == "range":
- members.append({"proto": parts[1], "port_range": (parts[3], parts[4])})
- elif sub.startswith("port-object "):
- parts = sub.split()
- if parts[1] == "eq":
- members.append({"proto": proto, "port": parts[2]})
- elif parts[1] == "range":
- members.append({"proto": proto, "port_range": (parts[2], parts[3])})
- i += 1
- result["service_groups"][name] = {"protocol": proto, "members": members}
- return i
-
-
-def _parse_protocol_group(lines: list[str], start: int, result: dict[str, Any]) -> int:
- name = lines[start].strip().split()[2]
- members: list[str] = []
- i = start + 1
- while i < len(lines) and lines[i].startswith(" "):
- sub = lines[i].strip()
- if sub.startswith("protocol-object "):
- members.append(sub.split()[1])
- i += 1
- result["protocol_groups"][name] = members
- return i
-
-
-def _parse_icmp_type_group(lines: list[str], start: int, result: dict[str, Any]) -> int:
- name = lines[start].strip().split()[2]
- members: list[str] = []
- i = start + 1
- while i < len(lines) and lines[i].startswith(" "):
- sub = lines[i].strip()
- if sub.startswith("icmp-object "):
- members.append(sub.split()[1])
- i += 1
- result["icmp_type_groups"][name] = members
- return i
-
-
-def _parse_access_list(lines: list[str], start: int, result: dict[str, Any]) -> int:
- tokens = lines[start].strip().split()
- acl_name = tokens[1]
- rest = tokens[2:]
- if rest and rest[0] == "remark":
- remark = " ".join(rest[1:])
- result["acls"].setdefault(acl_name, []).append({"remark": remark})
- return start + 1
- rule: dict[str, Any] = {}
- if rest and rest[0] == "extended":
- action = rest[1]
- proto = rest[2]
- idx = 3
- src, idx = _parse_address(rest, idx)
- dst, idx = _parse_address(rest, idx)
- rule.update({
- "action": action,
- "protocol": proto,
- "source": src,
- "destination": dst,
- })
- if idx < len(rest):
- rule["service"] = " ".join(rest[idx:])
- else:
- rule["raw"] = " ".join(rest)
- result["acls"].setdefault(acl_name, []).append(rule)
- return start + 1
-
-
-def _parse_access_group(stripped: str, result: dict[str, Any]) -> None:
- parts = stripped.split()
- entry = {"acl": parts[1], "direction": parts[2]}
- if len(parts) >= 5 and parts[3] == "interface":
- entry["interface"] = parts[4]
- result["access_groups"].append(entry)
-
-
-def parse_asa_config(config: str) -> dict[str, Any]:
- """Convert raw ASA configuration text to a normalized Python dict."""
-
- lines = config.splitlines()
- result: dict[str, Any] = {
- "hostname": None,
- "interfaces": [],
- "time_ranges": {},
- "network_objects": {},
- "network_groups": {},
- "service_groups": {},
- "protocol_groups": {},
- "icmp_type_groups": {},
- "acls": {},
- "access_groups": [],
- }
-
- i = 0
- while i < len(lines):
- stripped = lines[i].strip()
- if not stripped or stripped.startswith("!"):
- i += 1
- continue
- if stripped.startswith("hostname"):
- result["hostname"] = stripped.split()[1]
- elif stripped.startswith("interface "):
- i = _parse_interface(lines, i, result)
- continue
- elif stripped.startswith("time-range "):
- i = _parse_time_range(lines, i, result)
- continue
- elif stripped.startswith("object network"):
- i = _parse_network_object(lines, i, result)
- continue
- elif stripped.startswith("object-group network"):
- i = _parse_network_group(lines, i, result)
- continue
- elif stripped.startswith("object-group service"):
- i = _parse_service_group(lines, i, result)
- continue
- elif stripped.startswith("object-group protocol"):
- i = _parse_protocol_group(lines, i, result)
- continue
- elif stripped.startswith("object-group icmp-type"):
- i = _parse_icmp_type_group(lines, i, result)
- continue
- elif stripped.startswith("access-list "):
- i = _parse_access_list(lines, i, result)
- continue
- elif stripped.startswith("access-group "):
- _parse_access_group(stripped, result)
- i += 1
-
- return result
diff --git a/roles/importer/files/importer/ciscofirepowerdomain7ff/cifp_getter.py b/roles/importer/files/importer/ciscofirepowerdomain7ff/cifp_getter.py
deleted file mode 100644
index 2c565f01ca..0000000000
--- a/roles/importer/files/importer/ciscofirepowerdomain7ff/cifp_getter.py
+++ /dev/null
@@ -1,94 +0,0 @@
-# library for API get functions
-import base64
-from typing import Dict
-from fwo_log import getFwoLogger
-import requests.packages
-import requests
-import json
-import fwo_globals
-from fwo_exception import FwLoginFailed
-
-auth_token = ""
-
-def api_call(url, params = {}, headers = {}, json_payload = {}, auth_token = '', show_progress=False, method='get'):
- logger = getFwoLogger()
- request_headers = {'Content-Type': 'application/json'}
- for header_key in headers:
- request_headers[header_key] = headers[header_key]
- if auth_token != '':
- request_headers["X-auth-access-token"] = auth_token
-
- if method == "post":
- response = requests.post(url, params=params, data=json.dumps(json_payload), headers=request_headers,
- verify=fwo_globals.verify_certs)
- elif method == "get":
- response = requests.get(url, params=params, data=json.dumps(json_payload), headers=request_headers,
- verify=fwo_globals.verify_certs)
- else:
- raise Exception("unknown HTTP method found in cifp_getter")
-
- if response is None:
- if 'pass' in json.dumps(json_payload):
- exception_text = "error while sending api_call containing credential information to url '" + \
- str(url)
- else:
- exception_text = "error while sending api_call to url '" + str(url) + "' with payload '" + json.dumps(
- json_payload, indent=2) + "' and headers: '" + json.dumps(request_headers, indent=2)
- raise Exception(exception_text)
- if (len(response.content) > 0):
- body_json = response.json()
- else:
- body_json = {}
-
- if fwo_globals.debug_level > 2:
- if 'pass' in json.dumps(json_payload):
- logger.debug("api_call containing credential information to url '" +
- str(url) + " - not logging query")
- else:
- logger.debug("api_call to url '" + str(url) + "' with payload '" + json.dumps(
- json_payload, indent=2) + "' and headers: '" + json.dumps(request_headers, indent=2))
-
- return response.headers, body_json
-
-def login(user, password, api_host, api_port):
- base_url = 'https://' + api_host + ':' + str(api_port) + '/api/'
- try:
- headers, _ = api_call(base_url + "fmc_platform/v1/auth/generatetoken", method="post", headers={"Authorization" : "Basic " + str(base64.b64encode((user + ":" + password).encode('utf-8')), 'utf-8')})
- except Exception as e:
- raise FwLoginFailed(
- "Cisco Firepower login ERROR: host=" + str(api_host) + ":" + str(api_port) + " Message: " + str(e)) from None
- if headers.get("X-auth-access-token") == None: # leaving out payload as it contains pwd
- raise FwLoginFailed(
- "Cisco Firepower login ERROR: host=" + str(api_host) + ":" + str(api_port)) from None
- if fwo_globals.debug_level > 2:
- logger = getFwoLogger()
- logger.debug("Login successful. Received auth token: " + headers["X-auth-access-token"])
- return headers.get("X-auth-access-token"), headers.get("DOMAINS")
-
-# TODO Is there an logout?
-def logout(v_url, sid, method='exec'):
- return
- # logger = getFwoLogger()
- # payload = {"params": [{}]}
-
- # response = api_call(v_url, 'sys/logout', payload, sid, method=method)
- # if "result" in response and "status" in response["result"][0] and "code" in response["result"][0]["status"] and response["result"][0]["status"]["code"] == 0:
- # logger.debug("successfully logged out")
- # else:
- # raise Exception("cifp_getter ERROR: did not get status code 0 when logging out, " +
- # "api call: url: " + str(v_url) + ", + payload: " + str(payload))
-
-def update_config_with_cisco_api_call(session_id, api_base_url, api_path, parameters={}, payload={}, show_progress=False, limit: int=1000, method="get"):
- offset = 0
- limit = 1000
- returned_new_data = True
- full_result = []
- while returned_new_data:
- parameters["offset"] = offset
- parameters["limit"] = limit
- result = api_call(api_base_url + "/" + api_path, auth_token=session_id, params=parameters, json_payload=payload, show_progress=show_progress, method=method)[1]
- returned_new_data = result["paging"]["count"] > 0
- if returned_new_data:
- full_result.extend(result["items"])
- offset += limit
- return full_result
\ No newline at end of file
diff --git a/roles/importer/files/importer/ciscofirepowerdomain7ff/cifp_network.py b/roles/importer/files/importer/ciscofirepowerdomain7ff/cifp_network.py
deleted file mode 100644
index a78b47d87f..0000000000
--- a/roles/importer/files/importer/ciscofirepowerdomain7ff/cifp_network.py
+++ /dev/null
@@ -1,103 +0,0 @@
-from asyncio.log import logger
-import random
-
-from fwo_log import getFwoLogger
-from fwo_const import list_delimiter
-from netaddr import IPAddress
-
-def normalize_nwobjects(full_config, config2import, import_id, jwt=None, mgm_id=None):
- logger = getFwoLogger()
- nw_objects = []
- for obj_orig in full_config["networkObjects"]:
- nw_objects.append(parse_object(obj_orig, import_id))
- for obj_grp_orig in full_config["networkObjectGroups"]:
- obj_grp = extract_base_object_infos(obj_grp_orig, import_id)
- obj_grp["obj_typ"] = "group"
- obj_grp["obj_member_refs"], obj_grp["obj_member_names"] = parse_obj_group(obj_grp_orig, import_id, nw_objects)
- nw_objects.append(obj_grp)
- config2import['network_objects'] = nw_objects
-
-def parse_obj_group(orig_grp, import_id, nw_objects, id = None):
- refs = []
- names = []
- if "literals" in orig_grp:
- if id == None:
- id = orig_grp["id"] if "id" in orig_grp else random.random()
- for orig_literal in orig_grp["literals"]:
- literal = parse_object(orig_literal, import_id)
- literal["obj_uid"] += "_" + str(id)
- nw_objects.append(literal)
- names.append(orig_literal["value"])
- refs.append(literal["obj_uid"])
- if "objects" in orig_grp:
- for orig_obj in orig_grp["objects"]:
- if "type" in orig_obj:
- if (orig_obj["type"] != "NetworkGroup" and orig_obj["type"] != "Host" and
- orig_obj["type"] != "Network" and orig_obj["type"] != "Range" and
- orig_obj["type"] != "FQDN"):
- logger = getFwoLogger()
- logger.warn("Unknown network object type found: \"" + orig_obj["type"] + "\". Skipping.")
- break
- names.append(orig_obj["name"])
- refs.append(orig_obj["id"])
-
- return list_delimiter.join(refs), list_delimiter.join(names)
-
-def extract_base_object_infos(obj_orig, import_id):
- logger = getFwoLogger()
- obj = {}
- if "id" in obj_orig:
- obj["obj_uid"] = obj_orig['id']
- else:
- obj["obj_uid"] = obj_orig["value"]
- if "name" in obj_orig:
- obj["obj_name"] = obj_orig["name"]
- else:
- obj["obj_name"] = obj_orig["value"]
- if 'description' in obj_orig:
- obj["obj_comment"] = obj_orig["description"]
- if 'color' in obj_orig:
- # TODO Do colors exist?
- logger.debug("colors exist :)")
- obj['control_id'] = import_id
- return obj
-
-def parse_object(obj_orig, import_id):
- obj = extract_base_object_infos(obj_orig, import_id)
- if obj_orig["type"] == "Network": # network
- obj["obj_typ"] = "network"
- if "value" in obj_orig:
- cidr = obj_orig["value"].split("/")
- if str.isdigit(cidr[1]):
- obj['obj_ip'] = cidr[0] + "/" + cidr[1]
- else: # not real cidr (netmask after /)
- obj['obj_ip'] = cidr[0] + "/" + str(IPAddress(cidr[1]).netmask_bits())
- else:
- logger.warn("missing value field in object - skipping: " + str(obj_orig))
- obj['obj_ip'] = "0.0.0.0"
- elif obj_orig["type"] == "Host": # host
- obj["obj_typ"] = "host"
- if "value" in obj_orig:
- obj["obj_ip"] = obj_orig["value"]
- if obj_orig["value"].find(":") != -1: # ipv6
- if obj_orig["value"].find("/") == -1:
- obj["obj_ip"] += "/128"
- else: # ipv4
- if obj_orig["value"].find("/") == -1:
- obj["obj_ip"] += "/32"
- else:
- logger.warn("missing value field in object - skipping: " + str(obj_orig))
- obj['obj_ip'] = "0.0.0.0/0"
- elif obj_orig["type"] == "Range": # ip range
- obj['obj_typ'] = 'ip_range'
- ip_range = obj_orig['value'].split("-")
- obj['obj_ip'] = ip_range[0]
- obj['obj_ip_end'] = ip_range[1]
- elif obj_orig["type"] == "FQDN": # fully qualified domain name
- obj['obj_typ'] = 'network'
- obj['obj_ip'] = "0.0.0.0/0"
- else: # unknown type
- obj["obj_name"] = obj["obj_name"] + " [not supported]"
- obj['obj_typ'] = 'network'
- obj['obj_ip'] = "0.0.0.0/0"
- return obj
diff --git a/roles/importer/files/importer/ciscofirepowerdomain7ff/cifp_rule.py b/roles/importer/files/importer/ciscofirepowerdomain7ff/cifp_rule.py
deleted file mode 100644
index f2e64a4909..0000000000
--- a/roles/importer/files/importer/ciscofirepowerdomain7ff/cifp_rule.py
+++ /dev/null
@@ -1,85 +0,0 @@
-
-from cifp_service import parse_svc_group
-from cifp_network import parse_obj_group
-import cifp_getter
-from fwo_log import getFwoLogger
-
-rule_access_scope_v4 = ['rules_global_header_v4',
- 'rules_adom_v4', 'rules_global_footer_v4']
-rule_access_scope_v6 = ['rules_global_header_v6',
- 'rules_adom_v6', 'rules_global_footer_v6']
-rule_access_scope = rule_access_scope_v6 + rule_access_scope_v4
-rule_nat_scope = ['rules_global_nat', 'rules_adom_nat']
-rule_scope = rule_access_scope + rule_nat_scope
-
-def getAccessPolicy(sessionId, api_url, config, device, limit):
- access_policy = device["accessPolicy"]["id"]
- domain = device["domain"]
- logger = getFwoLogger()
-
- device["rules"] = cifp_getter.update_config_with_cisco_api_call(sessionId, api_url,
- "fmc_config/v1/domain/" + domain + "/policy/accesspolicies/" + access_policy + "/accessrules", parameters={"expanded": True}, limit=limit)
-
- return
-
-def normalize_access_rules(full_config, config2import, import_id, mgm_details={}, jwt=None):
- any_nw_svc = {"svc_uid": "any_svc_placeholder", "svc_name": "Any", "svc_comment": "Placeholder service.",
- "svc_typ": "simple", "ip_proto": -1, "svc_port": 0, "svc_port_end": 65535, "control_id": import_id}
- any_nw_object = {"obj_uid": "any_obj_placeholder", "obj_name": "Any", "obj_comment": "Placeholder object.",
- "obj_typ": "network", "obj_ip": "0.0.0.0/0", "control_id": import_id}
- config2import["service_objects"].append(any_nw_svc)
- config2import["network_objects"].append(any_nw_object)
-
- rules = []
- for device in full_config["devices"]:
- access_policy = device["accessPolicy"]
- rule_number = 0
- for rule_orig in device["rules"]:
- rule = {'rule_src': 'any', 'rule_dst': 'any', 'rule_svc': 'any',
- 'rule_src_refs': 'any_obj_placeholder', 'rule_dst_refs': 'any_obj_placeholder',
- 'rule_svc_refs': 'any_svc_placeholder'}
- rule['control_id'] = import_id
- rule['rulebase_name'] = access_policy["name"]
- rule["rule_uid"] = rule_orig["id"]
- rule["rule_name"] = rule_orig["name"]
- rule['rule_type'] = "access"
- rule['rule_num'] = rule_number
- rule['rule_installon'] = None
- rule['parent_rule_id'] = None
- rule['rule_time'] = None
- rule['rule_implied'] = False
-
- if 'description' in rule_orig:
- rule['rule_comment'] = rule_orig['description']
- else:
- rule["rule_comment"] = None
- if rule_orig["action"] == "ALLOW":
- rule["rule_action"] = "Accept"
- elif rule_orig["action"] == "BLOCK":
- rule["rule_action"] = "Drop"
- elif rule_orig["action"] == "TRUST":
- rule["rule_action"] = "Accept" #TODO More specific?
- elif rule_orig["action"] == "MONITOR":
- continue #TODO No access rule (just tracking and logging)
- if rule_orig["enableSyslog"]:
- rule["rule_track"] = "Log"
- else:
- rule["rule_track"] = "None"
- rule["rule_disabled"] = not rule_orig["enabled"]
-
- if "sourceNetworks" in rule_orig:
- rule['rule_src_refs'], rule["rule_src"] = parse_obj_group(rule_orig["sourceNetworks"], import_id, config2import['network_objects'], rule["rule_uid"])
- if "destinationNetworks" in rule_orig:
- rule['rule_dst_refs'], rule["rule_dst"] = parse_obj_group(rule_orig["destinationNetworks"], import_id, config2import['network_objects'], rule["rule_uid"])
- # TODO source ports
- if "destinationPorts" in rule_orig:
- rule["rule_svc_refs"], rule["rule_svc"] = parse_svc_group(rule_orig["destinationPorts"], import_id, config2import['service_objects'], rule["rule_uid"])
-
- rule["rule_src_neg"] = False
- rule["rule_dst_neg"] = False
- rule["rule_svc_neg"] = False
-
- rule_number += 1
- rules.append(rule)
-
- config2import['rules'] = rules
diff --git a/roles/importer/files/importer/ciscofirepowerdomain7ff/cifp_service.py b/roles/importer/files/importer/ciscofirepowerdomain7ff/cifp_service.py
deleted file mode 100644
index 6245ae2241..0000000000
--- a/roles/importer/files/importer/ciscofirepowerdomain7ff/cifp_service.py
+++ /dev/null
@@ -1,86 +0,0 @@
-import random
-from fwo_const import list_delimiter
-
-
-def normalize_svcobjects(full_config, config2import, import_id):
- svc_objects = []
- for svc_orig in full_config["serviceObjects"]:
- svc_objects.append(parse_svc(svc_orig, import_id))
- for svc_grp_orig in full_config["serviceObjectGroups"]:
- svc_grp = extract_base_svc_infos(svc_grp_orig, import_id)
- svc_grp["svc_typ"] = "group"
- svc_grp["svc_member_refs"] , svc_grp["svc_member_names"] = parse_svc_group(svc_grp_orig, import_id, svc_objects)
- svc_objects.append(svc_grp)
- config2import['service_objects'] = svc_objects
-
-def extract_base_svc_infos(svc_orig, import_id):
- svc = {}
- if "id" in svc_orig:
- svc["svc_uid"] = svc_orig["id"]
- else:
- svc["svc_uid"] = svc_orig["protocol"]
- if "port" in svc_orig:
- svc["svc_uid"] += "_" + svc_orig["port"]
- if "name" in svc_orig:
- svc["svc_name"] = svc_orig["name"]
- else:
- svc["svc_name"] = svc_orig["protocol"]
- if "port" in svc_orig:
- svc["svc_name"] += "_" + svc_orig["port"]
- if "svc_comment" in svc_orig:
- svc["svc_comment"] = svc_orig["comment"]
- svc["svc_timeout"] = None
- svc["svc_color"] = None
- svc["control_id"] = import_id
- return svc
-
-def parse_svc(orig_svc, import_id):
- svc = extract_base_svc_infos(orig_svc, import_id)
- svc["svc_typ"] = "simple"
- parse_port(orig_svc, svc)
- if orig_svc["type"] == "ProtocolPortObject":
- if orig_svc["protocol"] == "TCP":
- svc["ip_proto"] = 6
- elif orig_svc["protocol"] == "UDP":
- svc["ip_proto"] = 17
- elif orig_svc["protocol"] == "ESP":
- svc["ip_proto"] = 50
- else:
- svc["svc_name"] += " [Protocol \"" + orig_svc["protocol"] + "\" not supported]"
- # TODO Icmp
- # TODO add all protocols
- elif orig_svc["type"] == "PortLiteral":
- svc["ip_proto"] = orig_svc["protocol"]
- else:
- svc["svc_name"] += " [Not supported]"
- return svc
-
-def parse_port(orig_svc, svc):
- if "port" in orig_svc:
- if orig_svc["port"].find("-") != -1: # port range
- port_range = orig_svc["port"].split("-")
- svc["svc_port"] = port_range[0]
- svc["svc_port_end"] = port_range[1]
- else: # single port
- svc["svc_port"] = orig_svc["port"]
- svc["svc_port_end"] = None
-
-def parse_svc_group(orig_svc_grp, import_id, svc_objects, id = None):
- refs = []
- names = []
-
- if "literals" in orig_svc_grp:
- if id == None:
- id = orig_svc_grp["id"] if "id" in orig_svc_grp else random.random()
- for orig_literal in orig_svc_grp["literals"]:
- literal = parse_svc(orig_literal, import_id)
- literal["svc_uid"] += "_" + id
- svc_objects.append(literal)
- names.append(literal["svc_name"])
- refs.append(literal["svc_uid"])
- if "objects" in orig_svc_grp:
- for svc_orig in orig_svc_grp["objects"]:
- refs.append(svc_orig["id"])
- names.append(svc_orig["name"])
- return list_delimiter.join(refs), list_delimiter.join(names)
-
\ No newline at end of file
diff --git a/roles/importer/files/importer/ciscofirepowerdomain7ff/discovery_logging.conf b/roles/importer/files/importer/ciscofirepowerdomain7ff/discovery_logging.conf
deleted file mode 100644
index 139c55a9cb..0000000000
--- a/roles/importer/files/importer/ciscofirepowerdomain7ff/discovery_logging.conf
+++ /dev/null
@@ -1,41 +0,0 @@
-[loggers]
-keys=root,discoveryDebugLogger
-#keys=root,__main__
-
-[handlers]
-keys=consoleHandler,debugFileHandler
-
-[formatters]
-keys=defaultFormatter,debugFileFormatter
-
-[logger_root]
-level=DEBUG
-handlers=consoleHandler
-
-[logger_discoveryDebugLogger]
-#[logger___main__]
-level=DEBUG
-handlers=debugFileHandler
-qualname=discoveryDebugLogger
-#qualname=__main__
-propagate=0
-
-[handler_consoleHandler]
-class=StreamHandler
-level=DEBUG
-formatter=defaultFormatter
-args=(sys.stderr,)
-
-[handler_debugFileHandler]
-class=FileHandler
-level=DEBUG
-formatter=debugFileFormatter
-args=('/tmp/fworch_discovery.log',)
-# args=('/var/log/fworch/discovery.log',)
-
-[formatter_defaultFormatter]
-format=%(levelname)s:%(name)s:%(message)s
-
-[formatter_debugFileFormatter]
-format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
-
diff --git a/roles/importer/files/importer/ciscofirepowerdomain7ff/fwcommon.py b/roles/importer/files/importer/ciscofirepowerdomain7ff/fwcommon.py
deleted file mode 100644
index 3b6a8921f9..0000000000
--- a/roles/importer/files/importer/ciscofirepowerdomain7ff/fwcommon.py
+++ /dev/null
@@ -1,147 +0,0 @@
-import sys
-from common import importer_base_dir
-sys.path.append(importer_base_dir + '/ciscofirepowerdomain7ff')
-import cifp_service
-import cifp_rule
-import cifp_network
-import cifp_getter
-import json
-from fwo_log import getFwoLogger
-
-
-def has_config_changed(full_config, mgm_details, force=False):
- # dummy - may be filled with real check later on
- return True
-
-
-def get_config(config2import, full_config, current_import_id, mgm_details, limit=1000, force=False, jwt=''):
- logger = getFwoLogger()
- if full_config == {}: # no native config was passed in, so getting it from Cisco Management
- parsing_config_only = False
- else:
- parsing_config_only = True
-
- if not parsing_config_only: # no native config was passed in, so getting it from Cisco Management
- cisco_api_url = 'https://' + \
- mgm_details['hostname'] + ':' + \
- str(mgm_details['port']) + '/api'
- sessionId, domains = cifp_getter.login(mgm_details["import_credential"]['user'], mgm_details["import_credential"]['secret'],
- mgm_details['hostname'], mgm_details['port'])
- domain = mgm_details["configPath"]
- if sessionId == None or sessionId == "":
- logger.error(
- 'Did not succeed in logging in to Cisco Firepower API, no sid returned.')
- return 1
- if domain == None or domain == "":
- logger.error(
- 'Configured domain is null or empty.')
- return 1
- scopes = getScopes(domain, json.loads(domains))
- if len(scopes) == 0:
- logger.error(
- "Domain \"" + domain + "\" could not be found. \"" + domain + "\" does not appear to be a domain name or a domain UID.")
- return 1
-
- getDevices(sessionId, cisco_api_url, full_config, limit, scopes, mgm_details["devices"])
- getObjects(sessionId, cisco_api_url, full_config, limit, scopes)
-
- for device in full_config["devices"]:
- cifp_rule.getAccessPolicy(sessionId, cisco_api_url, full_config, device, limit)
- ##cifp_rule.getNatPolicy(sessionId, cisco_api_url, full_config, domain, device, limit) TODO
-
- try: # logout
- cifp_getter.logout(cisco_api_url, sessionId)
- except:
- logger.warning(
- "logout exception probably due to timeout - irrelevant, so ignoring it")
-
- # now we normalize relevant parts of the raw config and write the results to config2import dict
-
- # write normalized networking data to config2import
- # this is currently not written to the database but only used for natting decisions
- # later we will probably store the networking info in the database as well as a basis
- # for path analysis
-
- # normalize_network_data(full_config, config2import, mgm_details)
-
- # cifp_user.normalize_users(
- # full_config, config2import, current_import_id, user_scope)
- cifp_network.normalize_nwobjects(
- full_config, config2import, current_import_id, jwt=jwt, mgm_id=mgm_details['id'])
- cifp_service.normalize_svcobjects(
- full_config, config2import, current_import_id)
- cifp_rule.normalize_access_rules(
- full_config, config2import, current_import_id, mgm_details=mgm_details, jwt=jwt)
- # cifp_rule.normalize_nat_rules(
- # full_config, config2import, current_import_id, jwt=jwt)
- # cifp_network.remove_nat_ip_entries(config2import)
- return 0
-
-def getAllAccessRules(sessionId, api_url, domains):
- for domain in domains:
- domain["access_policies"] = cifp_getter.update_config_with_cisco_api_call(sessionId, api_url,
- "fmc_config/v1/domain/" + domain["uuid"] + "/policy/accesspolicies" , parameters={"expanded": True}, limit=1000)
-
- for access_policy in domain["access_policies"]:
- access_policy["rules"] = cifp_getter.update_config_with_cisco_api_call(sessionId, api_url,
- "fmc_config/v1/domain/" + domain["uuid"] + "/policy/accesspolicies/" + access_policy["id"] + "/accessrules", parameters={"expanded": True}, limit=1000)
- return domains
-
-def getScopes(searchDomain, domains):
- scopes = []
- for domain in domains:
- if domain == domain["uuid"] or domain["name"].endswith(searchDomain):
- scopes.append(domain["uuid"])
- return scopes
-
-def getDevices(sessionId, api_url, config, limit, scopes, devices):
- logger = getFwoLogger()
- # get all devices
- for scope in scopes:
- config["devices"] = cifp_getter.update_config_with_cisco_api_call(sessionId, api_url,
- "fmc_config/v1/domain/" + scope + "/devices/devicerecords", parameters={"expanded": True}, limit=limit)
- for device in config["devices"]:
- if not "domain" in device:
- device["domain"] = scope
- # filter for existent devices
- for cisco_api_device in config["devices"]:
- found = False
- for device in devices:
- if device["name"] == cisco_api_device["name"] or device["name"] == cisco_api_device["id"]:
- found = True
- break
- # remove device if not in fwo api
- if found == False:
- config["devices"].remove(cisco_api_device)
- logger.info("Device \"" + cisco_api_device["name"] + "\" was found but it is not registered in FWO. Ignoring it.")
-
-def getObjects(sessionId, api_url, config, limit, scopes):
- # network objects:
- config["networkObjects"] = []
- config["networkObjectGroups"] = []
- # service objects:
- config["serviceObjects"] = []
- config["serviceObjectGroups"] = []
- # user objects:
- config["userObjects"] = []
- config["userObjectGroups"] = []
-
- # get those objects that exist globally and on domain level
- for scope in scopes:
- # get network objects (groups):
- # for object_type in nw_obj_types:
- config["networkObjects"].extend(cifp_getter.update_config_with_cisco_api_call(sessionId, api_url,
- "fmc_config/v1/domain/" + scope + "/object/networkaddresses", parameters={"expanded": True}, limit=limit))
- config["networkObjectGroups"].extend(cifp_getter.update_config_with_cisco_api_call(sessionId, api_url,
- "fmc_config/v1/domain/" + scope + "/object/networkgroups", parameters={"expanded": True}, limit=limit))
- # get service objects:
- # for object_type in svc_obj_types:
- config["serviceObjects"].extend(cifp_getter.update_config_with_cisco_api_call(sessionId, api_url,
- "fmc_config/v1/domain/" + scope + "/object/ports", parameters={"expanded": True}, limit=limit))
- config["serviceObjectGroups"].extend(cifp_getter.update_config_with_cisco_api_call(sessionId, api_url,
- "fmc_config/v1/domain/" + scope + "/object/portobjectgroups", parameters={"expanded": True}, limit=limit))
- # get user objects:
- config["userObjects"].extend(cifp_getter.update_config_with_cisco_api_call(sessionId, api_url,
- "fmc_config/v1/domain/" + scope + "/object/realmusers", parameters={"expanded": True}, limit=limit))
- config["userObjectGroups"].extend(cifp_getter.update_config_with_cisco_api_call(sessionId, api_url,
- "fmc_config/v1/domain/" + scope + "/object/realmusergroups", parameters={"expanded": True}, limit=limit))
diff --git a/roles/importer/files/importer/common.py b/roles/importer/files/importer/common.py
index 24a4205fd1..6085e1e695 100644
--- a/roles/importer/files/importer/common.py
+++ b/roles/importer/files/importer/common.py
@@ -1,422 +1,330 @@
+import sys
+import time
import traceback
-import sys, time, datetime
-import json, requests, requests.packages
-from socket import gethostname
-import importlib.util
-from fwo_const import importer_base_dir
from pathlib import Path
-sys.path.append(importer_base_dir) # adding absolute path here once
-import fwo_api
-from fwo_log import getFwoLogger
-from fwo_config import readConfig
-from fwo_const import fw_module_name, full_config_size_limit
-from fwo_const import fwo_config_filename, importer_pwd_file, importer_user_name, import_tmp_path
-import fwo_globals
-import jsonpickle
-from fwo_exception import FwoApiLoginFailed, FwoApiFailedLockImport, FwLoginFailed, ImportRecursionLimitReached
-from fwo_base import split_config
-import re
-import fwo_file_import
+from socket import gethostname
+from fw_modules.checkpointR8x.fwcommon import CheckpointR8xCommon
+from fw_modules.ciscoasa9.fwcommon import CiscoAsa9Common
+from fw_modules.fortiadom5ff.fwcommon import FortiAdom5ffCommon
+from fwo_const import IMPORTER_BASE_DIR
+from fwo_log import FWOLogger
+from model_controllers.fwconfig_import_rollback import FwConfigImportRollback
+from model_controllers.management_controller import ManagementController
+from models.fw_common import FwCommon
+from models.import_state import ImportState
+
+if IMPORTER_BASE_DIR not in sys.path:
+ sys.path.append(IMPORTER_BASE_DIR) # adding absolute path here once
+import fwo_file_import
+import fwo_globals
+import fwo_signalling
+from fwo_api_call import FwoApiCall
+from fwo_base import string_is_uri, write_native_config_to_file
+from fwo_const import IMPORT_TMP_PATH
+from fwo_exceptions import (
+ FwLoginFailedError,
+ FwoApiWriteError,
+ FwoImporterError,
+ FwoImporterErrorInconsistenciesError,
+ ImportInterruptionError,
+ ImportRecursionLimitReachedError,
+ ShutdownRequestedError,
+)
+from model_controllers.check_consistency import FwConfigImportCheckConsistency
+from model_controllers.fwconfig_import import FwConfigImport
+from model_controllers.fwconfigmanagerlist_controller import FwConfigManagerListController
+from model_controllers.import_state_controller import ImportStateController
+from models.gateway import Gateway
+from services.enums import Services
+from services.service_provider import ServiceProvider
+
+"""
+ import_management: import a single management (if no import for it is running)
+ if mgmId is that of a super management, it will import all submanagements as well
+ lock mgmt for import via FWORCH API call, generating new import_id
+ check if we need to import (no md5, api call if anything has changed since last import)
+ get complete config (get, enrich, parse)
+ write into json dict write json dict to new table (single entry for complete config)
+ this top level function mainly deals with exception handling
+
+ expects service_provider to be initialized
+"""
+
+
+def import_management(
+ mgm_id: int,
+ api_call: FwoApiCall,
+ ssl_verification: bool,
+ limit: int,
+ clear_management_data: bool,
+ suppress_cert_warnings: bool,
+ file: str | None = None,
+) -> None:
+ fwo_signalling.register_signalling_handlers()
+ service_provider = ServiceProvider()
+ import_state = service_provider.get_global_state().import_state
+ config_importer = FwConfigImport()
+ exception: BaseException | None = None
-class FwConfig():
- ConfigFormat: str
- Config: dict
+ try:
+ _import_management(mgm_id, ssl_verification, file, limit, clear_management_data, suppress_cert_warnings)
+ except FwLoginFailedError as e:
+ exception = e
+ import_state.delete_import() # delete whole import
+ roll_back_exception_handler(import_state, config_importer=config_importer, exc=e, error_text="")
+ except (ImportRecursionLimitReachedError, FwoImporterErrorInconsistenciesError) as e:
+ import_state.delete_import() # delete whole import
+ exception = e
+ except (KeyboardInterrupt, ImportInterruptionError, ShutdownRequestedError) as e:
+ roll_back_exception_handler(
+ import_state, config_importer=config_importer, exc=e, error_text="shutdown requested"
+ )
+ raise
+ except (FwoApiWriteError, FwoImporterError) as e:
+ exception = e
+ roll_back_exception_handler(import_state, config_importer=config_importer, exc=e, error_text="")
+ except ValueError:
+ raise
+ except Exception as e:
+ exception = e
+ handle_unexpected_exception(import_state=import_state, config_importer=config_importer, e=e)
+ finally:
+ try:
+ api_call.complete_import(import_state.state, exception)
+ ServiceProvider().dispose_service(Services.UID2ID_MAPPER, import_state.state.import_id)
+ except Exception as e:
+ FWOLogger.error(f"Error during import completion: {e!s}")
+
+
+def _import_management(
+ mgm_id: int,
+ ssl_verification: bool,
+ file: str | None,
+ limit: int,
+ clear_management_data: bool,
+ suppress_cert_warnings: bool,
+) -> None:
+ config_normalized: FwConfigManagerListController
- def __init__(self, configFormat=None, config=None):
- if configFormat is not None:
- self.ConfigFormat = configFormat
+ config_changed_since_last_import = True
+ service_provider = ServiceProvider()
+ import_state = service_provider.get_global_state().import_state
+ config_importer = FwConfigImport()
+ FWOLogger.debug(f"import_management - ssl_verification: {ssl_verification}", 9)
+ FWOLogger.debug(f"import_management - suppress_cert_warnings_in: {suppress_cert_warnings}", 9)
+ FWOLogger.debug(f"import_management - limit: {limit}", 9)
+
+ if import_state.state.mgm_details.import_disabled and not import_state.state.force_import:
+ FWOLogger.info(f"import_management - import disabled for mgm {mgm_id!s} - skipping")
+ return
+
+ if import_state.state.mgm_details.importer_hostname != gethostname() and not import_state.state.force_import:
+ FWOLogger.info(
+ f"import_management - this host ( {gethostname()}) is not responsible for importing management {mgm_id!s}"
+ )
+ import_state.state.responsible_for_importing = False
+ return
+
+ Path(IMPORT_TMP_PATH).mkdir(parents=True, exist_ok=True) # make sure tmp path exists
+ gateways = ManagementController.build_gateway_list(import_state.state.mgm_details)
+
+ import_state.state.import_id = import_state.api_call.set_import_lock(
+ import_state.state.mgm_details, import_state.state.is_full_import, import_state.state.is_initial_import
+ )
+ FWOLogger.info(
+ f"starting import of management {import_state.state.mgm_details.name} ({mgm_id!s}), import_id={import_state.state.import_id!s}"
+ )
+
+ if clear_management_data:
+ config_normalized = config_importer.clear_management()
+ else:
+ # get config
+ config_changed_since_last_import, config_normalized = get_config_top_level(import_state, file, gateways)
+
+ # write normalized config to file
+ config_normalized.store_full_normalized_config_to_file(import_state.state)
+ FWOLogger.debug(
+ "import_management - getting config total duration "
+ + str(int(time.time()) - import_state.state.start_time)
+ + "s"
+ )
+
+ # check config consistency and import it
+ if config_changed_since_last_import or import_state.state.force_import:
+ FwConfigImportCheckConsistency(import_state, config_normalized).check_config_consistency(config_normalized)
+ config_importer.import_management_set(service_provider, config_normalized)
+
+ # delete data that has passed the retention time
+ # TODO: replace by deletion of old data with removed date > retention?
+ if (
+ not clear_management_data
+ and import_state.state.data_retention_days < import_state.state.days_since_last_full_import
+ ):
+ config_importer.delete_old_imports() # delete all imports of the current management before the last but one full import
+
+
+def handle_unexpected_exception(
+ import_state: ImportStateController | None = None,
+ config_importer: FwConfigImport | None = None,
+ e: Exception | None = None,
+):
+ if (
+ "importState" in locals()
+ and import_state is not None
+ and "configImporter" in locals()
+ and config_importer is not None
+ ):
+ roll_back_exception_handler(import_state, config_importer=config_importer, exc=e)
+
+
+def roll_back_exception_handler(
+ import_state: ImportStateController,
+ config_importer: FwConfigImport | None = None,
+ exc: BaseException | None = None,
+ error_text: str = "",
+):
+ try:
+ if fwo_globals.shutdown_requested:
+ FWOLogger.warning("Shutdown requested.")
+ elif error_text != "":
+ FWOLogger.error(f"Exception: {error_text}")
+ elif exc is not None:
+ FWOLogger.error(f"Exception: {type(exc).__name__}")
else:
- self.ConfigFormat = "Normalized" # default format
- if config is not None:
- self.Config = config
+ FWOLogger.error("Exception: no exception provided")
+ if "configImporter" in locals() and config_importer is not None:
+ FwConfigImportRollback().rollback_current_import(
+ import_state=import_state.state, fwo_api_call=import_state.api_call
+ )
else:
- self.Config = {}
-
- @classmethod
- def from_json(cls, json_dict):
- ConfigFormat = json_dict['config-type']
- Config = json_dict['config']
- return cls(ConfigFormat, Config)
-
- def __str__(self):
- return f"{self.ConfigFormat}({str(self.Config)})"
-
-class ManagementDetails():
- Id: int
- Name: str
- Hostname: str
- ImportDisabled: bool
- Devices: dict
- ImporterHostname: str
- DeviceTypeName: str
- DeviceTypeVersion: str
-
- def __init__(self, hostname, id, importDisabled, devices, importerHostname, name, deviceTypeName, deviceTypeVersion):
- self.Hostname = hostname
- self.Id = id
- self.ImportDisabled = importDisabled
- self.Devices = devices
- self.ImporterHostname = importerHostname
- self.Name = name
- self.DeviceTypeName = deviceTypeName
- self.DeviceTypeVersion = deviceTypeVersion
-
- @classmethod
- def from_json(cls, json_dict):
- Hostname = json_dict['hostname']
- Id = json_dict['id']
- ImportDisabled = json_dict['importDisabled']
- Devices = json_dict['devices']
- ImporterHostname = json_dict['importerHostname']
- Name = json_dict['name']
- DeviceTypeName = json_dict['deviceType']['name']
- DeviceTypeVersion = json_dict['deviceType']['version']
- return cls(Hostname, Id, ImportDisabled, Devices, ImporterHostname, Name, DeviceTypeName, DeviceTypeVersion)
-
- def __str__(self):
- return f"{self.Hostname}({self.Id})"
-
-
-"""Used for storing state during import process per management"""
-class ImportState():
- ErrorCount: int
- AnyChangeCount: int
- RuleChangeCount: int
- ErrorString: str
- StartTime: int
- DebugLevel: int
- Config2import: dict
- ConfigChangedSinceLastImport: bool
- FwoConfig: dict
- MgmDetails: ManagementDetails
- FullMgmDetails: dict
- ImportId: int
- Jwt: str
- ImportFileName: str
- ForceImport: str
-
-
- def __init__(self, debugLevel, configChangedSinceLastImport, fwoConfig, mgmDetails, jwt, force):
- self.ErrorCount = 0
- self.setAnyChangeCounter(0)
- self.setRuleChangeCounter(0)
- self.ErrorString = ''
- self.StartTime = int(time.time())
- self.DebugLevel = debugLevel
- self.Config2import = { "network_objects": [], "service_objects": [], "user_objects": [], "zone_objects": [], "rules": [] }
- self.ConfigChangedSinceLastImport = configChangedSinceLastImport
- self.FwoConfig = fwoConfig
- self.MgmDetails = ManagementDetails.from_json(mgmDetails)
- self.FullMgmDetails = mgmDetails
- self.ImportId = None
- self.Jwt = jwt
- self.ImportFileName = None
- self.ForceImport = force
-
- def __str__(self):
- return f"{str(self.MgmDetails)}(ImportId: {self.ImportId})"
-
- def setImportFileName(self, importFileName):
- self.ImportFileName = importFileName
-
- def setImportId(self, importId):
- self.ImportId = importId
-
- def setAnyChangeCounter(self, changeNo):
- self.AnyChangeCount = changeNo
-
- def setRuleChangeCounter(self, changeNo):
- self.RuleChangeCount = changeNo
-
- def setErrorCounter(self, errorNo):
- self.ErrorCount = errorNo
-
- def setErrorString(self, errorStr):
- self.ErrorString = errorStr
-
-
-# import_management: import a single management (if no import for it is running)
-# lock mgmt for import via FWORCH API call, generating new import_id y
-# check if we need to import (no md5, api call if anything has changed since last import)
-# get complete config (get, enrich, parse)
-# write into json dict write json dict to new table (single entry for complete config)
-# trigger import from json into csv and from there into destination tables
-# release mgmt for import via FWORCH API call (also removing import_id y data from import_tables?)
-# no changes: remove import_control?
-def import_management(mgmId=None, ssl_verification=None, debug_level_in=0,
- limit=150, force=False, clearManagementData=False, suppress_cert_warnings_in=None,
- in_file=None):
-
- importState = initializeImport(mgmId, debugLevel=debug_level_in, force=force)
- logger = getFwoLogger()
- config_changed_since_last_import = True
+ FWOLogger.info("No configImporter found, skipping rollback.")
+ import_state.delete_import() # delete whole import
+ except Exception as rollbackError:
+ FWOLogger.error(f"Error during rollback: {type(rollbackError).__name__} - {rollbackError}")
+
+
+def get_config_top_level(
+ import_state: ImportStateController, in_file: str | None = None, gateways: list[Gateway] | None = None
+) -> tuple[bool, FwConfigManagerListController]:
+ config_from_file = FwConfigManagerListController.generate_empty_config()
+ if gateways is None:
+ gateways = []
+ if in_file is not None or string_is_uri(import_state.state.mgm_details.hostname):
+ ### getting config from file ######################
+ if in_file is None:
+ in_file = import_state.state.mgm_details.hostname
+ _, config_from_file = import_from_file(import_state, in_file)
+ if not config_from_file.is_native_non_empty():
+ config_has_changes = True
+ return config_has_changes, config_from_file
+ # else we feed the native config back into the importer process for normalization
+ ### getting config from firewall manager API ######
+ return get_config_from_api(import_state, config_from_file)
+
+
+def import_from_file(
+ import_state: ImportStateController, file_name: str = ""
+) -> tuple[bool, FwConfigManagerListController]:
+ FWOLogger.debug(f"import_management - not getting config from API but from file: {file_name}")
- if type(importState)==ImportState:
- if importState.MgmDetails.ImportDisabled and not importState.ForceImport:
- logger.info("import_management - import disabled for mgm " + str(mgmId))
- else:
- Path(import_tmp_path).mkdir(parents=True, exist_ok=True) # make sure tmp path exists
- package_list = []
- for dev in importState.MgmDetails.Devices:
- package_list.append(dev['package_name'])
-
- # only run if this is the correct import module
- if importState.MgmDetails.ImporterHostname != gethostname() and not importState.ForceImport:
- logger.info("import_management - this host (" + gethostname() + ") is not responsible for importing management " + str(mgmId))
- return ""
-
- setImportLock(importState)
- logger.info("starting import of management " + importState.MgmDetails.Name + '(' + str(mgmId) + "), import_id=" + str(importState.ImportId))
- config2import = {}
-
- configObj = FwConfig()
- if clearManagementData:
- logger.info('this import run will reset the configuration of this management to "empty"')
- config2import = importState.Config2import
- else:
- if in_file is None: # if the host name is an URI, do not connect to an API but simply read the config from this URI
- if stringIsUri(importState.MgmDetails.Hostname):
- importState.setImportFileName(importState.MgmDetails.Hostname)
- else:
- importState.setImportFileName(in_file)
- if importState.ImportFileName is not None:
- configFromFile = fwo_file_import.readJsonConfigFromFile(importState, configObj.Config)
- if 'config-format' in configFromFile:
- if 'fw-config' in configFromFile:
- configObj = FwConfig(configFromFile['config-format'], configFromFile['fw-config'])
- else:
- configObj = FwConfig(configFromFile['config-format'], configFromFile)
- else: # assuming native config
- if 'network_objects' in configFromFile and 'service_objects' in configFromFile:
- configObj = FwConfig('normalized', configFromFile) # assuming plain old normalized config
- config2import = configFromFile # TODO: switch to objects
- else:
- configObj = FwConfig('native', configFromFile) # assuming old native config
- # need to normalize config (actually not reading from API but already read from file)
- config_changed_since_last_import = get_config_from_api(importState, configObj.Config, config2import)
-
- if configObj.ConfigFormat == 'normalized':
- # before importing from normalized config file, we need to replace the import id:
- replace_import_id(configObj.Config, importState.ImportId)
- else:
- ### getting config from firewall manager ######################
- config_changed_since_last_import = get_config_from_api(importState, configObj.Config, config2import)
- if (importState.DebugLevel>8): # dump full native config read from fw API
- logger.info(json.dumps(configObj.Config, indent=2))
- print("now dumping normalized#config: " + json.dumps(config2import, indent=2))
- print("finished printing normalized#config")
- if (importState.DebugLevel>7): # dump full normalized config
- logger.info(json.dumps(config2import, indent=2))
-
- time_get_config = int(time.time()) - importState.StartTime
- logger.debug("import_management - getting config total duration " + str(int(time.time()) - importState.StartTime) + "s")
-
- if config_changed_since_last_import or importState.ForceImport:
- try: # now we import the config via API chunk by chunk:
- for config_chunk in split_config(config2import, importState.ImportId, mgmId):
- importState.ErrorCount += fwo_api.import_json_config(importState, config_chunk)
- fwo_api.update_hit_counter(importState, config_chunk)
- except:
- logger.error("import_management - unspecified error while importing config via FWO API: " + str(traceback.format_exc()))
- raise
- time_write2api = int(time.time()) - time_get_config - importState.StartTime
- logger.debug("import_management - writing config to API and stored procedure import duration: " + str(time_write2api) + "s")
-
- error_from_imp_control = "assuming error"
- try: # checking for errors during stored_procedure db imort in import_control table
- error_from_imp_control = fwo_api.get_error_string_from_imp_control(importState, {"importId": importState.ImportId})
- except:
- logger.error("import_management - unspecified error while getting error string: " + str(traceback.format_exc()))
-
- if error_from_imp_control != None and error_from_imp_control != [{'import_errors': None}]:
- importState.setErrorCounter(importState.ErrorCount + 1)
- importState.setErrorString(importState.ErrorString + str(error_from_imp_control))
- # todo: if no objects found at all: at least throw a warning
-
- try: # get change count from db
- # temporarily only count rule changes until change report also includes other changes
- rule_change_count = fwo_api.count_rule_changes_per_import(importState.FwoConfig['fwo_api_base_url'], importState.Jwt, importState.ImportId)
- any_change_count = fwo_api.count_any_changes_per_import(importState.FwoConfig['fwo_api_base_url'], importState.Jwt, importState.ImportId)
- importState.setAnyChangeCounter(any_change_count)
- importState.setRuleChangeCounter(rule_change_count)
- except:
- logger.error("import_management - unspecified error while getting change count: " + str(traceback.format_exc()))
- raise
-
- try: # calculate config sizes
- full_config_size = sys.getsizeof(json.dumps(configObj.Config))
- config2import_size = sys.getsizeof(jsonpickle.dumps(config2import))
- logger.debug("full_config size: " + str(full_config_size) + " bytes, config2import size: " + str(config2import_size) + " bytes")
- except:
- logger.error("import_management - unspecified error while calculating config sizes: " + str(traceback.format_exc()))
- raise
-
- if (importState.DebugLevel>5 or rule_change_count > 0 or any_change_count > 0 or importState.ErrorCount > 0) and full_config_size < full_config_size_limit: # store full config in case of change or error
- try: # store full config in DB
- importState.setErrorCounter(importState.ErrorCount + fwo_api.store_full_json_config(importState, {
- "importId": importState.ImportId, "mgmId": mgmId, "config": configObj.Config}))
- except:
- logger.error("import_management - unspecified error while storing full config: " + str(traceback.format_exc()))
- raise
- else: # if no changes were found, we skip everything else without errors
- pass
-
- importState.setErrorCounter(fwo_api.complete_import(importState))
-
- return importState.ErrorCount
- else:
- return 1 # error during initial FWO API login attempt
+ config_changed_since_last_import = True
+ set_filename(import_state, file_name=file_name)
-# when we read from a normalized config file, it contains non-matching import ids, so updating them
-# for native configs this function should do nothing
-def replace_import_id(config, current_import_id):
- for tab in ['network_objects', 'service_objects', 'user_objects', 'zone_objects', 'rules']:
- if tab in config:
- for item in config[tab]:
- if 'control_id' in item:
- item['control_id'] = current_import_id
- else: # assuming native config is read
- pass
+ config_from_file = fwo_file_import.read_json_config_from_file(import_state.api_call, import_state.state)
+ return config_changed_since_last_import, config_from_file
-def initializeImport(mgmId, debugLevel=0, suppressCertWarnings=False, sslVerification=False, force=False):
- def check_input_parameters(mgmId):
- if mgmId is None:
- raise BaseException("parameter mgm_id is mandatory")
+def get_module(import_state: ImportState) -> FwCommon:
+ # pick product-specific importer:
+ pkg_name = get_module_package_name(import_state)
+ match pkg_name:
+ case "ciscoasa9":
+ fw_module = CiscoAsa9Common()
+ case "fortiadom5ff":
+ fw_module = FortiAdom5ffCommon()
+ case "checkpointR8x":
+ fw_module = CheckpointR8xCommon()
+ case _:
+ raise FwoImporterError(f"import_management - no fwcommon module found for package name {pkg_name}")
- logger = getFwoLogger()
- check_input_parameters(mgmId)
+ return fw_module
- fwoConfig = readConfig(fwo_config_filename)
- # authenticate to get JWT
- with open(importer_pwd_file, 'r') as file:
- importer_pwd = file.read().replace('\n', '')
- try:
- jwt = fwo_api.login(importer_user_name, importer_pwd, fwoConfig['user_management_api_base_url'])
- except FwoApiLoginFailed as e:
- logger.error(e.message)
- return e.message
- except:
- return "unspecified error during FWO API login"
-
- # set global https connection values
- fwo_globals.setGlobalValues (suppress_cert_warnings_in=suppressCertWarnings, verify_certs_in=sslVerification, debug_level_in=debugLevel)
- if fwo_globals.verify_certs is None: # not defined via parameter
- fwo_globals.verify_certs = fwo_api.get_config_value(fwoConfig['fwo_api_base_url'], jwt, key='importCheckCertificates')=='True'
- if fwo_globals.suppress_cert_warnings is None: # not defined via parameter
- fwo_globals.suppress_cert_warnings = fwo_api.get_config_value(fwoConfig['fwo_api_base_url'], jwt, key='importSuppressCertificateWarnings')=='True'
- if fwo_globals.suppress_cert_warnings: # not defined via parameter
- requests.packages.urllib3.disable_warnings() # suppress ssl warnings only
-
- try: # get mgm_details (fw-type, port, ip, user credentials):
- mgmDetails = fwo_api.get_mgm_details(fwoConfig['fwo_api_base_url'], jwt, {"mgmId": int(mgmId)}, int(debugLevel))
- except:
- logger.error("import_management - error while getting fw management details for mgm=" + str(mgmId) )
+def get_config_from_api(
+ import_state: ImportStateController, config_in: FwConfigManagerListController
+) -> tuple[bool, FwConfigManagerListController]:
+ try: # pick product-specific importer:
+ fw_module = get_module(import_state.state)
+ except Exception:
+ FWOLogger.exception(
+ "import_management - error while loading product specific fwcommon module", traceback.format_exc()
+ )
raise
- # return ImportState (int(debugLevel), True, fwoConfig, mgmDetails)
- return ImportState (
- debugLevel = int(debugLevel),
- configChangedSinceLastImport = True,
- fwoConfig = fwoConfig,
- mgmDetails = mgmDetails,
- jwt = jwt,
- force = force
- )
-
-
-def stringIsUri(s):
- return re.match('http://.+', s) or re.match('https://.+', s) or re.match('file://.+', s)
-
-
-def setImportLock(importState):
- logger = getFwoLogger()
- try: # set import lock
- # url = importState.FwoConfig['fwo_api_base_url']
- url = importState.FwoConfig['fwo_api_base_url']
- mgmId = int(importState.MgmDetails.Id)
- importState.setImportId(fwo_api.lock_import(url, importState.Jwt, {"mgmId": mgmId }))
- except:
- logger.error("import_management - failed to get import lock for management id " + str(mgmId))
- importState.setImportId(-1)
- if importState.ImportId == -1:
- fwo_api.create_data_issue(importState.FwoConfig['fwo_api_base_url'], importState.Jwt, mgm_id=int(importState.MgmDetails.Id), severity=1,
- description="failed to get import lock for management id " + str(mgmId))
- fwo_api.setAlert(url, importState.Jwt, import_id=importState.ImportId, title="import error", mgm_id=str(mgmId), severity=1, role='importer', \
- description="fwo_api: failed to get import lock", source='import', alertCode=15, mgm_details=importState.MgmDetails)
- raise FwoApiFailedLockImport("fwo_api: failed to get import lock for management id " + str(mgmId)) from None
-
-
-def get_config_from_api(importState, full_config_json, config2import, import_tmp_path='.', limit=150):
- logger = getFwoLogger()
- errors_found = 0
-
- try: # pick product-specific importer:
- pkg_name = importState.MgmDetails.DeviceTypeName.lower().replace(' ', '') + importState.MgmDetails.DeviceTypeVersion
- fw_module = importlib.import_module("." + fw_module_name, pkg_name)
- except:
- logger.exception("import_management - error while loading product specific fwcommon module", traceback.format_exc())
- raise
-
- try: # get the config data from the firewall manager's API:
- # check for changes from product-specific FW API
- config_changed_since_last_import = importState.ImportFileName != None or fw_module.has_config_changed(full_config_json, importState.FullMgmDetails, force=importState.ForceImport)
- if config_changed_since_last_import:
- logger.info ( "has_config_changed: changes found or forced mode -> go ahead with getting config, Force = " + str(importState.ForceImport))
- else:
- logger.info ( "has_config_changed: no new changes found")
-
- if config_changed_since_last_import or importState.ForceImport:
- errors_found = fw_module.get_config( # get config from product-specific FW API
- config2import, full_config_json, importState.ImportId, importState.FullMgmDetails,
- limit=limit, force=importState.ForceImport, jwt=importState.Jwt)
- except (FwLoginFailed) as e:
- importState.ErrorString += " login failed: mgm_id=" + str(importState.MgmDetails.Id) + ", mgm_name=" + importState.MgmDetails.Name + ", " + e.message
- importState.ErrorCount += 1
- logger.error(importState.ErrorString)
- fwo_api.delete_import(importState) # deleting trace of not even begun import
- importState.ErrorCount = fwo_api.complete_import(importState)
- raise FwLoginFailed(e.message)
- except ImportRecursionLimitReached as e:
- importState.ErrorString += " recursion limit reached: mgm_id=" + str(importState.MgmDetails.Id) + ", mgm_name=" + importState.MgmDetails.Name + ", " + e.message
- importState.ErrorCount += 1
- logger.error(importState.ErrorString)
- fwo_api.delete_import(importState.Jwt) # deleting trace of not even begun import
- importState.ErrorCount = fwo_api.complete_import(importState)
- raise ImportRecursionLimitReached(e.message)
- except:
- importState.ErrorString += " import_management - unspecified error while getting config: " + str(traceback.format_exc())
- logger.error(importState.ErrorString)
- importState.ErrorCount += 1
- importState.ErrorCount = fwo_api.complete_import(importState)
- raise
+ # check for changes from product-specific FW API, if we are importing from file we assume config changes
+ # TODO: implement real change detection
+ config_changed_since_last_import = fw_module.has_config_changed(
+ config_in, import_state, import_state.state.force_import
+ )
+ if config_changed_since_last_import:
+ FWOLogger.info(
+ "has_config_changed: changes found or forced mode -> go ahead with getting config, Force = "
+ + str(import_state.state.force_import)
+ )
+ else:
+ FWOLogger.info("has_config_changed: no new changes found")
- logger.debug("import_management: get_config completed (including normalization), duration: " + str(int(time.time()) - importState.StartTime) + "s")
+ if config_changed_since_last_import or import_state.state.force_import:
+ # get config from product-specific FW API
+ _, native_config = fw_module.get_config(config_in, import_state)
+ else:
+ native_config = FwConfigManagerListController.generate_empty_config(
+ import_state.state.mgm_details.is_super_manager
+ )
+ if config_in.native_config is None:
+ raise FwoImporterError("import_management: get_config returned no config")
+
+ write_native_config_to_file(import_state.state, config_in.native_config)
+
+ FWOLogger.debug(
+ "import_management: get_config completed (including normalization), duration: "
+ + str(int(time.time()) - import_state.state.start_time)
+ + "s"
+ )
+
+ return config_changed_since_last_import, native_config
+
+
+# transform device name and type to correct package name
+def get_module_package_name(import_state: ImportState):
+ if import_state.mgm_details.device_type_name.lower().replace(" ", "") == "checkpoint":
+ pkg_name = import_state.mgm_details.device_type_name.lower().replace(
+ " ", ""
+ ) + import_state.mgm_details.device_type_version.replace(" ", "").replace("MDS", "")
+ elif import_state.mgm_details.device_type_name.lower() == "fortimanager":
+ pkg_name = (
+ import_state.mgm_details.device_type_name.lower()
+ .replace(" ", "")
+ .replace("fortimanager", "FortiAdom")
+ .lower()
+ + import_state.mgm_details.device_type_version.replace(" ", "").lower()
+ )
+ elif import_state.mgm_details.device_type_name == "Cisco Asa on FirePower":
+ pkg_name = "ciscoasa" + import_state.mgm_details.device_type_version
+ else:
+ pkg_name = f"{import_state.mgm_details.device_type_name.lower().replace(' ', '')}{import_state.mgm_details.device_type_version}"
- if errors_found>0:
- raise BaseException("error while getting config from API")
-
- if config_changed_since_last_import and fwo_globals.debug_level>2: # debugging: writing config to json file
- debug_start_time = int(time.time())
- try:
- normalized_config_filename = import_tmp_path + '/mgm_id_' + \
- str(importState.MgmDetails.Id) + '_config_normalized.json'
- with open(normalized_config_filename, "w") as json_data:
- json_data.write(json.dumps(jsonpickle.dumps(config2import)))
-
- if fwo_globals.debug_level>3:
- full_native_config_filename = import_tmp_path + '/mgm_id_' + \
- str(importState.MgmDetails.Id) + '_config_native.json'
- with open(full_native_config_filename, "w") as json_data: # create empty config file
- json_data.write(json.dumps(full_config_json, indent=2))
- except:
- logger.error("import_management - unspecified error while dumping config to json file: " + str(traceback.format_exc()))
- raise
-
- time_write_debug_json = int(time.time()) - debug_start_time
- logger.debug("import_management - writing debug config json files duration " + str(time_write_debug_json) + "s")
- return config_changed_since_last_import
+ return pkg_name
+
+
+def set_filename(import_state: ImportStateController, file_name: str = ""):
+ # set file name in importState
+ if file_name == "":
+ # if the host name is an URI, do not connect to an API but simply read the config from this URI
+ if string_is_uri(import_state.state.mgm_details.hostname):
+ import_state.set_import_file_name(import_state.state.mgm_details.hostname)
+ else:
+ import_state.set_import_file_name(file_name)
diff --git a/roles/importer/files/importer/dummyroutermanagement1/fwcommon.py b/roles/importer/files/importer/dummyroutermanagement1/fwcommon.py
deleted file mode 100644
index 1e0f57bc62..0000000000
--- a/roles/importer/files/importer/dummyroutermanagement1/fwcommon.py
+++ /dev/null
@@ -1,68 +0,0 @@
-import sys
-from common import importer_base_dir, complete_import
-from fwo_exception import ConfigFileNotFound
-
-sys.path.append(importer_base_dir + '/dummyrouter')
-from curses import raw
-from fwo_log import getFwoLogger
-import fwo_globals
-from fwo_data_networking import Interface, Route, getRouteDestination
-import json, requests, requests.packages
-from datetime import datetime
-import jsonpickle
-
-def has_config_changed(_, __, force=False):
- return True
-
-
-def get_config(config2import, _, current_import_id, mgm_details, limit=100, force=False, jwt=''):
- router_file_url = mgm_details['configPath']
- error_count = 0
- change_count = 0
- start_time=datetime.now()
- error_string = ''
-
- if len(mgm_details['devices'])!=1:
- logger = getFwoLogger()
- logger.error('expected exactly one device but found: ' + str(mgm_details['devices']))
- exit(1)
- dev_id = mgm_details['devices'][0]['id']
-
- try:
- session = requests.Session()
- #session.headers = { 'Content-Type': 'application/json' }
- session.verify=fwo_globals.verify_certs
- r = session.get(router_file_url, )
- r.raise_for_status()
- cfg = json.loads(r.content)
-
- except requests.exceptions.RequestException:
- error_string = "got HTTP status code" + str(r.status_code) + " while trying to read config file from URL " + router_file_url
- error_count += 1
- error_count = complete_import(current_import_id, error_string, start_time, mgm_details, change_count, error_count, jwt)
- raise ConfigFileNotFound(error_string) from None
- except:
- error_string = "Could not read config file " + router_file_url
- error_count += 1
- error_count = complete_import(current_import_id, error_string, start_time, mgm_details, change_count, error_count, jwt)
- raise ConfigFileNotFound(error_string) from None
-
- # deserialize network info from json into objects
-
- # device_id, name, ip, netmask_bits, state_up=True, ip_version=4
- ifaces = []
- for iface in cfg['interfaces']:
- ifaces.append(Interface(dev_id, iface['name'], iface['ip'], iface['netmask_bits'], state_up=iface['state_up'], ip_version=iface['ip_version']))
- cfg['interfaces'] = ifaces
-
- # device_id, target_gateway, destination, static=True, source=None, interface=None, metric=None, distance=None, ip_version=4
- routes = []
- for route in cfg['routing']:
- routes.append(Route(dev_id, route['target_gateway'], route['destination'], static=route['static'], interface=route['interface'], metric=route['metric'], distance=route['distance'], ip_version=route['ip_version']))
- cfg['routing'] = routes
-
- cfg['routing'].sort(key=getRouteDestination,reverse=True)
-
- config2import.update({'interfaces': cfg['interfaces'], 'routing': cfg['routing']})
-
- return 0
diff --git a/roles/importer/files/importer/fortiadom5ff/autodiscover.sh b/roles/importer/files/importer/fortiadom5ff/autodiscover.sh
deleted file mode 100644
index 1d421be37c..0000000000
--- a/roles/importer/files/importer/fortiadom5ff/autodiscover.sh
+++ /dev/null
@@ -1,111 +0,0 @@
-#!/bin/bash
-
-NAME_="dicovery-fgtmgr.sh"
-SYNOPSIS_="$NAME_ [-d] [-H ] [-U ] [-K ]"
-REQUIRES_="standard GNU commands"
-VERSION_="0.1"
-DATE_="2021-06-24"
-AUTHOR_="Holger Dost "
-PURPOSE_="extracts information from the fortimanager"
-EXIT_ERROR=3
-EXIT_BUG=3
-EXIT_SUCCESS=0
-
-
-KEY='/usr/local/fworch/.ssh/id_rsa_forti'
-USER='itsecorg'
-SERVER='1.1.1.1'
-REMCOM='diagnose dvm device list'
-DEBUGMODE=0
-GREP="/bin/fgrep"
-AWK="/usr/bin/awk"
-HEAD="/usr/bin/head"
-
-
-usage () {
- echo >&2 "$NAME_ $VERSION_ - $PURPOSE_
-Usage: $SYNOPSIS_
-Requires: $REQUIRES_
-Example: discovery-fgtmgr.sh -d -H 1.1.1.1 -U testuser -K .ssh/id_rsa_testkey
-"
- exit 1
-}
-
-shopt -s extglob
-
-while getopts 'dhH:C:D:' OPTION ; do
- case $OPTION in
- h) usage $EXIT_SUCCESS
- ;;
- d) DEBUGMODE=1
- ;;
- H) SERVER="$OPTARG"
- ;;
- U) USER="$OPTARG"
- ;;
- K) KEY="$OPTARG"
- ;;
- \?)echo "unknown option \"-$OPTARG\"." >&2
- usage $EXIT_ERROR
- ;;
- :) echo "option \"-$OPTARG\" argument missing" >&2
- usage $EXIT_ERROR
- ;;
- *) echo "bug ..." >&2
- usage $EXIT_BUG
- ;;
- esac
-done
-
-# : ${SERVER:='1.1.1.1'}
-# : ${USER:='itsecorg'}
-# : ${KEY:='/usr/local/fworch/.ssh/id_rsa_forti'}
-
-DEBUG () {
- if [ $DEBUGMODE -gt 0 ]; then
- #printf "$1\n"
- printf '%s\n' "$1"
- fi
-}
-DEBUGWOLF () {
- if [ $DEBUGMODE -gt 0 ]; then
- printf '%s' "$1"
- fi
-}
-
-REMRES=`ssh -i ${KEY} ${USER}@${SERVER} "${REMCOM}" | egrep "fmg/faz|vdom|^TYPE" | grep -v 'root flags'`
-LINECOUNT=0
-FMGLINECOUNT=0
-while read line; do
- ((LINECOUNT++))
- #DEBUG "$line"
- if [[ "$line" =~ "fmg/faz" ]]; then
- ((FMGLINECOUNT++))
- IFS=' '; read -ra FMGLINE <<< $line
- FMGVALCOUNT=0
- for FMGVAL in "${FMGLINE[@]}"; do
- ((FMGVALCOUNT++))
- FMG[${FMGLINECOUNT},${FMGVALCOUNT}]=$FMGVAL
- DEBUGWOLF "${FMG[${FMGLINECOUNT},${FMGVALCOUNT}]},"
- done
- DEBUG ""
- # array für die Ausgabezeilen bauen, oder die Zeile direkt ausgeben
- fi
- if [[ "$line" =~ "vdom" ]]; then
- ((VDOMLINECOUNT++))
- IFS=' '; read -ra VDOMLINE <<< $line
- VDOMVALCOUNT=0
- for VDOMVAL in "${VDOMLINE[@]}"; do
- ((VDOMVALCOUNT++))
- VDOM[${FMGLINECOUNT},${VDOMLINECOUNT},${VDOMVALCOUNT}]=$VDOMVAL
- DEBUGWOLF "${VDOM[${FMGLINECOUNT},${VDOMLINECOUNT},${VDOMVALCOUNT}]},"
- done
- DEBUG ""
- # wenn vdoms existieren obige zeile ergänzen, auch mehrfach
- fi
-done <<< "$REMRES"
-FMGLINECOUNTMAX=$FMGLINECOUNT
-
-echo "${#FMG[@]}"
-echo "${#VDOM[@]}"
-#printf "${FMG[${FMGLINECOUNT},${FMGVALCOUNT}]}
diff --git a/roles/importer/files/importer/fortiadom5ff/discovery_logging.conf b/roles/importer/files/importer/fortiadom5ff/discovery_logging.conf
deleted file mode 100644
index 139c55a9cb..0000000000
--- a/roles/importer/files/importer/fortiadom5ff/discovery_logging.conf
+++ /dev/null
@@ -1,41 +0,0 @@
-[loggers]
-keys=root,discoveryDebugLogger
-#keys=root,__main__
-
-[handlers]
-keys=consoleHandler,debugFileHandler
-
-[formatters]
-keys=defaultFormatter,debugFileFormatter
-
-[logger_root]
-level=DEBUG
-handlers=consoleHandler
-
-[logger_discoveryDebugLogger]
-#[logger___main__]
-level=DEBUG
-handlers=debugFileHandler
-qualname=discoveryDebugLogger
-#qualname=__main__
-propagate=0
-
-[handler_consoleHandler]
-class=StreamHandler
-level=DEBUG
-formatter=defaultFormatter
-args=(sys.stderr,)
-
-[handler_debugFileHandler]
-class=FileHandler
-level=DEBUG
-formatter=debugFileFormatter
-args=('/tmp/fworch_discovery.log',)
-# args=('/var/log/fworch/discovery.log',)
-
-[formatter_defaultFormatter]
-format=%(levelname)s:%(name)s:%(message)s
-
-[formatter_debugFileFormatter]
-format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
-
diff --git a/roles/importer/files/importer/fortiadom5ff/fmgr_getter.py b/roles/importer/files/importer/fortiadom5ff/fmgr_getter.py
deleted file mode 100644
index da0c6ed156..0000000000
--- a/roles/importer/files/importer/fortiadom5ff/fmgr_getter.py
+++ /dev/null
@@ -1,137 +0,0 @@
-# library for API get functions
-import re
-from fwo_log import getFwoLogger
-import requests.packages
-import requests
-import json
-import fwo_globals
-from fwo_exception import FwLoginFailed
-
-
-def api_call(url, command, json_payload, sid, show_progress=False, method=''):
- logger = getFwoLogger()
- request_headers = {'Content-Type': 'application/json'}
- if sid != '':
- json_payload.update({"session": sid})
- if command != '':
- for p in json_payload['params']:
- p.update({"url": command})
- if method == '':
- method = 'get'
- json_payload.update({"method": method})
-
- r = requests.post(url, data=json.dumps(json_payload), headers=request_headers, verify=fwo_globals.verify_certs)
- if r is None:
- if 'pass' in json.dumps(json_payload):
- exception_text = "error while sending api_call containing credential information to url '" + str(url)
- else:
- exception_text = "error while sending api_call to url '" + str(url) + "' with payload '" + json.dumps(json_payload, indent=2) + "' and headers: '" + json.dumps(request_headers, indent=2)
- raise Exception(exception_text)
- result_json = r.json()
- if 'result' not in result_json or len(result_json['result'])<1:
- if 'pass' in json.dumps(json_payload):
- raise Exception("error while sending api_call containing credential information to url '" + str(url))
- else:
- if 'status' in result_json['result'][0]:
- raise Exception("error while sending api_call to url '" + str(url) + "' with payload '" +
- json.dumps(json_payload, indent=2) + "' and headers: '" + json.dumps(request_headers, indent=2) + ', result=' + json.dumps(r.json()['result'][0]['status'], indent=2))
- else:
- raise Exception("error while sending api_call to url '" + str(url) + "' with payload '" +
- json.dumps(json_payload, indent=2) + "' and headers: '" + json.dumps(request_headers, indent=2) + ', result=' + json.dumps(r.json()['result'][0], indent=2))
- if 'status' not in result_json['result'][0] or 'code' not in result_json['result'][0]['status'] or result_json['result'][0]['status']['code'] != 0:
- # trying to ignore empty results as valid
- pass # logger.warning('received empty result')
- if fwo_globals.debug_level>2:
- if 'pass' in json.dumps(json_payload):
- logger.debug("api_call containing credential information to url '" + str(url) + " - not logging query")
- else:
- logger.debug("api_call to url '" + str(url) + "' with payload '" + json.dumps(
- json_payload, indent=2) + "' and headers: '" + json.dumps(request_headers, indent=2))
-
- return result_json
-
-
-def login(user, password, base_url):
- payload = {
- "id": 1,
- "params": [ { "data": [ { "user": user, "passwd": password, } ] } ]
- }
- try:
- response = api_call(base_url, 'sys/login/user', payload, '', method="exec")
- except Exception:
- raise FwLoginFailed("FortiManager login ERROR: url=" + base_url) from None
- if "session" not in response: # leaving out payload as it contains pwd
- raise FwLoginFailed("FortiManager login ERROR: url=" + base_url) from None
- return response["session"]
-
-
-def logout(v_url, sid, method='exec'):
- logger = getFwoLogger()
- payload = {"params": [{}]}
-
- response = api_call(v_url, 'sys/logout', payload, sid, method=method)
- if "result" in response and "status" in response["result"][0] and "code" in response["result"][0]["status"] and response["result"][0]["status"]["code"] == 0:
- logger.debug("successfully logged out")
- else:
- raise Exception( "fmgr_getter ERROR: did not get status code 0 when logging out, " +
- "api call: url: " + str(v_url) + ", + payload: " + str(payload))
-
-
-def set_api_url(base_url, testmode, api_supported, hostname):
- url = ''
- if testmode == 'off':
- url = base_url
- else:
- if re.search(r'^\d+[\.\d+]+$', testmode) or re.search(r'^\d+$', testmode):
- if testmode in api_supported:
- url = base_url + 'v' + testmode + '/'
- else:
- raise Exception("api version " + testmode +
- " is not supported by the manager " + hostname + " - Import is canceled")
- else:
- raise Exception("\"" + testmode + "\" - not a valid version")
- return url
-
-
-def update_config_with_fortinet_api_call(config_json, sid, api_base_url, api_path, result_name, payload={}, options=[], show_progress=False, limit=150, method="get"):
- offset = 0
- limit = int(limit)
- returned_new_objects = True
- full_result = []
- while returned_new_objects:
- range = [offset, limit]
- if payload == {}:
- payload = {"params": [{'range': range}]}
- else:
- if 'params' in payload and len(payload['params'])>0:
- payload['params'][0].update({'range': range})
-
- # adding options
- if len(options)>0:
- payload['params'][0].update({'option': options})
- # payload['params'][0].update({'filter': options})
-
- result = fortinet_api_call(sid, api_base_url, api_path, payload=payload, show_progress=show_progress, method=method)
- full_result.extend(result)
- offset += limit
- if len(result)5:
- logger.warning('native configs contains the following keys ' + str(native_config.keys()))
- normalized_config['networking'][full_vdom_name]['routingv6'] = []
- else:
- for route in native_config['routing-table-ipv6/' + full_vdom_name]:
- #gateway = None if route['gateway']=='::' else route['gateway'] # local network
- normRoute = Route(dev_id, route['gateway'], route['ip_mask'], metric=route['metric'],
- distance=route['distance'], interface=route['interface'], ip_version=6)
- normalized_config['routing'].append(normRoute)
-
- normalized_config['routing'].sort(key=getRouteDestination,reverse=True)
-
- for interface in native_config['interfaces_per_device/' + full_vdom_name]:
- if 'ipv6' in interface and 'ip6-address' in interface['ipv6'] and interface['ipv6']['ip6-address']!='::/0':
- ipv6, netmask_bits = interface['ipv6']['ip6-address'].split('/')
- normIfV6 = Interface(dev_id, interface['name'], IPAddress(ipv6), netmask_bits, ip_version=6)
- normalized_config['interfaces'].append(normIfV6)
-
- if 'ip' in interface and interface['ip']!=['0.0.0.0','0.0.0.0']:
- ipv4 = IPAddress(interface['ip'][0])
- netmask_bits = IPAddress(interface['ip'][1]).netmask_bits()
- normIfV4 = Interface(dev_id, interface['name'], ipv4, netmask_bits, ip_version=4)
- normalized_config['interfaces'].append(normIfV4)
-
- #devices_without_default_route = get_devices_without_default_route(normalized_config)
- #if len(devices_without_default_route)>0:
- # logger.warning('found devices without default route')
-
-
-def get_matching_route(destination_ip, routing_table):
-
- logger = getFwoLogger()
-
- def route_matches(ip, destination):
- ip_n = IPNetwork(ip).cidr
- dest_n = IPNetwork(destination).cidr
- return ip_n in dest_n or dest_n in ip_n
-
-
- if len(routing_table)==0:
- logger.error('src nat behind interface: encountered empty routing table')
- return None
-
- for route in routing_table:
- if route_matches(destination_ip, route['destination']):
- return route
-
- logger.warning('src nat behind interface: found no matching route in routing table - no default route?!')
- return None
-
-
-def get_ip_of_interface(interface, interface_list=[]):
-
- interface_details = next((sub for sub in interface_list if sub['name'] == interface), None)
-
- if interface_details is not None and 'ipv4' in interface_details:
- return interface_details['ipv4']
- else:
- return None
-
-
-def sort_reverse(ar_in, key):
-
- def comp(left, right):
- l_submask = int(left[key].split("/")[1])
- r_submask = int(right[key].split("/")[1])
- return l_submask - r_submask
-
- return sorted(ar_in, key=cmp_to_key(comp), reverse=True)
-
-
-# strip off last part of a string separated by separator
-def strip_off_last_part(string_in, separator='_'):
- string_out = string_in
- if separator in string_in: # strip off final _xxx part
- str_ar = string_in.split(separator)
- str_ar.pop()
- string_out = separator.join(str_ar)
- return string_out
-
-
-def get_last_part(string_in, separator='_'):
- string_out = ''
- if separator in string_in: # strip off _vdom_name
- str_ar = string_in.split(separator)
- string_out = str_ar.pop()
- return string_out
-
-
-def get_plain_device_names_without_vdoms(devices):
- device_array = []
- for dev in devices:
- dev_name = strip_off_last_part(dev["name"])
- if dev_name not in device_array:
- device_array.append(dev_name)
- return device_array
-
-
-# only getting one vdom as currently assuming routing to be
-# the same for all vdoms on a device
-def get_device_names_plus_one_vdom(devices):
- device_array = []
- device_array_with_vdom = []
- for dev in devices:
- dev_name = strip_off_last_part(dev["name"])
- vdom_name = get_last_part(dev["name"])
- if dev_name not in device_array:
- device_array.append(dev_name)
- device_array_with_vdom.append([dev_name, vdom_name])
- return device_array_with_vdom
-
-
-# getting devices and their vdom names
-def get_device_plus_full_vdom_names(devices):
- device_array_with_vdom = []
- for dev in devices:
- dev_name = strip_off_last_part(dev["name"])
- vdom_name = dev["name"]
- device_array_with_vdom.append([dev_name, vdom_name])
- return device_array_with_vdom
-
-
-# getting devices and their vdom names
-def get_all_dev_names(devices):
- device_array_with_vdom = []
- for dev in devices:
- dev_id = dev["id"]
- dev_name = strip_off_last_part(dev["name"])
- plain_vdom_name = get_last_part(dev["name"])
- full_vdom_name = dev["name"]
- device_array_with_vdom.append([dev_id, dev_name, plain_vdom_name, full_vdom_name])
- return device_array_with_vdom
-
-
-# get network information (currently only used for source nat)
-def getInterfacesAndRouting(sid, fm_api_url, raw_config, adom_name, devices, limit):
-
- logger = getFwoLogger()
- # strip off vdom names, just deal with the plain device
- device_array = get_all_dev_names(devices)
-
- for dev_id, plain_dev_name, plain_vdom_name, full_vdom_name in device_array:
- logger.info("dev_name: " + plain_dev_name + ", full vdom_name: " + full_vdom_name)
-
- # getting interfaces of device
- all_interfaces_payload = {
- "id": 1,
- "params": [
- {
- "fields": [ "name", "ip" ],
- "filter": [ "vdom", "==", plain_vdom_name ],
- "sub fetch": {
- "client-options": {
- "subfetch hidden": 1
- },
- "dhcp-snooping-server-list": {
- "subfetch hidden": 1
- },
- "egress-queues": {
- "subfetch hidden": 1
- },
- "ipv6": {
- "fields": [
- "ip6-address"
- ],
- "sub fetch": {
- "dhcp6-iapd-list": {
- "subfetch hidden": 1
- },
- "ip6-delegated-prefix-list": {
- "subfetch hidden": 1
- },
- "ip6-extra-addr": {
- "subfetch hidden": 1
- },
- "ip6-prefix-list": {
- "subfetch hidden": 1
- },
- "vrrp6": {
- "subfetch hidden": 1
- }
- }
- },
- "l2tp-client-settings": {
- "subfetch hidden": 1
- },
- "secondaryip": {
- "subfetch hidden": 1
- },
- "tagging": {
- "subfetch hidden": 1
- },
- "vrrp": {
- "subfetch hidden": 1
- },
- "wifi-networks": {
- "subfetch hidden": 1
- }
- }
- }
- ]
- }
- # get_interfaces_payload = {
- # "id": 1,
- # "params": [
- # {
- # "fields": [ "name", "ip" ],
- # "filter": [ "vdom", "==", plain_vdom_name ],
- # "option": [ "no loadsub" ],
- # }
- # ]
- # }
- try: # get interfaces from top level device (not vdom)
- fmgr_getter.update_config_with_fortinet_api_call(
- raw_config, sid, fm_api_url, "/pm/config/device/" + plain_dev_name + "/global/system/interface",
- "interfaces_per_device/" + full_vdom_name, payload=all_interfaces_payload, limit=limit, method="get")
- except:
- logger.warning("error while getting interfaces of device " + plain_vdom_name + ", vdom=" + plain_vdom_name + ", ignoring, traceback: " + str(traceback.format_exc()))
-
- # now getting routing information
- for ip_version in ["ipv4", "ipv6"]:
- payload = { "params": [ { "data": {
- "target": ["adom/" + adom_name + "/device/" + plain_dev_name],
- "action": "get",
- "resource": "/api/v2/monitor/router/" + ip_version + "/select?&vdom="+ plain_vdom_name } } ] }
- try: # get routing table per vdom
- routing_helper = {}
- routing_table = []
- fmgr_getter.update_config_with_fortinet_api_call(
- routing_helper, sid, fm_api_url, "/sys/proxy/json",
- "routing-table-" + ip_version + '/' + full_vdom_name,
- payload=payload, limit=limit, method="exec")
-
- if "routing-table-" + ip_version + '/' + full_vdom_name in routing_helper:
- routing_helper = routing_helper["routing-table-" + ip_version + '/' + full_vdom_name]
- if len(routing_helper)>0 and 'response' in routing_helper[0] and 'results' in routing_helper[0]['response']:
- routing_table = routing_helper[0]['response']['results']
- else:
- logger.warning("got empty " + ip_version + " routing table from device " + full_vdom_name + ", ignoring")
- routing_table = []
- except:
- logger.warning("could not get routing table for device " + full_vdom_name + ", ignoring") # exception " + str(traceback.format_exc()))
- routing_table = []
-
- # now storing the routing table:
- raw_config.update({"routing-table-" + ip_version + '/' + full_vdom_name: routing_table})
-
-
-def get_device_from_package(package_name, mgm_details):
- logger = getFwoLogger()
- for dev in mgm_details['devices']:
- if dev['local_rulebase_name'] == package_name:
- return dev['id']
- logger.debug('get_device_from_package - could not find device for package "' + package_name + '"')
- return None
diff --git a/roles/importer/files/importer/fortiadom5ff/fmgr_network.py b/roles/importer/files/importer/fortiadom5ff/fmgr_network.py
deleted file mode 100644
index 72c187624c..0000000000
--- a/roles/importer/files/importer/fortiadom5ff/fmgr_network.py
+++ /dev/null
@@ -1,288 +0,0 @@
-from asyncio.log import logger
-import ipaddress
-from fwo_log import getFwoLogger
-from fwo_const import list_delimiter, nat_postfix
-from fmgr_zone import add_zone_if_missing
-from fwo_config import readConfig
-from fwo_const import fwo_config_filename
-from fwo_api import setAlert, create_data_issue
-
-def normalize_nwobjects(full_config, config2import, import_id, nw_obj_types, jwt=None, mgm_id=None):
- logger = getFwoLogger()
- nw_objects = []
- for obj_type in nw_obj_types:
- for obj_orig in full_config[obj_type]:
- obj_zone = 'global'
- obj = {}
- obj.update({'obj_name': obj_orig['name']})
- if 'subnet' in obj_orig: # ipv4 object
- ipa = ipaddress.ip_network(str(obj_orig['subnet'][0]) + '/' + str(obj_orig['subnet'][1]))
- if ipa.num_addresses > 1:
- obj.update({ 'obj_typ': 'network' })
- else:
- obj.update({ 'obj_typ': 'host' })
- obj.update({ 'obj_ip': ipa.with_prefixlen })
- elif 'ip6' in obj_orig: # ipv6 object
- ipa = ipaddress.ip_network(str(obj_orig['ip6']).replace("\\", ""))
- if ipa.num_addresses > 1:
- obj.update({ 'obj_typ': 'network' })
- else:
- obj.update({ 'obj_typ': 'host' })
- obj.update({ 'obj_ip': ipa.with_prefixlen })
- elif 'member' in obj_orig: # addrgrp4 / addrgrp6
- obj.update({ 'obj_typ': 'group' })
- obj.update({ 'obj_member_names' : list_delimiter.join(obj_orig['member']) })
- obj.update({ 'obj_member_refs' : resolve_objects(obj['obj_member_names'], list_delimiter, full_config, 'name', 'uuid', jwt=jwt, import_id=import_id)}, mgm_id=mgm_id)
- elif 'startip' in obj_orig: # ippool object
- obj.update({ 'obj_typ': 'ip_range' })
- obj.update({ 'obj_ip': obj_orig['startip'] })
- obj.update({ 'obj_ip_end': obj_orig['endip'] })
- elif 'start-ip' in obj_orig: # standard ip range object
- obj.update({ 'obj_typ': 'ip_range' })
- obj.update({ 'obj_ip': obj_orig['start-ip'] })
- obj.update({ 'obj_ip_end': obj_orig['end-ip'] })
- elif 'extip' in obj_orig: # vip object, simplifying to a single ip
- obj.update({ 'obj_typ': 'host' })
- if 'extip' not in obj_orig or len(obj_orig['extip'])==0:
- logger.error("vip (extip): found empty extip field for " + obj_orig['name'])
- else:
- if len(obj_orig['extip'])>1:
- logger.warning("vip (extip): found more than one extip, just using the first one for " + obj_orig['name'])
- set_ip_in_obj(obj, obj_orig['extip'][0]) # resolving nat range if there is one
- nat_obj = {}
- nat_obj.update({'obj_typ': 'host' })
- nat_obj.update({'obj_color': 'black'})
- nat_obj.update({'obj_comment': 'FWO-auto-generated nat object for VIP'})
- if 'obj_ip_end' in obj: # this obj is a range - include the end ip in name and uid as well to avoid akey conflicts
- nat_obj.update({'obj_ip_end': obj['obj_ip_end']})
-
- # now dealing with the nat ip obj (mappedip)
- if 'mappedip' not in obj_orig or len(obj_orig['mappedip'])==0:
- logger.warning("vip (extip): found empty mappedip field for " + obj_orig['name'])
- else:
- if len(obj_orig['mappedip'])>1:
- logger.warning("vip (extip): found more than one mappedip, just using the first one for " + obj_orig['name'])
- nat_ip = obj_orig['mappedip'][0]
- set_ip_in_obj(nat_obj, nat_ip)
- obj.update({ 'obj_nat_ip': nat_obj['obj_ip'] }) # save nat ip in vip obj
- if 'obj_ip_end' in nat_obj: # this nat obj is a range - include the end ip in name and uid as well to avoid akey conflicts
- obj.update({ 'obj_nat_ip_end': nat_obj['obj_ip_end'] }) # save nat ip in vip obj
- nat_obj.update({'obj_name': nat_obj['obj_ip'] + '-' + nat_obj['obj_ip_end'] + nat_postfix})
- else:
- nat_obj.update({'obj_name': nat_obj['obj_ip'] + nat_postfix})
- nat_obj.update({'obj_uid': nat_obj['obj_name']})
- ###### range handling
-
- if 'associated-interface' in obj_orig and len(obj_orig['associated-interface'])>0: # and obj_orig['associated-interface'][0] != 'any':
- obj_zone = obj_orig['associated-interface'][0]
- nat_obj.update({'obj_zone': obj_zone })
- nat_obj.update({'control_id': import_id})
- if nat_obj not in nw_objects: # rare case when a destination nat is down for two different orig ips to the same dest ip
- nw_objects.append(nat_obj)
- else:
- pass
- else: # 'fqdn' in obj_orig: # "fully qualified domain name address" // other unknown types
- obj.update({ 'obj_typ': 'network' })
- obj.update({ 'obj_ip': '0.0.0.0/0'})
- if 'comment' in obj_orig:
- obj.update({'obj_comment': obj_orig['comment']})
- if 'color' in obj_orig and obj_orig['color']==0:
- obj.update({'obj_color': 'black'}) # todo: deal with all other colors (will be currently ignored)
- # we would need a list of fortinet color codes
- if 'uuid' not in obj_orig:
- obj_orig.update({'uuid': obj_orig['name']})
- obj.update({'obj_uid': obj_orig['uuid']})
-
- # here only picking first associated interface as zone:
- if 'associated-interface' in obj_orig and len(obj_orig['associated-interface'])>0: # and obj_orig['associated-interface'][0] != 'any':
- obj_zone = obj_orig['associated-interface'][0]
- # adding zone if it not yet exists
- obj_zone = add_zone_if_missing (config2import, obj_zone, import_id)
- obj.update({'obj_zone': obj_zone })
-
- obj.update({'control_id': import_id})
- nw_objects.append(obj)
-
- # dynamic objects have different return structure
- if 'response' in full_config['nw_obj_global_firewall/internet-service-basic'][0] and 'results' in full_config['nw_obj_global_firewall/internet-service-basic'][0]['response']:
- for obj_orig in full_config['nw_obj_global_firewall/internet-service-basic'][0]['response']['results']:
- if 'name' in obj_orig and 'q_origin_key' in obj_orig:
- obj = {
- 'obj_name': obj_orig['name'],
- 'obj_typ': 'network',
- 'obj_ip': '0.0.0.0/0',
- 'obj_uid': 'q_origin_key_' + str(obj_orig['q_origin_key']),
- 'control_id': import_id,
- 'obj_zone': 'global'
- }
- nw_objects.append(obj)
- else:
- logger.warning("internet service objects return format broken")
-
- # finally add "Original" network object for natting
- original_obj_name = 'Original'
- original_obj_uid = 'Original'
- nw_objects.append(create_network_object(import_id=import_id, name=original_obj_name, type='network', ip='0.0.0.0/0',\
- uid=original_obj_uid, zone='global', color='black', comment='"original" network object created by FWO importer for NAT purposes'))
-
- config2import.update({'network_objects': nw_objects})
-
-
-def set_ip_in_obj(nw_obj, ip): # add start and end ip in nw_obj if it is a range, otherwise do nothing
- if '-' in ip: # dealing with range
- ip_start, ip_end = ip.split('-')
- nw_obj.update({'obj_ip': ip_start })
- if ip_end != ip_start:
- nw_obj.update({'obj_ip_end': ip_end })
- else:
- nw_obj.update({'obj_ip': ip })
-
-
-# for members of groups, the name of the member obj needs to be fetched separately (starting from API v1.?)
-def resolve_nw_uid_to_name(uid, nw_objects):
- # return name of nw_objects element where obj_uid = uid
- for obj in nw_objects:
- if obj['obj_uid'] == uid:
- return obj['obj_name']
- return 'ERROR: uid "' + uid + '" not found'
-
-
-def add_member_names_for_nw_group(idx, nw_objects):
- group = nw_objects.pop(idx)
- if group['obj_member_refs'] == '' or group['obj_member_refs'] == None:
- #member_names = None
- #obj_member_refs = None
- group['obj_member_names'] = None
- group['obj_member_refs'] = None
- else:
- member_names = ''
- obj_member_refs = group['obj_member_refs'].split(list_delimiter)
- for ref in obj_member_refs:
- member_name = resolve_nw_uid_to_name(ref, nw_objects)
- member_names += member_name + list_delimiter
- group['obj_member_names'] = member_names[:-1]
- nw_objects.insert(idx, group)
-
-
-def create_network_object(import_id, name, type, ip, uid, color, comment, zone):
- # if zone is None or zone == '':
- # zone = 'global'
- return {
- 'control_id': import_id,
- 'obj_name': name,
- 'obj_typ': type,
- 'obj_ip': ip,
- 'obj_uid': uid,
- 'obj_color': color,
- 'obj_comment': comment,
- 'obj_zone': zone
- }
-
-
-# TODO: reduce commplexity if possible
-def get_nw_obj(nat_obj_name, nwobjects):
- for obj in nwobjects:
- if 'obj_name' in obj and obj['obj_name']==nat_obj_name:
- return obj
- return None
-
-
-# this removes all obj_nat_ip entries from all network objects
-# these were used during import but might cause issues if imported into db
-def remove_nat_ip_entries(config2import):
- for obj in config2import['network_objects']:
- if 'obj_nat_ip' in obj:
- obj.pop('obj_nat_ip')
-
-
-def get_first_ip_of_destination(obj_ref, config2import):
-
- logger = getFwoLogger()
- if list_delimiter in obj_ref:
- obj_ref = obj_ref.split(list_delimiter)[0]
- # if destination does not contain exactly one ip, raise a warning
- logger.info('src nat behind interface: more than one NAT IP - just using the first one for routing decision for obj_ref ' + obj_ref)
-
- for obj in config2import['network_objects']:
- if 'obj_uid' in obj and obj['obj_uid']==obj_ref:
- if 'obj_type' in obj and obj['obj_type']=='group':
- if 'obj_member_refs' in obj and list_delimiter in obj['obj_member_refs']:
- return get_first_ip_of_destination(obj['obj_member_refs'].split(list_delimiter)[0], config2import)
- elif 'obj_ip' in obj:
- return obj['obj_ip']
- logger.warning('src nat behind interface: found no IP info for destination object ' + obj_ref)
- return None
-
-
-def resolve_objects (obj_name_string_list, delimiter, obj_dict, name_key, uid_key, rule_type=None, jwt=None, import_id=None, mgm_id=None):
- # guessing ipv4 and adom (to also search global objects)
- return resolve_raw_objects (obj_name_string_list, delimiter, obj_dict, name_key, uid_key, rule_type='v4_adom', obj_type='network', jwt=jwt, import_id=import_id, mgm_id=mgm_id)
-
-
-def resolve_raw_objects (obj_name_string_list, delimiter, obj_dict, name_key, uid_key, rule_type=None, obj_type='network', jwt=None, import_id=None, rule_uid=None, object_type=None, mgm_id=None):
- logger = getFwoLogger()
- fwo_config = readConfig(fwo_config_filename)
-
- ref_list = []
- objects_not_found = []
- for el in obj_name_string_list.split(delimiter):
- found = False
- if rule_type is not None:
- if obj_type == 'network':
- if 'v4' in rule_type and 'global' in rule_type:
- object_tables = [obj_dict['nw_obj_global_firewall/address'], obj_dict['nw_obj_global_firewall/addrgrp'], obj_dict['nw_obj_global_firewall/internet-service-basic'][0]['response']['results']]
- elif 'v6' in rule_type and 'global' in rule_type:
- object_tables = [obj_dict['nw_obj_global_firewall/address6'], obj_dict['nw_obj_global_firewall/addrgrp6']]
- elif 'v4' in rule_type and 'adom' in rule_type:
- object_tables = [obj_dict['nw_obj_adom_firewall/address'], obj_dict['nw_obj_adom_firewall/addrgrp'], \
- obj_dict['nw_obj_global_firewall/address'], obj_dict['nw_obj_global_firewall/addrgrp'], \
- obj_dict['nw_obj_adom_firewall/vip'], obj_dict['nw_obj_adom_system/external-resource'], \
- obj_dict['nw_obj_global_firewall/internet-service-basic'][0]['response']['results'] ]
- elif 'v6' in rule_type and 'adom' in rule_type:
- object_tables = [obj_dict['nw_obj_adom_firewall/address6'], obj_dict['nw_obj_adom_firewall/addrgrp6'], \
- obj_dict['nw_obj_global_firewall/address6'], obj_dict['nw_obj_global_firewall/addrgrp6']]
- elif 'nat' in rule_type and 'adom' in rule_type:
- object_tables = [obj_dict['nw_obj_adom_firewall/address'], obj_dict['nw_obj_adom_firewall/addrgrp'], \
- obj_dict['nw_obj_global_firewall/address'], obj_dict['nw_obj_global_firewall/addrgrp']]
- elif 'nat' in rule_type and 'global' in rule_type:
- object_tables = [obj_dict['nw_obj_global_firewall/address'], obj_dict['nw_obj_global_firewall/addrgrp']]
- else:
- object_tables = []
- break_flag = False # if we find a match we stop the two inner for-loops
- for tab in object_tables:
- if break_flag:
- found = True
- break
- else:
- for obj in tab:
- if obj[name_key] == el:
- if uid_key in obj:
- ref_list.append(obj[uid_key])
- # in case of internet-service-object we find no uid field, but custom q_origin_key_
- elif 'q_origin_key' in obj:
- ref_list.append('q_origin_key_' + str(obj['q_origin_key']))
- else:
- logger.error('found object without expected uid')
- break_flag = True
- found = True
- break
- elif obj_type == 'service':
- logger.warning('todo later')
- else:
- logger.warning('decide what to do')
- if not found:
- objects_not_found.append(el)
- for obj in objects_not_found:
-
- if obj != 'all' and obj != 'Original':
- if not create_data_issue(fwo_config['fwo_api_base_url'], jwt, import_id=import_id, obj_name=obj, severity=1, rule_uid=rule_uid, mgm_id=mgm_id, object_type=object_type):
- logger.warning("resolve_raw_objects: encountered error while trying to log an import data issue using create_data_issue")
-
- desc = "found a broken network object reference '" + obj + "' "
- if object_type is not None:
- desc += "(type=" + object_type + ") "
- desc += "in rule with UID '" + str(rule_uid) + "'"
- setAlert(fwo_config['fwo_api_base_url'], jwt, import_id=import_id, title="object reference error", mgm_id=mgm_id, severity=1, role='importer', \
- description=desc, source='import', alertCode=16)
-
- return delimiter.join(ref_list)
diff --git a/roles/importer/files/importer/fortiadom5ff/fmgr_rule.py b/roles/importer/files/importer/fortiadom5ff/fmgr_rule.py
deleted file mode 100644
index 2c91651788..0000000000
--- a/roles/importer/files/importer/fortiadom5ff/fmgr_rule.py
+++ /dev/null
@@ -1,539 +0,0 @@
-import copy
-import jsonpickle
-from fwo_const import list_delimiter, nat_postfix
-from fwo_base import extend_string_list
-from fmgr_service import create_svc_object
-from fmgr_network import create_network_object, get_first_ip_of_destination
-import fmgr_zone, fmgr_getter
-from fmgr_gw_networking import get_device_from_package
-from fwo_log import getFwoLogger
-from fwo_data_networking import get_matching_route_obj, get_ip_of_interface_obj
-import ipaddress
-from fmgr_network import resolve_objects, resolve_raw_objects
-import time
-
-rule_access_scope_v4 = ['rules_global_header_v4', 'rules_adom_v4', 'rules_global_footer_v4']
-rule_access_scope_v6 = ['rules_global_header_v6', 'rules_adom_v6', 'rules_global_footer_v6']
-rule_access_scope = rule_access_scope_v6 + rule_access_scope_v4
-rule_nat_scope = ['rules_global_nat', 'rules_adom_nat']
-rule_scope = rule_access_scope + rule_nat_scope
-
-
-def initializeRulebases(raw_config):
- # initialize access rules
- if 'rules_global_header_v4' not in raw_config:
- raw_config.update({'rules_global_header_v4': {}})
- if 'rules_global_header_v6' not in raw_config:
- raw_config.update({'rules_global_header_v6': {}})
- if 'rules_adom_v4' not in raw_config:
- raw_config.update({'rules_adom_v4': {}})
- if 'rules_adom_v6' not in raw_config:
- raw_config.update({'rules_adom_v6': {}})
- if 'rules_global_footer_v4' not in raw_config:
- raw_config.update({'rules_global_footer_v4': {}})
- if 'rules_global_footer_v6' not in raw_config:
- raw_config.update({'rules_global_footer_v6': {}})
-
- # initialize nat rules
- if 'rules_global_nat' not in raw_config:
- raw_config.update({'rules_global_nat': {}})
- if 'rules_adom_nat' not in raw_config:
- raw_config.update({'rules_adom_nat': {}})
-
- # new in v8.3.1:
- # initialize hitcounts
- if 'rules_hitcount' not in raw_config:
- raw_config.update({'rules_hitcount': {}})
-
-
-def getAccessPolicy(sid, fm_api_url, raw_config, adom_name, device, limit):
- consolidated = '' # '/consolidated'
- logger = getFwoLogger()
-
- local_pkg_name = device['local_rulebase_name']
- global_pkg_name = device['global_rulebase_name']
- options = ['extra info', 'scope member', 'get meta']
- # pkg_name = device['package_name'] pkg_name is not used at all
-
- # get hitcount task number
- hitcount_payload = {
- "params": [
- {
- "data": {
- "adom": adom_name,
- "pkg": local_pkg_name
- }
- }
- ]
- }
- hitcount_task = fmgr_getter.fortinet_api_call(
- sid, fm_api_url, "/sys/hitcount", payload=hitcount_payload, method="get")
- time.sleep(2)
-
- # get global header rulebase:
- if device['global_rulebase_name'] is None or device['global_rulebase_name'] == '':
- logger.debug('no global rulebase name defined in fortimanager, ADOM=' + adom_name + ', local_package=' + local_pkg_name)
- else:
- fmgr_getter.update_config_with_fortinet_api_call(
- raw_config['rules_global_header_v4'], sid, fm_api_url, "/pm/config/global/pkg/" + global_pkg_name + "/global/header" + consolidated + "/policy", local_pkg_name, limit=limit)
- fmgr_getter.update_config_with_fortinet_api_call(
- raw_config['rules_global_header_v6'], sid, fm_api_url, "/pm/config/global/pkg/" + global_pkg_name + "/global/header" + consolidated + "/policy6", local_pkg_name, limit=limit)
-
- # get local rulebase
- fmgr_getter.update_config_with_fortinet_api_call(
- raw_config['rules_adom_v4'], sid, fm_api_url, "/pm/config/adom/" + adom_name + "/pkg/" + local_pkg_name + "/firewall" + consolidated + "/policy", local_pkg_name, options=options, limit=limit)
- fmgr_getter.update_config_with_fortinet_api_call(
- raw_config['rules_adom_v6'], sid, fm_api_url, "/pm/config/adom/" + adom_name + "/pkg/" + local_pkg_name + "/firewall" + consolidated + "/policy6", local_pkg_name, limit=limit)
-
- # get global footer rulebase:
- if device['global_rulebase_name'] != None and device['global_rulebase_name'] != '':
- fmgr_getter.update_config_with_fortinet_api_call(
- raw_config['rules_global_footer_v4'], sid, fm_api_url, "/pm/config/global/pkg/" + global_pkg_name + "/global/footer" + consolidated + "/policy", local_pkg_name, limit=limit)
- fmgr_getter.update_config_with_fortinet_api_call(
- raw_config['rules_global_footer_v6'], sid, fm_api_url, "/pm/config/global/pkg/" + global_pkg_name + "/global/footer" + consolidated + "/policy6", local_pkg_name, limit=limit)
-
- # execute hitcount task
- hitcount_payload = {
- "params": [
- {
- "data": {
- "taskid": hitcount_task[0]['task']
- }
- }
- ]
- }
- fmgr_getter.update_config_with_fortinet_api_call(
- raw_config['rules_hitcount'], sid, fm_api_url, "/sys/task/result", local_pkg_name, payload=hitcount_payload, limit=limit)
-
-
-def getNatPolicy(sid, fm_api_url, raw_config, adom_name, device, limit):
- scope = 'global'
- pkg = device['global_rulebase_name']
- if pkg is not None and pkg != '': # only read global rulebase if it exists
- for nat_type in ['central/dnat', 'central/dnat6', 'firewall/central-snat-map']:
- fmgr_getter.update_config_with_fortinet_api_call(
- raw_config['rules_global_nat'], sid, fm_api_url, "/pm/config/" + scope + "/pkg/" + pkg + '/' + nat_type, device['local_rulebase_name'], limit=limit)
-
- scope = 'adom/'+adom_name
- pkg = device['local_rulebase_name']
- for nat_type in ['central/dnat', 'central/dnat6', 'firewall/central-snat-map']:
- fmgr_getter.update_config_with_fortinet_api_call(
- raw_config['rules_adom_nat'], sid, fm_api_url, "/pm/config/" + scope + "/pkg/" + pkg + '/' + nat_type, device['local_rulebase_name'], limit=limit)
-
-
-def normalize_access_rules(full_config, config2import, import_id, mgm_details={}, jwt=None):
- logger = getFwoLogger()
- rules = []
- first_v4 = True
- first_v6 = True
- nat_rule_number = 0
- rule_number = 0
- src_ref_all = ""
- dst_ref_all = ""
- for rule_table in rule_access_scope:
- src_ref_all = resolve_raw_objects("all", list_delimiter, full_config, 'name', 'uuid', rule_type=rule_table, jwt=jwt, import_id=import_id, mgm_id=mgm_details['id'])
- dst_ref_all = resolve_raw_objects("all", list_delimiter, full_config, 'name', 'uuid', rule_type=rule_table, jwt=jwt, import_id=import_id, mgm_id=mgm_details['id'])
- for localPkgName in full_config[rule_table]:
- dev_id = get_device_from_package(localPkgName, mgm_details)
- if dev_id is None:
- logger.info('normalize_access_rules - no matching device found for package "' + localPkgName + '" in rule_table ' + rule_table)
- else:
- rule_number, first_v4, first_v6 = insert_headers(rule_table, first_v6, first_v4, full_config, rules, import_id, localPkgName,src_ref_all,dst_ref_all,rule_number)
-
- for rule_orig in full_config[rule_table][localPkgName]:
- rule = {'rule_src': '', 'rule_dst': '', 'rule_svc': ''}
- xlate_rule = None
- rule.update({ 'control_id': import_id})
- rule.update({ 'rulebase_name': localPkgName}) # the rulebase_name will be set to the pkg_name as there is no rulebase_name in FortiMangaer
- rule.update({ 'rule_ruleid': rule_orig['policyid']})
- rule.update({ 'rule_uid': rule_orig['uuid']})
- rule.update({ 'rule_num': rule_number})
- if 'name' in rule_orig:
- rule.update({ 'rule_name': rule_orig['name']})
- if 'scope member' in rule_orig:
- installon_target = []
- for vdom in rule_orig['scope member']:
- installon_target.append(vdom['name'] + '_' + vdom['vdom'])
- rule.update({ 'rule_installon': '|'.join(installon_target)})
- else:
- rule.update({ 'rule_installon': localPkgName })
- rule.update({ 'rule_implied': False })
- rule.update({ 'rule_time': None })
- rule.update({ 'rule_type': 'access' })
- rule.update({ 'parent_rule_id': None })
-
- if 'comments' in rule_orig:
- rule.update({ 'rule_comment': rule_orig['comments']})
- else:
- rule.update({ 'rule_comment': None })
- if rule_orig['action']==0:
- rule.update({ 'rule_action': 'Drop' })
- else:
- rule.update({ 'rule_action': 'Accept' })
- if 'status' in rule_orig and (rule_orig['status']=='enable' or rule_orig['status']==1):
- rule.update({ 'rule_disabled': False })
- else:
- rule.update({ 'rule_disabled': True })
- if rule_orig['logtraffic'] == 'disable':
- rule.update({ 'rule_track': 'None'})
- else:
- rule.update({ 'rule_track': 'Log'})
-
- rule['rule_src'] = extend_string_list(rule['rule_src'], rule_orig, 'srcaddr', list_delimiter, jwt=jwt, import_id=import_id)
- rule['rule_dst'] = extend_string_list(rule['rule_dst'], rule_orig, 'dstaddr', list_delimiter, jwt=jwt, import_id=import_id)
- rule['rule_svc'] = extend_string_list(rule['rule_svc'], rule_orig, 'service', list_delimiter, jwt=jwt, import_id=import_id)
- rule['rule_src'] = extend_string_list(rule['rule_src'], rule_orig, 'srcaddr6', list_delimiter, jwt=jwt, import_id=import_id)
- rule['rule_dst'] = extend_string_list(rule['rule_dst'], rule_orig, 'dstaddr6', list_delimiter, jwt=jwt, import_id=import_id)
- rule['rule_src'] = extend_string_list(rule['rule_src'], rule_orig, 'internet-service-src-name', list_delimiter, jwt=jwt, import_id=import_id)
-
- if len(rule_orig['srcintf'])>0:
- src_obj_zone = fmgr_zone.add_zone_if_missing (config2import, rule_orig['srcintf'][0], import_id)
- rule.update({ 'rule_from_zone': src_obj_zone }) # todo: currently only using the first zone
- if len(rule_orig['dstintf'])>0:
- dst_obj_zone = fmgr_zone.add_zone_if_missing (config2import, rule_orig['dstintf'][0], import_id)
- rule.update({ 'rule_to_zone': dst_obj_zone }) # todo: currently only using the first zone
-
- if 'srcaddr-negate' in rule_orig:
- rule.update({ 'rule_src_neg': rule_orig['srcaddr-negate']=='disable'})
- elif 'internet-service-src-negate' in rule_orig:
- rule.update({ 'rule_src_neg': rule_orig['internet-service-src-negate']=='disable'})
- if 'dstaddr-negate' in rule_orig:
- rule.update({ 'rule_dst_neg': rule_orig['dstaddr-negate']=='disable'})
- if 'service-negate' in rule_orig:
- rule.update({ 'rule_svc_neg': rule_orig['service-negate']=='disable'})
-
- rule.update({ 'rule_src_refs': resolve_raw_objects(rule['rule_src'], list_delimiter, full_config, 'name', 'uuid', \
- rule_type=rule_table, jwt=jwt, import_id=import_id, rule_uid=rule_orig['uuid'], object_type='network object', mgm_id=mgm_details['id']) })
- rule.update({ 'rule_dst_refs': resolve_raw_objects(rule['rule_dst'], list_delimiter, full_config, 'name', 'uuid', \
- rule_type=rule_table, jwt=jwt, import_id=import_id, rule_uid=rule_orig['uuid'], object_type='network object', mgm_id=mgm_details['id']) })
- rule.update({ 'rule_svc_refs': rule['rule_svc'] }) # services do not have uids, so using name instead
- add_users_to_rule(rule_orig, rule)
-
- # new in v8.0.3:
- if 'meta fields' in rule_orig:
- rule.update({ 'rule_custom_fields': rule_orig['meta fields']})
- if '_last-modified-by' in rule_orig:
- rule.update({ 'rule_last_change_admin': rule_orig['_last-modified-by']})
-
- if rule_table in rule_access_scope_v4 and len(full_config['rules_hitcount'][localPkgName])>0:
- for hitcount_config in full_config['rules_hitcount'][localPkgName][0]['firewall policy']:
- if rule_orig['policyid'] == hitcount_config['last_hit']:
- rule.update({ 'last_hit': time.strftime("%Y-%m-%dT%H:%M:%S%z", time.localtime(hitcount_config['policyid']))})
- elif rule_table in rule_access_scope_v6 and len(full_config['rules_hitcount'][localPkgName])>0:
- for hitcount_config in full_config['rules_hitcount'][localPkgName][0]['firewall policy6']:
- if rule_orig['policyid'] == hitcount_config['last_hit']:
- rule.update({ 'last_hit': time.strftime("%Y-%m-%dT%H:%M:%S%z", time.localtime(hitcount_config['policyid']))})
- else:
- rule.update({ 'last_hit': None})
-
- xlate_rule = handle_combined_nat_rule(rule, rule_orig, config2import, nat_rule_number, import_id, localPkgName, dev_id)
- rules.append(rule)
- if xlate_rule is not None:
- rules.append(xlate_rule)
- rule_number += 1 # nat rules have their own numbering
-
- config2import.update({'rules': rules})
-
-
-# pure nat rules
-def normalize_nat_rules(full_config, config2import, import_id, jwt=None):
- nat_rules = []
- rule_number = 0
-
- for rule_table in rule_nat_scope:
- for localPkgName in full_config['rules_global_nat']:
- for rule_orig in full_config[rule_table][localPkgName]:
- rule = {'rule_src': '', 'rule_dst': '', 'rule_svc': ''}
- if rule_orig['nat'] == 1: # assuming source nat
- rule.update({ 'control_id': import_id})
- rule.update({ 'rulebase_name': localPkgName}) # the rulebase_name just has to be a unique string among devices
- rule.update({ 'rule_ruleid': rule_orig['policyid']})
- rule.update({ 'rule_uid': rule_orig['uuid']})
- # rule.update({ 'rule_num': rule_orig['obj seq']})
- rule.update({ 'rule_num': rule_number })
- if 'comments' in rule_orig:
- rule.update({ 'rule_comment': rule_orig['comments']})
- rule.update({ 'rule_action': 'Drop' }) # not used for nat rules
- rule.update({ 'rule_track': 'None'}) # not used for nat rules
-
- rule['rule_src'] = extend_string_list(rule['rule_src'], rule_orig, 'orig-addr', list_delimiter, jwt=jwt, import_id=import_id)
- rule['rule_dst'] = extend_string_list(rule['rule_dst'], rule_orig, 'dst-addr', list_delimiter, jwt=jwt, import_id=import_id)
-
- if rule_orig['protocol']==17:
- svc_name = 'udp_' + str(rule_orig['orig-port'])
- elif rule_orig['protocol']==6:
- svc_name = 'tcp_' + str(rule_orig['orig-port'])
- else:
- svc_name = 'svc_' + str(rule_orig['orig-port'])
- # need to create a helper service object and add it to the nat rule, also needs to be added to service list
-
- if not 'service_objects' in config2import: # is normally defined
- config2import['service_objects'] = []
- config2import['service_objects'].append(create_svc_object( \
- import_id=import_id, name=svc_name, proto=rule_orig['protocol'], port=rule_orig['orig-port'], comment='service created by FWO importer for NAT purposes'))
- rule['rule_svc'] = svc_name
-
- #rule['rule_src'] = extend_string_list(rule['rule_src'], rule_orig, 'srcaddr6', list_delimiter, jwt=jwt, import_id=import_id)
- #rule['rule_dst'] = extend_string_list(rule['rule_dst'], rule_orig, 'dstaddr6', list_delimiter, jwt=jwt, import_id=import_id)
-
- if len(rule_orig['srcintf'])>0:
- rule.update({ 'rule_from_zone': rule_orig['srcintf'][0] }) # todo: currently only using the first zone
- if len(rule_orig['dstintf'])>0:
- rule.update({ 'rule_to_zone': rule_orig['dstintf'][0] }) # todo: currently only using the first zone
-
- rule.update({ 'rule_src_neg': False})
- rule.update({ 'rule_dst_neg': False})
- rule.update({ 'rule_svc_neg': False})
- rule.update({ 'rule_src_refs': resolve_raw_objects(rule['rule_src'], list_delimiter, full_config, 'name', 'uuid', rule_type=rule_table) }, \
- jwt=jwt, import_id=import_id, rule_uid=rule_orig['uuid'], object_type='network object')
- rule.update({ 'rule_dst_refs': resolve_raw_objects(rule['rule_dst'], list_delimiter, full_config, 'name', 'uuid', rule_type=rule_table) }, \
- jwt=jwt, import_id=import_id, rule_uid=rule_orig['uuid'], object_type='network object')
- # services do not have uids, so using name instead
- rule.update({ 'rule_svc_refs': rule['rule_svc'] })
- rule.update({ 'rule_type': 'original' })
- rule.update({ 'rule_installon': localPkgName })
- if 'status' in rule_orig and (rule_orig['status']=='enable' or rule_orig['status']==1):
- rule.update({ 'rule_disabled': False })
- else:
- rule.update({ 'rule_disabled': True })
- rule.update({ 'rule_implied': False })
- rule.update({ 'rule_time': None })
- rule.update({ 'parent_rule_id': None })
-
- nat_rules.append(rule)
- add_users_to_rule(rule_orig, rule)
-
- ############## now adding the xlate rule part ##########################
- xlate_rule = dict(rule) # copy the original (match) rule
- xlate_rule.update({'rule_src': '', 'rule_dst': '', 'rule_svc': ''})
- xlate_rule['rule_src'] = extend_string_list(xlate_rule['rule_src'], rule_orig, 'orig-addr', list_delimiter, jwt=jwt, import_id=import_id)
- xlate_rule['rule_dst'] = 'Original'
-
- if rule_orig['protocol']==17:
- svc_name = 'udp_' + str(rule_orig['nat-port'])
- elif rule_orig['protocol']==6:
- svc_name = 'tcp_' + str(rule_orig['nat-port'])
- else:
- svc_name = 'svc_' + str(rule_orig['nat-port'])
- # need to create a helper service object and add it to the nat rule, also needs to be added to service list!
- # fmgr_service.create_svc_object(name=svc_name, proto=rule_orig['protocol'], port=rule_orig['orig-port'], comment='service created by FWO importer for NAT purposes')
- config2import['service_objects'].append(create_svc_object(import_id=import_id, name=svc_name, proto=rule_orig['protocol'], port=rule_orig['nat-port'], comment='service created by FWO importer for NAT purposes'))
- xlate_rule['rule_svc'] = svc_name
-
- xlate_rule.update({ 'rule_src_refs': resolve_objects(xlate_rule['rule_src'], list_delimiter, full_config, 'name', 'uuid', rule_type=rule_table, jwt=jwt, import_id=import_id ) })
- xlate_rule.update({ 'rule_dst_refs': resolve_objects(xlate_rule['rule_dst'], list_delimiter, full_config, 'name', 'uuid', rule_type=rule_table, jwt=jwt, import_id=import_id ) })
- xlate_rule.update({ 'rule_svc_refs': xlate_rule['rule_svc'] }) # services do not have uids, so using name instead
-
- xlate_rule.update({ 'rule_type': 'xlate' })
-
- nat_rules.append(xlate_rule)
- rule_number += 1
- config2import['rules'].extend(nat_rules)
-
-
-def insert_header(rules, import_id, header_text, rulebase_name, rule_uid, rule_number, src_refs, dst_refs):
- rule = {
- "control_id": import_id,
- "rule_head_text": header_text,
- "rulebase_name": rulebase_name,
- "rule_ruleid": None,
- "rule_uid": rule_uid + rulebase_name,
- "rule_num": rule_number,
- "rule_disabled": False,
- "rule_src": "all",
- "rule_dst": "all",
- "rule_svc": "ALL",
- "rule_src_neg": False,
- "rule_dst_neg": False,
- "rule_svc_neg": False,
- "rule_src_refs": src_refs,
- "rule_dst_refs": dst_refs,
- "rule_svc_refs": "ALL",
- "rule_action": "Accept",
- "rule_track": "None",
- "rule_installon": None,
- "rule_time": None,
- "rule_type": "access",
- "parent_rule_id": None,
- "rule_implied": False,
- "rule_comment": None
- }
- rules.append(rule)
-
-
-def create_xlate_rule(rule):
- xlate_rule = copy.deepcopy(rule)
- rule['rule_type'] = 'combined'
- xlate_rule['rule_type'] = 'xlate'
- xlate_rule['rule_comment'] = None
- xlate_rule['rule_disabled'] = False
- xlate_rule['rule_src'] = 'Original'
- xlate_rule['rule_src_refs'] = 'Original'
- xlate_rule['rule_dst'] = 'Original'
- xlate_rule['rule_dst_refs'] = 'Original'
- xlate_rule['rule_svc'] = 'Original'
- xlate_rule['rule_svc_refs'] = 'Original'
- return xlate_rule
-
-
-def handle_combined_nat_rule(rule, rule_orig, config2import, nat_rule_number, import_id, localPkgName, dev_id):
- # now dealing with VIPs (dst NAT part) of combined rules
- logger = getFwoLogger()
- xlate_rule = None
-
- # dealing with src NAT part of combined rules
- if "nat" in rule_orig and rule_orig["nat"]==1:
- logger.debug("found mixed Access/NAT rule no. " + str(nat_rule_number))
- nat_rule_number += 1
- xlate_rule = create_xlate_rule(rule)
- if 'ippool' in rule_orig:
- if rule_orig['ippool']==0: # hiding behind outbound interface
- interface_name = 'unknownIF'
- destination_interface_ip = '0.0.0.0'
- destination_ip = get_first_ip_of_destination(rule['rule_dst_refs'], config2import) # get an ip of destination
- hideInterface = 'undefined_interface'
- if destination_ip is None:
- logger.warning('src nat behind interface: found no valid destination ip in rule with UID ' + rule['rule_uid'])
- else:
- # matching_route = get_matching_route_obj(destination_ip, config2import['networking'][device_name]['routingv4'])
- matching_route = get_matching_route_obj(destination_ip, config2import['routing'], dev_id)
- if matching_route is None:
- logger.warning('src nat behind interface: found no matching route in rule with UID '
- + rule['rule_uid'] + ', dest_ip: ' + destination_ip)
- else:
- destination_interface_ip = get_ip_of_interface_obj(matching_route.interface, dev_id, config2import['interfaces'])
- interface_name = matching_route.interface
- hideInterface=interface_name
- if hideInterface is None:
- logger.warning('src nat behind interface: found route with undefined interface ' + str(jsonpickle.dumps(matching_route, unpicklable=True)))
- if destination_interface_ip is None:
- logger.warning('src nat behind interface: found no matching interface IP in rule with UID '
- + rule['rule_uid'] + ', dest_ip: ' + destination_ip)
-
- # add dummy object "outbound-interface"
- if hideInterface is not None:
- obj_name = 'hide_IF_ip_' + str(hideInterface) + '_' + str(destination_interface_ip)
- obj_comment = 'FWO auto-generated dummy object for source nat'
- if destination_interface_ip is not None and type(ipaddress.ip_address(str(destination_interface_ip))) is ipaddress.IPv6Address:
- HideNatIp = str(destination_interface_ip) + '/128'
- elif destination_interface_ip is not None and type(ipaddress.ip_address(str(destination_interface_ip))) is ipaddress.IPv4Address:
- HideNatIp = str(destination_interface_ip) + '/32'
- else:
- HideNatIp = '0.0.0.0/32'
- logger.warning('found invalid HideNatIP ' + str(destination_interface_ip))
- obj = create_network_object(import_id, obj_name, 'host', HideNatIp, obj_name, 'black', obj_comment, 'global')
- if obj not in config2import['network_objects']:
- config2import['network_objects'].append(obj)
- xlate_rule['rule_src'] = obj_name
- xlate_rule['rule_src_refs'] = obj_name
-
- elif rule_orig['ippool']==1: # hiding behind one ip of an ip pool
- poolNameArray = rule_orig['poolname']
- if len(poolNameArray)>0:
- if len(poolNameArray)>1:
- logger.warning("found more than one ippool - ignoring all but first pool")
- poolName = poolNameArray[0]
- xlate_rule['rule_src'] = poolName
- xlate_rule['rule_src_refs'] = poolName
- else:
- logger.warning("found ippool rule without ippool: " + rule['rule_uid'])
- else:
- logger.warning("found ippool rule with unexpected ippool value: " + rule_orig['ippool'])
-
- if 'natip' in rule_orig and rule_orig['natip']!=["0.0.0.0","0.0.0.0"]:
- logger.warning("found explicit natip rule - ignoring for now: " + rule['rule_uid'])
- # need example for interpretation of config
-
- # todo: find out how match-vip=1 influences natting (only set in a few vip-nat rules)
- # if "match-vip" in rule_orig and rule_orig["match-vip"]==1:
- # logger.warning("found VIP destination Access/NAT rule (but not parsing yet); no. " + str(vip_nat_rule_number))
- # vip_nat_rule_number += 1
-
- # deal with vip natting: check for each (dst) nw obj if it contains "obj_nat_ip"
- rule_dst_list = rule['rule_dst'].split(list_delimiter)
- nat_object_list = extract_nat_objects(rule_dst_list, config2import['network_objects'])
-
- if len(nat_object_list)>0:
- if xlate_rule is None: # no source nat, so we create the necessary nat rule here
- xlate_rule = create_xlate_rule(rule)
- xlate_dst = []
- xlate_dst_refs = []
- for nat_obj in nat_object_list:
- if 'obj_ip_end' in nat_obj: # this nat obj is a range - include the end ip in name and uid as well to avoid akey conflicts
- xlate_dst.append(nat_obj['obj_nat_ip'] + '-' + nat_obj['obj_ip_end'] + nat_postfix)
- nat_ref = nat_obj['obj_nat_ip']
- if 'obj_nat_ip_end' in nat_obj:
- nat_ref += '-' + nat_obj['obj_nat_ip_end'] + nat_postfix
- xlate_dst_refs.append(nat_ref)
- else:
- xlate_dst.append(nat_obj['obj_nat_ip'] + nat_postfix)
- xlate_dst_refs.append(nat_obj['obj_nat_ip'] + nat_postfix)
- xlate_rule['rule_dst'] = list_delimiter.join(xlate_dst)
- xlate_rule['rule_dst_refs'] = list_delimiter.join(xlate_dst_refs)
- # else: (no nat object found) no dnatting involved, dst stays "Original"
-
- return xlate_rule
-
-
-def insert_headers(rule_table, first_v6, first_v4, full_config, rules, import_id, localPkgName,src_ref_all,dst_ref_all,rule_number):
- if rule_table in rule_access_scope_v6 and first_v6:
- insert_header(rules, import_id, "IPv6 rules", localPkgName, "IPv6HeaderText", rule_number, src_ref_all, dst_ref_all)
- rule_number += 1
- first_v6 = False
- elif rule_table in rule_access_scope_v4 and first_v4:
- insert_header(rules, import_id, "IPv4 rules", localPkgName, "IPv4HeaderText", rule_number, src_ref_all, dst_ref_all)
- rule_number += 1
- first_v4 = False
- if rule_table == 'rules_adom_v4' and len(full_config['rules_adom_v4'][localPkgName])>0:
- insert_header(rules, import_id, "Adom Rules IPv4", localPkgName, "IPv4AdomRules", rule_number, src_ref_all, dst_ref_all)
- rule_number += 1
- elif rule_table == 'rules_adom_v6' and len(full_config['rules_adom_v6'][localPkgName])>0:
- insert_header(rules, import_id, "Adom Rules IPv6", localPkgName, "IPv6AdomRules", rule_number, src_ref_all, dst_ref_all)
- rule_number += 1
- elif rule_table == 'rules_global_header_v4' and len(full_config['rules_global_header_v4'][localPkgName])>0:
- insert_header(rules, import_id, "Global Header Rules IPv4", localPkgName, "IPv4GlobalHeaderRules", rule_number, src_ref_all, dst_ref_all)
- rule_number += 1
- elif rule_table == 'rules_global_header_v6' and len(full_config['rules_global_header_v6'][localPkgName])>0:
- insert_header(rules, import_id, "Global Header Rules IPv6", localPkgName, "IPv6GlobalHeaderRules", rule_number, src_ref_all, dst_ref_all)
- rule_number += 1
- elif rule_table == 'rules_global_footer_v4' and len(full_config['rules_global_footer_v4'][localPkgName])>0:
- insert_header(rules, import_id, "Global Footer Rules IPv4", localPkgName, "IPv4GlobalFooterRules", rule_number, src_ref_all, dst_ref_all)
- rule_number += 1
- elif rule_table == 'rules_global_footer_v6' and len(full_config['rules_global_footer_v6'][localPkgName])>0:
- insert_header(rules, import_id, "Global Footer Rules IPv6", localPkgName, "IPv6GlobalFooterRules", rule_number, src_ref_all, dst_ref_all)
- rule_number += 1
- return rule_number, first_v4, first_v6
-
-
-def extract_nat_objects(nwobj_list, all_nwobjects):
- nat_obj_list = []
- for obj in nwobj_list:
- for obj2 in all_nwobjects:
- if obj2['obj_name']==obj:
- if 'obj_nat_ip' in obj2:
- nat_obj_list.append(obj2)
- break
- # if obj in all_nwobjects and 'obj_nat_ip' in all_nwobjects[obj]:
- # nat_obj_list.append(obj)
- return nat_obj_list
-
-
-def add_users_to_rule(rule_orig, rule):
- if 'groups' in rule_orig:
- add_users(rule_orig['groups'], rule)
- if 'users' in rule_orig:
- add_users(rule_orig['users'], rule)
-
-
-def add_users(users, rule):
- for user in users:
- rule_src_with_users = []
- for src in rule['rule_src'].split(list_delimiter):
- rule_src_with_users.append(user + '@' + src)
- rule['rule_src'] = list_delimiter.join(rule_src_with_users)
-
- # here user ref is the user name itself
- rule_src_refs_with_users = []
- for src in rule['rule_src_refs'].split(list_delimiter):
- rule_src_refs_with_users.append(user + '@' + src)
- rule['rule_src_refs'] = list_delimiter.join(rule_src_refs_with_users)
diff --git a/roles/importer/files/importer/fortiadom5ff/fmgr_service.py b/roles/importer/files/importer/fortiadom5ff/fmgr_service.py
deleted file mode 100644
index 19cdb947c1..0000000000
--- a/roles/importer/files/importer/fortiadom5ff/fmgr_service.py
+++ /dev/null
@@ -1,198 +0,0 @@
-import re
-from fwo_const import list_delimiter
-
-def normalize_svcobjects(full_config, config2import, import_id, scope):
- svc_objects = []
- for s in scope:
- for obj_orig in full_config[s]:
- member_names = ''
- if 'member' in obj_orig:
- type = 'group'
- for member in obj_orig['member']:
- member_names += member + list_delimiter
- member_names = member_names[:-1]
- else:
- type = 'simple'
-
- name = None
- if 'name' in obj_orig:
- name = str(obj_orig['name'])
-
- color = None
- if 'color' in obj_orig and str(obj_orig['color']) != 0:
- color = str(obj_orig['color'])
-
- session_timeout = None # todo: find the right timer
- # if 'udp-idle-timer' in obj_orig and str(obj_orig['udp-idle-timer']) != 0:
- # session_timeout = str(obj_orig['udp-idle-timer'])
-
- proto = 0
- range_names = ''
- if 'protocol' in obj_orig:
- added_svc_obj = 0
- if obj_orig['protocol'] == 1:
- addObject(svc_objects, type, name, color, 1, None, None, session_timeout, import_id)
- added_svc_obj += 1
- elif obj_orig['protocol'] == 2:
- if 'protocol-number' in obj_orig:
- proto = obj_orig['protocol-number']
- addObject(svc_objects, type, name, color, proto, None, None, session_timeout, import_id)
- added_svc_obj += 1
- elif obj_orig['protocol'] == 5 or obj_orig['protocol'] == 11:
- split = check_split(obj_orig)
- if "tcp-portrange" in obj_orig and len(obj_orig['tcp-portrange']) > 0:
- tcpname = name
- if split:
- tcpname += "_tcp"
- range_names += tcpname + list_delimiter
- addObject(svc_objects, type, tcpname, color, 6, obj_orig['tcp-portrange'], None, session_timeout, import_id)
- added_svc_obj += 1
- if "udp-portrange" in obj_orig and len(obj_orig['udp-portrange']) > 0:
- udpname = name
- if split:
- udpname += "_udp"
- range_names += udpname + list_delimiter
- addObject(svc_objects, type, udpname, color, 17, obj_orig['udp-portrange'], None, session_timeout, import_id)
- added_svc_obj += 1
- if "sctp-portrange" in obj_orig and len(obj_orig['sctp-portrange']) > 0:
- sctpname = name
- if split:
- sctpname += "_sctp"
- range_names += sctpname + list_delimiter
- addObject(svc_objects, type, sctpname, color, 132, obj_orig['sctp-portrange'], None, session_timeout, import_id)
- added_svc_obj += 1
- if split:
- range_names = range_names[:-1]
- addObject(svc_objects, 'group', name, color, 0, None, range_names, session_timeout, import_id)
- added_svc_obj += 1
- if added_svc_obj==0: # assuming RPC service which here has no properties at all
- addObject(svc_objects, 'rpc', name, color, 0, None, None, None, import_id)
- added_svc_obj += 1
- elif obj_orig['protocol'] == 6:
- addObject(svc_objects, type, name, color, 58, None, None, session_timeout, import_id)
- elif type == 'group':
- addObject(svc_objects, type, name, color, 0, None, member_names, session_timeout, import_id)
- else:
- addObject(svc_objects, type, name, color, 0, None, None, session_timeout, import_id)
-
- # finally add "Original" service object for natting
- original_obj_name = 'Original'
- svc_objects.append(create_svc_object(import_id=import_id, name=original_obj_name, proto=0, port=None,\
- comment='"original" service object created by FWO importer for NAT purposes'))
-
- config2import.update({'service_objects': svc_objects})
-
-
-def check_split(obj_orig):
- count = 0
- if "tcp-portrange" in obj_orig and len(obj_orig['tcp-portrange']) > 0:
- count += 1
- if "udp-portrange" in obj_orig and len(obj_orig['udp-portrange']) > 0:
- count += 1
- if "sctp-portrange" in obj_orig and len(obj_orig['sctp-portrange']) > 0:
- count += 1
- return (count > 1)
-
-
-def extractPorts(port_ranges):
- ports = []
- port_ends = []
- if port_ranges is not None and len(port_ranges) > 0:
- for port_range in port_ranges:
- # remove src-ports
- port = port_range.split(':')[0]
- port_end = port
-
- # open ranges (not found so far in data)
- pattern = re.compile('^\>(\d+)$')
- match = pattern.match(port)
- if match:
- port = str(int(match.group()[1:]) + 1)
- port_end = str(65535)
- pattern = re.compile('^\<(\d+)$')
- match = pattern.match(port)
- if match:
- port = str(1)
- port_end = str(int(match.group()[1:]) - 1)
-
- # split ranges
- pattern = re.compile('^(\d+)\-(\d+)$')
- match = pattern.match(port)
- if match:
- port, port_end = match.group().split('-')
- ports.append(port)
- port_ends.append(port_end)
- return ports, port_ends
-
-
-
-def create_svc_object(import_id, name, proto, port, comment):
- return {
- 'control_id': import_id,
- 'svc_name': name,
- 'svc_typ': 'simple',
- 'svc_port': port,
- 'ip_proto': proto,
- 'svc_uid': name, # services have no uid in fortimanager
- 'svc_comment': comment
- }
-
-
-
-def addObject(svc_objects, type, name, color, proto, port_ranges, member_names, session_timeout, import_id):
- if port_ranges is None:
- svc_objects.extend([{'svc_typ': type,
- 'svc_name': name,
- 'svc_color': color,
- 'svc_uid': name, # ?
- 'svc_comment': None, # ?
- 'ip_proto': proto,
- 'svc_port': None,
- 'svc_port_end': None,
- 'svc_member_refs': member_names, # ?
- 'svc_member_names': member_names,
- 'svc_timeout': session_timeout,
- 'rpc_nr': None, # ?
- 'control_id': import_id
- }])
- else:
- range_names = ''
- ports, port_ends = extractPorts(port_ranges)
- split = (len(ports) > 1)
- for index, port in enumerate(ports):
- port_end = port_ends[index]
- full_name = name
- if split:
- full_name += '_' + str(port)
- range_names += full_name + list_delimiter
- svc_objects.extend([{'svc_typ': type,
- 'svc_name': full_name,
- 'svc_color': color,
- 'svc_uid': full_name, # ?
- 'svc_comment': None, # ?
- 'ip_proto': proto,
- 'svc_port': port,
- 'svc_port_end': port_end,
- 'svc_member_refs': member_names, # ?
- 'svc_member_names': member_names,
- 'svc_timeout': session_timeout,
- 'rpc_nr': None, # ?
- 'control_id': import_id
- }])
- if split:
- range_names = range_names[:-1]
- svc_objects.extend([{'svc_typ': 'group',
- 'svc_name': name,
- 'svc_color': color,
- 'svc_uid': name, # ?
- 'svc_comment': None, # ?
- 'ip_proto': proto,
- 'svc_port': None,
- 'svc_port_end': None,
- 'svc_member_refs': range_names, # ?
- 'svc_member_names': range_names,
- 'svc_timeout': session_timeout,
- 'rpc_nr': None, # ?
- 'control_id': import_id
- }])
-
diff --git a/roles/importer/files/importer/fortiadom5ff/fmgr_user.py b/roles/importer/files/importer/fortiadom5ff/fmgr_user.py
deleted file mode 100644
index c8323694b7..0000000000
--- a/roles/importer/files/importer/fortiadom5ff/fmgr_user.py
+++ /dev/null
@@ -1,36 +0,0 @@
-from fwo_const import list_delimiter
-
-def normalize_users(full_config, config2import, import_id, user_scope):
- users = []
- for scope in user_scope:
- for user_orig in full_config[scope]:
- name = None
- type = 'simple'
- color = None
- member_names = None
- comment = None
-
- if 'member' in user_orig:
- type = 'group'
- member_names = ''
- for member in user_orig['member']:
- member_names += member + list_delimiter
- member_names = member_names[:-1]
- if 'name' in user_orig:
- name = str(user_orig['name'])
- if 'comment' in user_orig:
- comment = str(user_orig['comment'])
- if 'color' in user_orig and str(user_orig['color']) != 0:
- color = str(user_orig['color'])
-
- users.extend([{'user_typ': type,
- 'user_name': name,
- 'user_color': color,
- 'user_uid': name,
- 'user_comment': comment,
- 'user_member_refs': member_names,
- 'user_member_names': member_names,
- 'control_id': import_id
- }])
-
- config2import.update({'user_objects': users})
diff --git a/roles/importer/files/importer/fortiadom5ff/fmgr_zone.py b/roles/importer/files/importer/fortiadom5ff/fmgr_zone.py
deleted file mode 100644
index b9e41a1e68..0000000000
--- a/roles/importer/files/importer/fortiadom5ff/fmgr_zone.py
+++ /dev/null
@@ -1,29 +0,0 @@
-
-def normalize_zones(full_config, config2import, import_id):
- zones = []
- for orig_zone in full_config['zone_objects']['zone_list']:
- zone = {}
- zone.update({'zone_name': orig_zone})
- zone.update({'control_id': import_id})
- zones.append(zone)
-
- config2import.update({'zone_objects': zones})
-
-
-def add_zone_if_missing (config2import, zone_string, import_id):
- # adding zone if it not yet exists
-
- # also transforming any into global (normalized global zone)
- if zone_string == 'any':
- zone_string = 'global'
- if zone_string is not None:
- if 'zone_objects' not in config2import: # no zones yet? add empty zone_objects array
- config2import.update({'zone_objects': []})
- zone_exists = False
- for zone in config2import['zone_objects']:
- if zone_string == zone['zone_name']:
- zone_exists = True
- if not zone_exists:
- config2import['zone_objects'].append({'zone_name': zone_string, 'control_id': import_id})
- return zone_string
-
\ No newline at end of file
diff --git a/roles/importer/files/importer/fortiadom5ff/fwcommon.py b/roles/importer/files/importer/fortiadom5ff/fwcommon.py
deleted file mode 100644
index 414a1bde50..0000000000
--- a/roles/importer/files/importer/fortiadom5ff/fwcommon.py
+++ /dev/null
@@ -1,202 +0,0 @@
-import sys
-from common import importer_base_dir
-sys.path.append(importer_base_dir + '/fortiadom5ff')
-import fmgr_user
-import fmgr_service
-import fmgr_zone
-import fmgr_rule
-import fmgr_network
-import fmgr_getter
-from curses import raw
-from fwo_log import getFwoLogger
-from fmgr_gw_networking import getInterfacesAndRouting, normalize_network_data
-from fwo_data_networking import get_ip_of_interface_obj
-
-scope = ['global', 'adom']
-nw_obj_types = ['firewall/address', 'firewall/address6', 'firewall/addrgrp',
- 'firewall/addrgrp6', 'firewall/ippool', 'firewall/vip', 'system/external-resource']
-svc_obj_types = ['application/list', 'application/group', 'application/categories',
- 'application/custom', 'firewall/service/custom', 'firewall/service/group']
-
-# build the product of all scope/type combinations
-nw_obj_scope = ['nw_obj_' + s1 + '_' +
- s2 for s1 in scope for s2 in nw_obj_types]
-svc_obj_scope = ['svc_obj_' + s1 + '_' +
- s2 for s1 in scope for s2 in svc_obj_types]
-
-# zone_types = ['zones_global', 'zones_adom']
-
-user_obj_types = ['user/local', 'user/group']
-user_scope = ['user_obj_' + s1 + '_' +
- s2 for s1 in scope for s2 in user_obj_types]
-
-
-def has_config_changed(full_config, mgm_details, force=False):
- # dummy - may be filled with real check later on
- return True
-
-
-def get_config(config2import, full_config, current_import_id, mgm_details, limit=100, force=False, jwt=''):
- logger = getFwoLogger()
- if full_config == {}: # no native config was passed in, so getting it from FortiManager
- parsing_config_only = False
- else:
- parsing_config_only = True
-
- # fmgr API login
- if not parsing_config_only: # no native config was passed in, so getting it from FortiManager
- fm_api_url = 'https://' + \
- mgm_details['hostname'] + ':' + \
- str(mgm_details['port']) + '/jsonrpc'
- sid = fmgr_getter.login(mgm_details['import_credential']['user'], mgm_details['import_credential']['secret'], fm_api_url)
- if sid is None:
- logger.error('did not succeed in logging in to FortiManager API, no sid returned')
- return 1
-
- adom_name = mgm_details['configPath']
- if adom_name is None:
- logger.error('no ADOM name set for management ' + mgm_details['id'])
- return 1
- else:
- if not parsing_config_only: # no native config was passed in, so getting it from FortiManager
- getObjects(sid, fm_api_url, full_config, adom_name, limit, scope, nw_obj_types, svc_obj_types)
- # currently reading zone from objects/rules for backward compat with FortiManager 6.x
- # getZones(sid, fm_api_url, full_config, adom_name, limit, debug_level)
- getInterfacesAndRouting(
- sid, fm_api_url, full_config, adom_name, mgm_details['devices'], limit)
-
- # initialize all rule dicts
- fmgr_rule.initializeRulebases(full_config)
- for dev in mgm_details['devices']:
- fmgr_rule.getAccessPolicy(
- sid, fm_api_url, full_config, adom_name, dev, limit)
- fmgr_rule.getNatPolicy(
- sid, fm_api_url, full_config, adom_name, dev, limit)
-
- try: # logout of fortimanager API
- fmgr_getter.logout(
- fm_api_url, sid)
- except:
- logger.warning("logout exception probably due to timeout - irrelevant, so ignoring it")
-
- # now we normalize relevant parts of the raw config and write the results to config2import dict
- # currently reading zone from objects for backward compat with FortiManager 6.x
- # fmgr_zone.normalize_zones(full_config, config2import, current_import_id)
-
- # write normalized networking data to config2import
- # this is currently not written to the database but only used for natting decisions
- # later we will probably store the networking info in the database as well as a basis
- # for path analysis
-
- normalize_network_data(full_config, config2import, mgm_details)
-
- fmgr_user.normalize_users(
- full_config, config2import, current_import_id, user_scope)
- fmgr_network.normalize_nwobjects(
- full_config, config2import, current_import_id, nw_obj_scope, jwt=jwt, mgm_id=mgm_details['id'])
- fmgr_service.normalize_svcobjects(
- full_config, config2import, current_import_id, svc_obj_scope)
- fmgr_user.normalize_users(
- full_config, config2import, current_import_id, user_scope)
- fmgr_rule.normalize_access_rules(
- full_config, config2import, current_import_id, mgm_details=mgm_details, jwt=jwt)
- fmgr_rule.normalize_nat_rules(
- full_config, config2import, current_import_id, jwt=jwt)
- fmgr_network.remove_nat_ip_entries(config2import)
- return 0
-
-
-def getObjects(sid, fm_api_url, raw_config, adom_name, limit, scope, nw_obj_types, svc_obj_types):
- logger = getFwoLogger()
- # get those objects that exist globally and on adom level
- for s in scope:
- # get network objects:
- for object_type in nw_obj_types:
- if s == 'adom':
- adom_scope = 'adom/'+adom_name
- else:
- adom_scope = s
- fmgr_getter.update_config_with_fortinet_api_call(
- raw_config, sid, fm_api_url, "/pm/config/"+adom_scope+"/obj/" + object_type, "nw_obj_" + s + "_" + object_type, limit=limit)
-
- # get service objects:
- # service/custom is an undocumented API call!
- for object_type in svc_obj_types:
- if s == 'adom':
- adom_scope = 'adom/'+adom_name
- else:
- adom_scope = s
- fmgr_getter.update_config_with_fortinet_api_call(
- raw_config, sid, fm_api_url, "/pm/config/"+adom_scope+"/obj/" + object_type, "svc_obj_" + s + "_" + object_type, limit=limit)
-
- # user: /pm/config/global/obj/user/local, /pm/config/global/obj/user/group
- # get user objects:
- for object_type in user_obj_types:
- if s == 'adom':
- adom_scope = 'adom/'+adom_name
- else:
- adom_scope = s
- fmgr_getter.update_config_with_fortinet_api_call(
- raw_config, sid, fm_api_url, "/pm/config/"+adom_scope+"/obj/" + object_type, "user_obj_" + s + "_" + object_type, limit=limit)
-
- # get one arbitrary device and vdom to get dynamic objects
- # they are equal across all adoms, vdoms, devices
- devices = fmgr_getter.fortinet_api_call(sid, fm_api_url, '/dvmdb/adom/' + adom_name + '/device')
- if len(devices)>0 and 'name' in devices[0] and 'vdom' in devices[0] and 'name' in devices[0]['vdom'][0]:
- arbitraryDevice = devices[0]['name']
- arbitraryVdom = devices[0]['vdom'][0]['name']
- else:
- logger.error('no device or vdom info for adom: ' + adom_name)
-
- # get dynamic objects
- payload = {
- 'params': [
- {
- 'data': {
- 'action': 'get',
- 'resource': '/api/v2/monitor/firewall/internet-service-basic?vdom=' + arbitraryVdom,
- 'target': [
- 'adom/' + adom_name + '/device/' + arbitraryDevice
- ]
- }
- }
- ]
- }
- fmgr_getter.update_config_with_fortinet_api_call(
- raw_config, sid, fm_api_url, "sys/proxy/json", "nw_obj_global_firewall/internet-service-basic", limit=limit, payload=payload, method='exec')
-
-
-
-
-
-# def getZones(sid, fm_api_url, raw_config, adom_name, limit, debug_level):
-# raw_config.update({"zones": {}})
-
-# # get global zones?
-
-# # get local zones
-# for device in raw_config['devices']:
-# local_pkg_name = device['package']
-# for adom in raw_config['adoms']:
-# if adom['name']==adom_name:
-# if local_pkg_name not in adom['package_names']:
-# logger.error('local rulebase/package ' + local_pkg_name + ' not found in management ' + adom_name)
-# return 1
-# else:
-# fmgr_getter.update_config_with_fortinet_api_call(
-# raw_config['zones'], sid, fm_api_url, "/pm/config/adom/" + adom_name + "/obj/dynamic/interface", device['id'], debug=debug_level, limit=limit)
-
-# raw_config['zones']['zone_list'] = []
-# for device in raw_config['zones']:
-# for mapping in raw_config['zones'][device]:
-# if not isinstance(mapping, str):
-# if not mapping['dynamic_mapping'] is None:
-# for dyn_mapping in mapping['dynamic_mapping']:
-# if 'name' in dyn_mapping and not dyn_mapping['name'] in raw_config['zones']['zone_list']:
-# raw_config['zones']['zone_list'].append(dyn_mapping['name'])
-# if 'local-intf' in dyn_mapping and not dyn_mapping['local-intf'][0] in raw_config['zones']['zone_list']:
-# raw_config['zones']['zone_list'].append(dyn_mapping['local-intf'][0])
-# if not mapping['platform_mapping'] is None:
-# for dyn_mapping in mapping['platform_mapping']:
-# if 'intf-zone' in dyn_mapping and not dyn_mapping['intf-zone'] in raw_config['zones']['zone_list']:
-# raw_config['zones']['zone_list'].append(dyn_mapping['intf-zone'])
diff --git a/roles/importer/files/importer/fortiosmanagementREST/fOS_common.py b/roles/importer/files/importer/fortiosmanagementREST/fOS_common.py
deleted file mode 100644
index 154be9d417..0000000000
--- a/roles/importer/files/importer/fortiosmanagementREST/fOS_common.py
+++ /dev/null
@@ -1,34 +0,0 @@
-import sys
-from common import importer_base_dir
-sys.path.append(importer_base_dir + '/fortiosmanagementREST')
-from curses import raw
-from fwo_log import getFwoLogger
-from fwo_const import list_delimiter, fwo_config_filename
-from fwo_config import readConfig
-from fwo_api import setAlert, create_data_issue
-
-
-# TODO: deal with objects with identical names (e.g. all ipv4 & all ipv6)
-def resolve_objects (obj_name_string_list, lookup_dict={}, delimiter=list_delimiter, jwt=None, import_id=None, mgm_id=None):
- logger = getFwoLogger()
- fwo_config = readConfig(fwo_config_filename)
-
- ref_list = []
- objects_not_found = []
- for el in obj_name_string_list.split(delimiter):
- found = False
- if el in lookup_dict:
- ref_list.append(lookup_dict[el])
- else:
- objects_not_found.append(el)
-
- for obj in objects_not_found:
- if obj != 'all' and obj != 'Original':
- if not create_data_issue(fwo_config['fwo_api_base_url'], jwt, import_id=import_id, obj_name=obj, severity=1, mgm_id=mgm_id):
- logger.warning("resolve_raw_objects: encountered error while trying to log an import data issue using create_data_issue")
-
- desc = "found a broken object reference '" + obj + "' "
- setAlert(fwo_config['fwo_api_base_url'], jwt, import_id=import_id, title="object reference error", mgm_id=mgm_id, severity=1, role='importer', \
- description=desc, source='import', alertCode=16)
-
- return delimiter.join(ref_list)
diff --git a/roles/importer/files/importer/fortiosmanagementREST/fOS_getter.py b/roles/importer/files/importer/fortiosmanagementREST/fOS_getter.py
deleted file mode 100644
index 35d473d061..0000000000
--- a/roles/importer/files/importer/fortiosmanagementREST/fOS_getter.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# library for API get functions
-import re
-from fwo_log import getFwoLogger
-import requests.packages
-import requests
-import json
-import fwo_globals
-from fwo_exception import FwLoginFailed
-
-
-def api_call(url, show_progress=False):
- logger = getFwoLogger()
- request_headers = {'Content-Type': 'application/json'}
-
- r = requests.get(url, headers=request_headers, verify=fwo_globals.verify_certs)
- if r is None:
- exception_text = "error while sending api_call to url '" + str(url) + "' with headers: '" + json.dumps(request_headers, indent=2)
- raise Exception(exception_text)
- result_json = r.json()
- if 'results' not in result_json:
- raise Exception("error while sending api_call to url '" + str(url) + "' with headers: '" + json.dumps(request_headers, indent=2) + ', results=' + json.dumps(r.json()['results'], indent=2))
- if 'status' not in result_json:
- # trying to ignore empty results as valid
- pass # logger.warning('received empty result')
- if fwo_globals.debug_level>2:
- logger.debug("api_call to url '" + str(url) + "' with headers: '" + json.dumps(request_headers, indent=2))
- return result_json
-
-
-def set_api_url(base_url, testmode, api_supported, hostname):
- url = ''
- if testmode == 'off':
- url = base_url
- else:
- if re.search(r'^\d+[\.\d+]+$', testmode) or re.search(r'^\d+$', testmode):
- if testmode in api_supported:
- url = base_url + 'v' + testmode + '/'
- else:
- raise Exception("api version " + testmode +
- " is not supported by the manager " + hostname + " - Import is canceled")
- else:
- raise Exception("\"" + testmode + "\" - not a valid version")
- return url
-
-
-def update_config_with_fortiOS_api_call(config_json, api_url, result_name, show_progress=False, limit=150):
- offset = 0
- limit = int(limit)
- returned_new_objects = True
- full_result = []
- result = fortiOS_api_call(api_url)
- full_result.extend(result)
- # removing loop for api gets (no limit option in FortiOS API)
- # while returned_new_objects:
- # range = [offset, limit]
- # result = fortiOS_api_call(api_url)
- # full_result.extend(result)
- # offset += limit
- # if len(result) 1:
- obj.update({ 'obj_typ': 'network' })
- else:
- obj.update({ 'obj_typ': 'host' })
- obj.update({ 'obj_ip': ipa.with_prefixlen })
- elif 'ip6' in obj_orig: # ipv6 object
- ipa = ipaddress.ip_network(str(obj_orig['ip6']).replace("\\", ""))
- if ipa.num_addresses > 1:
- obj.update({ 'obj_typ': 'network' })
- else:
- obj.update({ 'obj_typ': 'host' })
- obj.update({ 'obj_ip': ipa.with_prefixlen })
- elif 'member' in obj_orig: # addrgrp4 / addrgrp6
- obj.update({ 'obj_typ': 'group' })
- obj.update({ 'obj_member_names' : list_delimiter.join([d['name'] for d in obj_orig['member']]) })
- obj.update({ 'obj_member_refs' : list_delimiter.join([d['name'] for d in obj_orig['member']]) })
- elif 'startip' in obj_orig: # ippool object
- obj.update({ 'obj_typ': 'ip_range' })
- obj.update({ 'obj_ip': obj_orig['startip'] })
- obj.update({ 'obj_ip_end': obj_orig['endip'] })
- elif 'start-ip' in obj_orig: # standard ip range object
- obj.update({ 'obj_typ': 'ip_range' })
- obj.update({ 'obj_ip': obj_orig['start-ip'] })
- obj.update({ 'obj_ip_end': obj_orig['end-ip'] })
- elif 'extip' in obj_orig: # vip object, simplifying to a single ip
- obj.update({ 'obj_typ': 'host' })
- if 'extip' not in obj_orig or len(obj_orig['extip'])==0:
- logger.error("vip (extip): found empty extip field for " + obj_orig['name'])
- else:
- set_ip_in_obj(obj, obj_orig['extip']) # resolving nat range if there is one
- nat_obj = {}
- nat_obj.update({'obj_typ': 'host' })
- nat_obj.update({'obj_color': 'black'})
- nat_obj.update({'obj_comment': 'FWO-auto-generated nat object for VIP'})
- if 'obj_ip_end' in obj: # this obj is a range - include the end ip in name and uid as well to avoid akey conflicts
- nat_obj.update({'obj_ip_end': obj['obj_ip_end']})
-
- # now dealing with the nat ip obj (mappedip)
- if 'mappedip' not in obj_orig or len(obj_orig['mappedip'])==0:
- logger.warning("vip (extip): found empty mappedip field for " + obj_orig['name'])
- else:
- if len(obj_orig['mappedip'])>1:
- logger.warning("vip (extip): found more than one mappedip, just using the first one for " + obj_orig['name'])
- nat_ip = obj_orig['mappedip'][0]['range']
- set_ip_in_obj(nat_obj, nat_ip)
- obj.update({ 'obj_nat_ip': nat_obj['obj_ip'] }) # save nat ip in vip obj
- if 'obj_ip_end' in nat_obj: # this nat obj is a range - include the end ip in name and uid as well to avoid akey conflicts
- obj.update({ 'obj_nat_ip_end': nat_obj['obj_ip_end'] }) # save nat ip in vip obj
- nat_obj.update({'obj_name': nat_obj['obj_ip'] + '-' + nat_obj['obj_ip_end'] + nat_postfix})
- else:
- nat_obj.update({'obj_name': str(nat_obj['obj_ip']) + nat_postfix})
- nat_obj.update({'obj_uid': nat_obj['obj_name']})
- ###### range handling
-
- if 'associated-interface' in obj_orig and len(obj_orig['associated-interface'])>0: # and obj_orig['associated-interface'][0] != 'any':
- obj_zone = obj_orig['associated-interface'][0]
- nat_obj.update({'obj_zone': obj_zone })
- nat_obj.update({'control_id': import_id})
- if nat_obj not in nw_objects: # rare case when a destination nat is down for two different orig ips to the same dest ip
- nw_objects.append(nat_obj)
- else:
- pass
- else: # 'fqdn' in obj_orig: # "fully qualified domain name address" // other unknown types
- obj.update({ 'obj_typ': 'network' })
- obj.update({ 'obj_ip': '0.0.0.0/0'})
- if 'comment' in obj_orig:
- obj.update({'obj_comment': obj_orig['comment']})
- if 'color' in obj_orig and obj_orig['color']==0:
- obj.update({'obj_color': 'black'}) # todo: deal with all other colors (will be currently ignored)
- # we would need a list of fortinet color codes
- if 'uuid' not in obj_orig:
- obj_orig.update({'uuid': obj_orig['name']})
- obj.update({'obj_uid': obj_orig['uuid']})
-
- # here only picking first associated interface as zone:
- if 'associated-interface' in obj_orig and len(obj_orig['associated-interface'])>0: # and obj_orig['associated-interface'][0] != 'any':
- obj_zone = obj_orig['associated-interface'][0]
- # adding zone if it not yet exists
- obj_zone = add_zone_if_missing (config2import, obj_zone, import_id)
- obj.update({'obj_zone': obj_zone })
-
- obj.update({'control_id': import_id})
- nw_objects.append(obj)
- full_config['nw_obj_lookup_dict'][obj['obj_name']] = obj['obj_uid']
-
- # finally add "Original" network object for natting
- original_obj_name = 'Original'
- original_obj_uid = 'Original'
- orig_obj = create_network_object(import_id=import_id, name=original_obj_name, type='network', ip='0.0.0.0/0',\
- uid=original_obj_uid, zone='global', color='black', comment='"original" network object created by FWO importer for NAT purposes')
- full_config['nw_obj_lookup_dict'][original_obj_name] = original_obj_uid
- nw_objects.append(orig_obj)
-
- resolve_nw_groups(nw_objects)
- config2import.update({'network_objects': nw_objects})
-
-
-def set_ip_in_obj(nw_obj, ip): # add start and end ip in nw_obj if it is a range, otherwise do nothing
- if '-' in ip: # dealing with range
- ip_start, ip_end = ip.split('-')
- nw_obj.update({'obj_ip': ip_start })
- if ip_end != ip_start:
- nw_obj.update({'obj_ip_end': ip_end })
- else:
- nw_obj.update({'obj_ip': ip })
-
-
-# for members of groups, the name of the member obj needs to be fetched separately (starting from API v1.?)
-def resolve_nw_uid_to_name(uid, nw_objects):
- # return name of nw_objects element where obj_uid = uid
- for obj in nw_objects:
- if obj['obj_uid'] == uid:
- return obj['obj_name']
- return 'ERROR: uid "' + uid + '" not found'
-
-
-def resolve_nw_groups(nw_objects):
- # add uids (if possible)
-
- # build helper dict with idx = name
- helper_dict = {}
- for obj in nw_objects:
- helper_dict[obj['obj_name']] = obj['obj_uid']
-
- for obj in nw_objects:
- if obj['obj_typ'] == 'group':
- member_ref_ar = []
- for member_name in obj['obj_member_names'].split(list_delimiter):
- member_ref_ar.append(helper_dict[member_name])
- obj['obj_member_refs'] = list_delimiter.join(member_ref_ar)
-
-
-# def add_member_names_for_nw_group(idx, nw_objects):
-# group = nw_objects.pop(idx)
-# if group['obj_member_refs'] == '' or group['obj_member_refs'] == None:
-# #member_names = None
-# #obj_member_refs = None
-# group['obj_member_names'] = None
-# group['obj_member_refs'] = None
-# else:
-# member_names = ''
-# obj_member_refs = group['obj_member_refs'].split(list_delimiter)
-# for ref in obj_member_refs:
-# member_name = resolve_nw_uid_to_name(ref, nw_objects)
-# member_names += member_name + list_delimiter
-# group['obj_member_names'] = member_names[:-1]
-# nw_objects.insert(idx, group)
-
-
-def create_network_object(import_id, name, type, ip, uid, color, comment, zone):
- # if zone is None or zone == '':
- # zone = 'global'
- return {
- 'control_id': import_id,
- 'obj_name': name,
- 'obj_typ': type,
- 'obj_ip': ip,
- 'obj_uid': uid,
- 'obj_color': color,
- 'obj_comment': comment,
- 'obj_zone': zone
- }
-
-
-# TODO: reduce commplexity if possible
-def get_nw_obj(nat_obj_name, nwobjects):
- for obj in nwobjects:
- if 'obj_name' in obj and obj['obj_name']==nat_obj_name:
- return obj
- return None
-
-
-# this removes all obj_nat_ip entries from all network objects
-# these were used during import but might cause issues if imported into db
-def remove_nat_ip_entries(config2import):
- for obj in config2import['network_objects']:
- if 'obj_nat_ip' in obj:
- obj.pop('obj_nat_ip')
-
-
-def get_first_ip_of_destination(obj_ref, config2import):
-
- logger = getFwoLogger()
- if list_delimiter in obj_ref:
- obj_ref = obj_ref.split(list_delimiter)[0]
- # if destination does not contain exactly one ip, raise a warning
- logger.info('src nat behind interface: more than one NAT IP - just using the first one for routing decision for obj_ref ' + obj_ref)
-
- for obj in config2import['network_objects']:
- if 'obj_uid' in obj and obj['obj_uid']==obj_ref:
- return obj['obj_ip']
- logger.warning('src nat behind interface: found no IP info for destination object ' + obj_ref)
- return None
diff --git a/roles/importer/files/importer/fortiosmanagementREST/fOS_rule.py b/roles/importer/files/importer/fortiosmanagementREST/fOS_rule.py
deleted file mode 100644
index df4ff9a506..0000000000
--- a/roles/importer/files/importer/fortiosmanagementREST/fOS_rule.py
+++ /dev/null
@@ -1,466 +0,0 @@
-import copy
-import jsonpickle
-from fwo_const import list_delimiter, nat_postfix
-from fwo_base import extend_string_list
-from fOS_service import create_svc_object
-from fOS_network import create_network_object, get_first_ip_of_destination
-import fOS_zone, fOS_getter
-#from fOS_gw_networking import get_device_from_package
-from fwo_log import getFwoLogger
-from fwo_data_networking import get_matching_route_obj, get_ip_of_interface_obj
-import ipaddress
-from fOS_common import resolve_objects
-import time
-
-
-rule_access_scope_v4 = ['rules']
-rule_access_scope_v6 = []
-
-rule_access_scope = ['rules']
-rule_nat_scope = ['rules_nat']
-rule_scope = rule_access_scope + rule_nat_scope
-
-
-def initializeRulebases(raw_config):
- for scope in rule_scope:
- if scope not in raw_config:
- raw_config.update({scope: {}})
-
-
-def getAccessPolicy(sid, fm_api_url, raw_config, limit):
- fOS_getter.update_config_with_fortiOS_api_call(raw_config['rules'], fm_api_url + "/cmdb/firewall/policy" + "?access_token=" + sid, 'rules', limit=limit)
- if 'rules' not in raw_config or 'rules' not in raw_config['rules']:
- logger = getFwoLogger()
- logger.warning('did not receive any access rules via API')
-
-
-def getNatPolicy(sid, fm_api_url, raw_config, adom_name, device, limit):
- scope = 'global'
- pkg = device['global_rulebase_name']
- if pkg is not None and pkg != '': # only read global rulebase if it exists
- for nat_type in ['central/dnat', 'central/dnat6', 'firewall/central-snat-map']:
- fOS_getter.update_config_with_fortinet_api_call(
- raw_config['rules_global_nat'], sid, fm_api_url, "/pm/config/" + scope + "/pkg/" + pkg + '/' + nat_type, device['local_rulebase_name'], limit=limit)
-
- scope = 'adom/'+adom_name
- pkg = device['local_rulebase_name']
- for nat_type in ['central/dnat', 'central/dnat6', 'firewall/central-snat-map']:
- fOS_getter.update_config_with_fortinet_api_call(
- raw_config['rules_adom_nat'], sid, fm_api_url, "/pm/config/" + scope + "/pkg/" + pkg + '/' + nat_type, device['local_rulebase_name'], limit=limit)
-
-
-def normalize_access_rules(full_config, config2import, import_id, mgm_details={}, jwt=None):
- logger = getFwoLogger()
- rules = []
- rule_number = 0
- # rule_number, first_v4, first_v6 = insert_headers(rule_table, first_v6, first_v4, full_config, rules, import_id, localPkgName,src_ref_all,dst_ref_all,rule_number)
-
- if 'rules' in full_config and 'rules' in full_config['rules']:
- for rule_orig in full_config['rules']['rules']:
- rule = {'rule_src': '', 'rule_dst': '', 'rule_svc': ''}
- rule.update({ 'control_id': import_id})
- rule.update({ 'rulebase_name': 'access_rules'}) # the rulebase_name will be set to the pkg_name as there is no rulebase_name in FortiMangaer
- rule.update({ 'rule_ruleid': rule_orig['policyid']})
- rule.update({ 'rule_uid': rule_orig['uuid']})
- rule.update({ 'rule_num': rule_number})
- if 'name' in rule_orig:
- rule.update({ 'rule_name': rule_orig['name']})
- rule.update({ 'rule_installon': mgm_details['devices'][0]['name'] })
- # rule.update({ 'rule_installon': localPkgName })
- rule.update({ 'rule_implied': False })
- rule.update({ 'rule_time': None })
- rule.update({ 'rule_type': 'access' })
- rule.update({ 'parent_rule_id': None })
-
- if 'comments' in rule_orig:
- rule.update({ 'rule_comment': rule_orig['comments']})
- else:
- rule.update({ 'rule_comment': None })
- if rule_orig['action']=='deny':
- rule.update({ 'rule_action': 'Drop' })
- else:
- rule.update({ 'rule_action': 'Accept' })
- if 'status' in rule_orig and (rule_orig['status']=='enable' or rule_orig['status']==1):
- rule.update({ 'rule_disabled': False })
- else:
- rule.update({ 'rule_disabled': True })
- if rule_orig['logtraffic'] == 'disable':
- rule.update({ 'rule_track': 'None'})
- else:
- rule.update({ 'rule_track': 'Log'})
-
- if '_last_hit' not in rule_orig or rule_orig['_last_hit'] == 0:
- rule.update({ 'last_hit': None})
- else:
- rule.update({ 'last_hit': time.strftime("%Y-%m-%d", time.localtime(rule_orig['_last_hit']))})
-
- rule['rule_src'] = list_delimiter.join([d['name'] for d in rule_orig['srcaddr']])
- rule['rule_dst'] = list_delimiter.join([d['name'] for d in rule_orig['dstaddr']])
- rule['rule_svc'] = list_delimiter.join([d['name'] for d in rule_orig['service']])
-
- # handling internet-service rules - no mixed mode between (src/dst) and internet service (src), so overwriting)
- if 'internet-service-src-name' in rule_orig and len(rule_orig['internet-service-src-name'])>0:
- rule['rule_src'] = list_delimiter.join([d['name'] for d in rule_orig['internet-service-src-name']])
- set_service_field_internet_service(rule, config2import, import_id)
- if 'internet-service-name' in rule_orig and len(rule_orig['internet-service-name'])>0:
- rule['rule_dst'] = list_delimiter.join([d['name'] for d in rule_orig['internet-service-name']])
- set_service_field_internet_service(rule, config2import, import_id)
-
- # add ipv6 addresses
- rule_src_v6 = [d['name'] for d in rule_orig['srcaddr6']]
- rule_dst_v6 = [d['name'] for d in rule_orig['dstaddr6']]
- if len(rule_src_v6)>0:
- if len(rule['rule_src'])>0:
- rule['rule_src'] = list_delimiter.join(rule['rule_src'].split(list_delimiter) + rule_src_v6)
- else:
- rule['rule_src'] = list_delimiter.join(rule_src_v6)
- if len(rule_dst_v6)>0:
- if len(rule['rule_dst'])>0:
- rule['rule_dst'] = list_delimiter.join(rule['rule_dst'].split(list_delimiter) + rule_dst_v6)
- else:
- rule['rule_dst'] = list_delimiter.join(rule_dst_v6)
-
- # add zone information
- if len(rule_orig['srcintf'])>0:
- src_obj_zone = fOS_zone.add_zone_if_missing (config2import, rule_orig['srcintf'][0]['name'], import_id)
- rule.update({ 'rule_from_zone': src_obj_zone }) # todo: currently only using the first zone
- if len(rule_orig['dstintf'])>0:
- dst_obj_zone = fOS_zone.add_zone_if_missing (config2import, rule_orig['dstintf'][0]['name'], import_id)
- rule.update({ 'rule_to_zone': dst_obj_zone }) # todo: currently only using the first zone
-
- rule.update({ 'rule_src_neg': rule_orig['srcaddr-negate']!='disable'})
- rule.update({ 'rule_dst_neg': rule_orig['dstaddr-negate']!='disable'})
- rule.update({ 'rule_svc_neg': rule_orig['service-negate']!='disable'})
-
- rule.update({ 'rule_src_refs': list_delimiter.join(resolve_objects(d, lookup_dict=full_config['nw_obj_lookup_dict'],jwt=jwt) for d in rule['rule_src'].split(list_delimiter))})
- rule.update({ 'rule_dst_refs': list_delimiter.join(resolve_objects(d, lookup_dict=full_config['nw_obj_lookup_dict'],jwt=jwt) for d in rule['rule_dst'].split(list_delimiter))})
- rule.update({ 'rule_svc_refs': rule['rule_svc']}) # for service name and uid are identical
-
- add_users_to_rule(rule_orig, rule)
-
- # xlate_rule = handle_combined_nat_rule(rule, rule_orig, config2import, nat_rule_number, import_id, localPkgName, dev_id)
- rules.append(rule)
- # if xlate_rule is not None:
- # rules.append(xlate_rule)
- rule_number += 1 # nat rules have their own numbering
- else:
- logger.warning('did not find any access rules')
-
- config2import.update({'rules': rules})
-
-
-def set_service_field_internet_service(rule, config2import, import_id):
- # check if dummy service "Internet Service" already exists and create if not
- found_internet_service_obj = next((item for item in config2import['service_objects'] if item["svc_name"] == "Internet Service"), None)
- if found_internet_service_obj is None:
- config2import['service_objects'].append({
- 'svc_name': 'Internet Service', 'svc_typ': 'group', 'svc_uid': 'Internet Service', 'control_id': import_id
- })
-
- # set service to "Internet Service"
- rule['rule_svc'] = 'Internet Service'
- rule['rule_svc_refs'] = 'Internet Service'
-
-
-# pure nat rules
-def normalize_nat_rules(full_config, config2import, import_id, jwt=None):
- nat_rules = []
- rule_number = 0
-
- for rule_table in rule_nat_scope:
- for localPkgName in full_config['rules_global_nat']:
- for rule_orig in full_config[rule_table][localPkgName]:
- rule = {'rule_src': '', 'rule_dst': '', 'rule_svc': ''}
- if rule_orig['nat'] == 1: # assuming source nat
- rule.update({ 'control_id': import_id})
- rule.update({ 'rulebase_name': localPkgName}) # the rulebase_name just has to be a unique string among devices
- rule.update({ 'rule_ruleid': rule_orig['policyid']})
- rule.update({ 'rule_uid': rule_orig['uuid']})
- # rule.update({ 'rule_num': rule_orig['obj seq']})
- rule.update({ 'rule_num': rule_number })
- if 'comments' in rule_orig:
- rule.update({ 'rule_comment': rule_orig['comments']})
- rule.update({ 'rule_action': 'Drop' }) # not used for nat rules
- rule.update({ 'rule_track': 'None'}) # not used for nat rules
-
- rule['rule_src'] = extend_string_list(rule['rule_src'], rule_orig, 'orig-addr', list_delimiter, jwt=jwt, import_id=import_id)
- rule['rule_dst'] = extend_string_list(rule['rule_dst'], rule_orig, 'dst-addr', list_delimiter, jwt=jwt, import_id=import_id)
-
- if rule_orig['protocol']==17:
- svc_name = 'udp_' + str(rule_orig['orig-port'])
- elif rule_orig['protocol']==6:
- svc_name = 'tcp_' + str(rule_orig['orig-port'])
- else:
- svc_name = 'svc_' + str(rule_orig['orig-port'])
- # need to create a helper service object and add it to the nat rule, also needs to be added to service list
-
- if not 'service_objects' in config2import: # is normally defined
- config2import['service_objects'] = []
- config2import['service_objects'].append(create_svc_object( \
- import_id=import_id, name=svc_name, proto=rule_orig['protocol'], port=rule_orig['orig-port'], comment='service created by FWO importer for NAT purposes'))
- rule['rule_svc'] = svc_name
-
- #rule['rule_src'] = extend_string_list(rule['rule_src'], rule_orig, 'srcaddr6', list_delimiter, jwt=jwt, import_id=import_id)
- #rule['rule_dst'] = extend_string_list(rule['rule_dst'], rule_orig, 'dstaddr6', list_delimiter, jwt=jwt, import_id=import_id)
-
- if len(rule_orig['srcintf'])>0:
- rule.update({ 'rule_from_zone': rule_orig['srcintf'][0] }) # todo: currently only using the first zone
- if len(rule_orig['dstintf'])>0:
- rule.update({ 'rule_to_zone': rule_orig['dstintf'][0] }) # todo: currently only using the first zone
-
- rule.update({ 'rule_src_neg': False})
- rule.update({ 'rule_dst_neg': False})
- rule.update({ 'rule_svc_neg': False})
- rule.update({ 'rule_src_refs': resolve_raw_objects(rule['rule_src'], list_delimiter, full_config, 'name', 'uuid', rule_type=rule_table) }, \
- jwt=jwt, import_id=import_id, rule_uid=rule_orig['uuid'], object_type='network object')
- rule.update({ 'rule_dst_refs': resolve_raw_objects(rule['rule_dst'], list_delimiter, full_config, 'name', 'uuid', rule_type=rule_table) }, \
- jwt=jwt, import_id=import_id, rule_uid=rule_orig['uuid'], object_type='network object')
- # services do not have uids, so using name instead
- rule.update({ 'rule_svc_refs': rule['rule_svc'] })
- rule.update({ 'rule_type': 'original' })
- rule.update({ 'rule_installon': None })
- if 'status' in rule_orig and (rule_orig['status']=='enable' or rule_orig['status']==1):
- rule.update({ 'rule_disabled': False })
- else:
- rule.update({ 'rule_disabled': True })
- rule.update({ 'rule_implied': False })
- rule.update({ 'rule_time': None })
- rule.update({ 'parent_rule_id': None })
-
- nat_rules.append(rule)
- add_users_to_rule(rule_orig, rule)
-
- ############## now adding the xlate rule part ##########################
- xlate_rule = dict(rule) # copy the original (match) rule
- xlate_rule.update({'rule_src': '', 'rule_dst': '', 'rule_svc': ''})
- xlate_rule['rule_src'] = extend_string_list(xlate_rule['rule_src'], rule_orig, 'orig-addr', list_delimiter, jwt=jwt, import_id=import_id)
- xlate_rule['rule_dst'] = 'Original'
-
- if rule_orig['protocol']==17:
- svc_name = 'udp_' + str(rule_orig['nat-port'])
- elif rule_orig['protocol']==6:
- svc_name = 'tcp_' + str(rule_orig['nat-port'])
- else:
- svc_name = 'svc_' + str(rule_orig['nat-port'])
- # need to create a helper service object and add it to the nat rule, also needs to be added to service list!
- # fmgr_service.create_svc_object(name=svc_name, proto=rule_orig['protocol'], port=rule_orig['orig-port'], comment='service created by FWO importer for NAT purposes')
- config2import['service_objects'].append(create_svc_object(import_id=import_id, name=svc_name, proto=rule_orig['protocol'], port=rule_orig['nat-port'], comment='service created by FWO importer for NAT purposes'))
- xlate_rule['rule_svc'] = svc_name
-
- xlate_rule.update({ 'rule_src_refs': resolve_objects(xlate_rule['rule_src'], list_delimiter, full_config, 'name', 'uuid', rule_type=rule_table, jwt=jwt, import_id=import_id ) })
- xlate_rule.update({ 'rule_dst_refs': resolve_objects(xlate_rule['rule_dst'], list_delimiter, full_config, 'name', 'uuid', rule_type=rule_table, jwt=jwt, import_id=import_id ) })
- xlate_rule.update({ 'rule_svc_refs': xlate_rule['rule_svc'] }) # services do not have uids, so using name instead
-
- xlate_rule.update({ 'rule_type': 'xlate' })
-
- nat_rules.append(xlate_rule)
- rule_number += 1
- config2import['rules'].extend(nat_rules)
-
-
-def insert_header(rules, import_id, header_text, rulebase_name, rule_uid, rule_number, src_refs, dst_refs):
- rule = {
- "control_id": import_id,
- "rule_head_text": header_text,
- "rulebase_name": rulebase_name,
- "rule_ruleid": None,
- "rule_uid": rule_uid + rulebase_name,
- "rule_num": rule_number,
- "rule_disabled": False,
- "rule_src": "all",
- "rule_dst": "all",
- "rule_svc": "ALL",
- "rule_src_neg": False,
- "rule_dst_neg": False,
- "rule_svc_neg": False,
- "rule_src_refs": src_refs,
- "rule_dst_refs": dst_refs,
- "rule_svc_refs": "ALL",
- "rule_action": "Accept",
- "rule_track": "None",
- "rule_installon": None,
- "rule_time": None,
- "rule_type": "access",
- "parent_rule_id": None,
- "rule_implied": False,
- "rule_comment": None
- }
- rules.append(rule)
-
-
-def create_xlate_rule(rule):
- xlate_rule = copy.deepcopy(rule)
- rule['rule_type'] = 'combined'
- xlate_rule['rule_type'] = 'xlate'
- xlate_rule['rule_comment'] = None
- xlate_rule['rule_disabled'] = False
- xlate_rule['rule_src'] = 'Original'
- xlate_rule['rule_src_refs'] = 'Original'
- xlate_rule['rule_dst'] = 'Original'
- xlate_rule['rule_dst_refs'] = 'Original'
- xlate_rule['rule_svc'] = 'Original'
- xlate_rule['rule_svc_refs'] = 'Original'
- return xlate_rule
-
-
-def handle_combined_nat_rule(rule, rule_orig, config2import, nat_rule_number, import_id, localPkgName, dev_id):
- # now dealing with VIPs (dst NAT part) of combined rules
- logger = getFwoLogger()
- xlate_rule = None
-
- # dealing with src NAT part of combined rules
- if "nat" in rule_orig and rule_orig["nat"]==1:
- logger.debug("found mixed Access/NAT rule no. " + str(nat_rule_number))
- nat_rule_number += 1
- xlate_rule = create_xlate_rule(rule)
- if 'ippool' in rule_orig:
- if rule_orig['ippool']==0: # hiding behind outbound interface
- interface_name = 'unknownIF'
- destination_interface_ip = '0.0.0.0'
- destination_ip = get_first_ip_of_destination(rule['rule_dst_refs'], config2import) # get an ip of destination
- hideInterface = 'undefined_interface'
- if destination_ip is None:
- logger.warning('src nat behind interface: found no valid destination ip in rule with UID ' + rule['rule_uid'])
- else:
- # matching_route = get_matching_route_obj(destination_ip, config2import['networking'][device_name]['routingv4'])
- matching_route = get_matching_route_obj(destination_ip, config2import['routing'], dev_id)
- if matching_route is None:
- logger.warning('src nat behind interface: found no matching route in rule with UID '
- + rule['rule_uid'] + ', dest_ip: ' + destination_ip)
- else:
- destination_interface_ip = get_ip_of_interface_obj(matching_route.interface, dev_id, config2import['interfaces'])
- interface_name = matching_route.interface
- hideInterface=interface_name
- if hideInterface is None:
- logger.warning('src nat behind interface: found route with undefined interface ' + str(jsonpickle.dumps(matching_route, unpicklable=True)))
- if destination_interface_ip is None:
- logger.warning('src nat behind interface: found no matching interface IP in rule with UID '
- + rule['rule_uid'] + ', dest_ip: ' + destination_ip)
-
- # add dummy object "outbound-interface"
- if hideInterface is not None:
- obj_name = 'hide_IF_ip_' + str(hideInterface) + '_' + str(destination_interface_ip)
- obj_comment = 'FWO auto-generated dummy object for source nat'
- if type(ipaddress.ip_address(str(destination_interface_ip))) is ipaddress.IPv6Address:
- HideNatIp = str(destination_interface_ip) + '/128'
- elif type(ipaddress.ip_address(str(destination_interface_ip))) is ipaddress.IPv4Address:
- HideNatIp = str(destination_interface_ip) + '/32'
- else:
- HideNatIp = '0.0.0.0/32'
- logger.warning('found invalid HideNatIP ' + str(destination_interface_ip))
- obj = create_network_object(import_id, obj_name, 'host', HideNatIp, obj_name, 'black', obj_comment, 'global')
- if obj not in config2import['network_objects']:
- config2import['network_objects'].append(obj)
- xlate_rule['rule_src'] = obj_name
- xlate_rule['rule_src_refs'] = obj_name
-
- elif rule_orig['ippool']==1: # hiding behind one ip of an ip pool
- poolNameArray = rule_orig['poolname']
- if len(poolNameArray)>0:
- if len(poolNameArray)>1:
- logger.warning("found more than one ippool - ignoring all but first pool")
- poolName = poolNameArray[0]
- xlate_rule['rule_src'] = poolName
- xlate_rule['rule_src_refs'] = poolName
- else:
- logger.warning("found ippool rule without ippool: " + rule['rule_uid'])
- else:
- logger.warning("found ippool rule with unexpected ippool value: " + rule_orig['ippool'])
-
- if 'natip' in rule_orig and rule_orig['natip']!=["0.0.0.0","0.0.0.0"]:
- logger.warning("found explicit natip rule - ignoring for now: " + rule['rule_uid'])
- # need example for interpretation of config
-
- # todo: find out how match-vip=1 influences natting (only set in a few vip-nat rules)
- # if "match-vip" in rule_orig and rule_orig["match-vip"]==1:
- # logger.warning("found VIP destination Access/NAT rule (but not parsing yet); no. " + str(vip_nat_rule_number))
- # vip_nat_rule_number += 1
-
- # deal with vip natting: check for each (dst) nw obj if it contains "obj_nat_ip"
- rule_dst_list = rule['rule_dst'].split(list_delimiter)
- nat_object_list = extract_nat_objects(rule_dst_list, config2import['network_objects'])
-
- if len(nat_object_list)>0:
- if xlate_rule is None: # no source nat, so we create the necessary nat rule here
- xlate_rule = create_xlate_rule(rule)
- xlate_dst = []
- xlate_dst_refs = []
- for nat_obj in nat_object_list:
- if 'obj_ip_end' in nat_obj: # this nat obj is a range - include the end ip in name and uid as well to avoid akey conflicts
- xlate_dst.append(nat_obj['obj_nat_ip'] + '-' + nat_obj['obj_ip_end'] + nat_postfix)
- nat_ref = nat_obj['obj_nat_ip']
- if 'obj_nat_ip_end' in nat_obj:
- nat_ref += '-' + nat_obj['obj_nat_ip_end'] + nat_postfix
- xlate_dst_refs.append(nat_ref)
- else:
- xlate_dst.append(nat_obj['obj_nat_ip'] + nat_postfix)
- xlate_dst_refs.append(nat_obj['obj_nat_ip'] + nat_postfix)
- xlate_rule['rule_dst'] = list_delimiter.join(xlate_dst)
- xlate_rule['rule_dst_refs'] = list_delimiter.join(xlate_dst_refs)
- # else: (no nat object found) no dnatting involved, dst stays "Original"
-
- return xlate_rule
-
-
-def insert_headers(rule_table, first_v6, first_v4, full_config, rules, import_id, localPkgName,src_ref_all,dst_ref_all,rule_number):
- if rule_table in rule_access_scope_v6 and first_v6:
- insert_header(rules, import_id, "IPv6 rules", localPkgName, "IPv6HeaderText", rule_number, src_ref_all, dst_ref_all)
- rule_number += 1
- first_v6 = False
- elif rule_table in rule_access_scope_v4 and first_v4:
- insert_header(rules, import_id, "IPv4 rules", localPkgName, "IPv4HeaderText", rule_number, src_ref_all, dst_ref_all)
- rule_number += 1
- first_v4 = False
- if rule_table == 'rules_adom_v4' and len(full_config['rules_adom_v4'][localPkgName])>0:
- insert_header(rules, import_id, "Adom Rules IPv4", localPkgName, "IPv4AdomRules", rule_number, src_ref_all, dst_ref_all)
- rule_number += 1
- elif rule_table == 'rules_adom_v6' and len(full_config['rules_adom_v6'][localPkgName])>0:
- insert_header(rules, import_id, "Adom Rules IPv6", localPkgName, "IPv6AdomRules", rule_number, src_ref_all, dst_ref_all)
- rule_number += 1
- elif rule_table == 'rules_global_header_v4' and len(full_config['rules_global_header_v4'][localPkgName])>0:
- insert_header(rules, import_id, "Global Header Rules IPv4", localPkgName, "IPv4GlobalHeaderRules", rule_number, src_ref_all, dst_ref_all)
- rule_number += 1
- elif rule_table == 'rules_global_header_v6' and len(full_config['rules_global_header_v6'][localPkgName])>0:
- insert_header(rules, import_id, "Global Header Rules IPv6", localPkgName, "IPv6GlobalHeaderRules", rule_number, src_ref_all, dst_ref_all)
- rule_number += 1
- elif rule_table == 'rules_global_footer_v4' and len(full_config['rules_global_footer_v4'][localPkgName])>0:
- insert_header(rules, import_id, "Global Footer Rules IPv4", localPkgName, "IPv4GlobalFooterRules", rule_number, src_ref_all, dst_ref_all)
- rule_number += 1
- elif rule_table == 'rules_global_footer_v6' and len(full_config['rules_global_footer_v6'][localPkgName])>0:
- insert_header(rules, import_id, "Global Footer Rules IPv6", localPkgName, "IPv6GlobalFooterRules", rule_number, src_ref_all, dst_ref_all)
- rule_number += 1
- return rule_number, first_v4, first_v6
-
-
-def extract_nat_objects(nwobj_list, all_nwobjects):
- nat_obj_list = []
- for obj in nwobj_list:
- for obj2 in all_nwobjects:
- if obj2['obj_name']==obj:
- if 'obj_nat_ip' in obj2:
- nat_obj_list.append(obj2)
- break
- # if obj in all_nwobjects and 'obj_nat_ip' in all_nwobjects[obj]:
- # nat_obj_list.append(obj)
- return nat_obj_list
-
-
-def add_users_to_rule(rule_orig, rule):
- if 'groups' in rule_orig:
- add_users(rule_orig['groups'], rule)
- if 'users' in rule_orig:
- add_users(rule_orig['users'], rule)
-
-
-def add_users(users, rule):
- for user in users:
- rule_src_with_users = []
- for src in rule['rule_src'].split(list_delimiter):
- rule_src_with_users.append(user + '@' + src)
- rule['rule_src'] = list_delimiter.join(rule_src_with_users)
-
- # here user ref is the user name itself
- rule_src_refs_with_users = []
- for src in rule['rule_src_refs'].split(list_delimiter):
- rule_src_refs_with_users.append(user + '@' + src)
- rule['rule_src_refs'] = list_delimiter.join(rule_src_refs_with_users)
diff --git a/roles/importer/files/importer/fortiosmanagementREST/fOS_service.py b/roles/importer/files/importer/fortiosmanagementREST/fOS_service.py
deleted file mode 100644
index b827e5e0a7..0000000000
--- a/roles/importer/files/importer/fortiosmanagementREST/fOS_service.py
+++ /dev/null
@@ -1,212 +0,0 @@
-import re
-from fwo_const import list_delimiter
-from fwo_log import getFwoLogger
-
-
-def normalize_svcobjects(full_config, config2import, import_id, scope):
- logger = getFwoLogger()
- svc_objects = []
- full_config['svc_obj_lookup_dict'] = {}
- for s in scope:
- if s in full_config:
- for obj_orig in full_config[s]:
- member_names = ''
- if 'member' in obj_orig:
- type = 'group'
- for member in obj_orig['member']:
- member_names += member['name'] + list_delimiter
- member_names = member_names[:-1]
- else:
- type = 'simple'
-
- name = None
- if 'name' in obj_orig:
- name = str(obj_orig['name'])
-
- color = None
- if 'color' in obj_orig and str(obj_orig['color']) != 0:
- color = str(obj_orig['color'])
-
- session_timeout = None # todo: find the right timer
- # if 'udp-idle-timer' in obj_orig and str(obj_orig['udp-idle-timer']) != 0:
- # session_timeout = str(obj_orig['udp-idle-timer'])
-
- proto = 0
- range_names = ''
- if 'protocol' in obj_orig:
- added_svc_obj = 0
- # if obj_orig['protocol'] == 1:
- # addObject(svc_objects, type, name, color, 1, None, None, session_timeout, import_id, full_config=full_config)
- # added_svc_obj += 1
- # if obj_orig['protocol'] == 2:
- # if 'protocol-number' in obj_orig:
- # proto = obj_orig['protocol-number']
- # addObject(svc_objects, type, name, color, proto, None, None, session_timeout, import_id)
- # added_svc_obj += 1
- # if obj_orig['protocol'] == 5 or obj_orig['protocol'] == 11 or obj_orig['protocol'] == 'TCP/UDP/SCTP':
- if obj_orig['protocol'] == 'TCP/UDP/SCTP':
- split = check_split(obj_orig)
- if "tcp-portrange" in obj_orig and len(obj_orig['tcp-portrange']) > 0:
- tcpname = name
- if split:
- tcpname += "_tcp"
- range_names += tcpname + list_delimiter
- addObject(svc_objects, type, tcpname, color, 6, obj_orig['tcp-portrange'], None, session_timeout, import_id, full_config=full_config)
- added_svc_obj += 1
- if "udp-portrange" in obj_orig and len(obj_orig['udp-portrange']) > 0:
- udpname = name
- if split:
- udpname += "_udp"
- range_names += udpname + list_delimiter
- addObject(svc_objects, type, udpname, color, 17, obj_orig['udp-portrange'], None, session_timeout, import_id, full_config=full_config)
- added_svc_obj += 1
- if "sctp-portrange" in obj_orig and len(obj_orig['sctp-portrange']) > 0:
- sctpname = name
- if split:
- sctpname += "_sctp"
- range_names += sctpname + list_delimiter
- addObject(svc_objects, type, sctpname, color, 132, obj_orig['sctp-portrange'], None, session_timeout, import_id, full_config=full_config)
- added_svc_obj += 1
- if split:
- range_names = range_names[:-1]
- # TODO: collect group members
- addObject(svc_objects, 'group', name, color, 0, None, range_names, session_timeout, import_id, full_config=full_config)
- added_svc_obj += 1
- if added_svc_obj==0: # assuming RPC service which here has no properties at all
- addObject(svc_objects, 'rpc', name, color, 0, None, None, None, import_id, full_config=full_config)
- added_svc_obj += 1
- elif obj_orig['protocol'] == 'IP':
- addObject(svc_objects, 'simple', name, color, obj_orig['protocol-number'], None, None, None, import_id, full_config=full_config)
- added_svc_obj += 1
- elif obj_orig['protocol'] == 'ICMP':
- addObject(svc_objects, 'simple', name, color, 1, None, None, None, import_id, full_config=full_config)
- added_svc_obj += 1
- elif obj_orig['protocol'] == 'ICMP6':
- addObject(svc_objects, 'simple', name, color, 1, None, None, None, import_id, full_config=full_config)
- added_svc_obj += 1
- else:
- logger.warning("Unknown service protocol found: " + obj_orig['name'] +', proto: ' + obj_orig['protocol'])
- elif type == 'group':
- addObject(svc_objects, type, name, color, 0, None, member_names, session_timeout, import_id, full_config=full_config)
- else:
- # application/list
- addObject(svc_objects, type, name, color, 0, None, None, session_timeout, import_id, full_config=full_config)
-
- # finally add "Original" service object for natting
- original_obj_name = 'Original'
- svc_objects.append(create_svc_object(import_id=import_id, name=original_obj_name, proto=0, port=None,\
- comment='"original" service object created by FWO importer for NAT purposes'))
-
- config2import.update({'service_objects': svc_objects})
-
-
-def check_split(obj_orig):
- count = 0
- if "tcp-portrange" in obj_orig and len(obj_orig['tcp-portrange']) > 0:
- count += 1
- if "udp-portrange" in obj_orig and len(obj_orig['udp-portrange']) > 0:
- count += 1
- if "sctp-portrange" in obj_orig and len(obj_orig['sctp-portrange']) > 0:
- count += 1
- return (count > 1)
-
-
-def extractSinglePortRange(port_range):
- # remove src-ports
- port = port_range.split(':')[0]
- port_end = port
-
- # open ranges (not found so far in data)
- pattern = re.compile('^\>(\d+)$')
- match = pattern.match(port)
- if match:
- port = str(int(match.group()[1:]) + 1)
- port_end = str(65535)
- pattern = re.compile('^\<(\d+)$')
- match = pattern.match(port)
- if match:
- port = str(1)
- port_end = str(int(match.group()[1:]) - 1)
-
- # split ranges
- pattern = re.compile('^(\d+)\-(\d+)$')
- match = pattern.match(port)
- if match:
- port, port_end = match.group().split('-')
- return port, port_end
-
-
-def extractPorts(port_ranges):
- ports = []
- port_ends = []
- if port_ranges is not None and len(port_ranges) > 0:
- if ' ' in port_ranges:
- # port range of the form "12 13 114"
- port_ranges = port_ranges.split(' ')
-
- if not isinstance(port_ranges, str):
- for port_range in port_ranges:
- port1, port2 = extractSinglePortRange(port_range)
- ports.append(port1)
- port_ends.append(port2)
- else:
- port1, port2 = extractSinglePortRange(port_ranges)
- ports.append(port1)
- port_ends.append(port2)
- return ports, port_ends
-
-
-def create_svc_object(import_id, name, proto, port, comment):
- return {
- 'control_id': import_id,
- 'svc_name': name,
- 'svc_typ': 'simple',
- 'svc_port': port,
- 'ip_proto': proto,
- 'svc_uid': name, # services have no uid in fortimanager
- 'svc_comment': comment
- }
-
-
-def addObject(svc_objects, type, name, color, proto, port_ranges, member_names, session_timeout, import_id, full_config={}):
-
- # add service object in lookup table (currently no UID, name is the UID)
- full_config['svc_obj_lookup_dict'][name] = name
-
- svc_obj = create_svc_object(import_id, name, proto, None, None)
- svc_obj['svc_color'] = color
- svc_obj['svc_typ'] = type
- svc_obj['svc_port_end'] = None
- svc_obj['svc_member_names'] = member_names
- svc_obj['svc_member_refs'] = member_names
- svc_obj['svc_timeout'] = session_timeout
-
- if port_ranges is not None:
- range_names = ''
- ports, port_ends = extractPorts(port_ranges)
- split = (len(ports) > 1)
- for index, port in enumerate(ports):
- port_end = port_ends[index]
- full_name = name
- if split:
- full_name += '_' + str(port)
- range_names += full_name + list_delimiter
- if port_end != port:
- port_range_local = port + '-' + port_end
- else:
- port_range_local = port
- addObject(svc_objects, 'simple', full_name, color, proto, port_range_local, None, None, import_id, full_config)
-
- svc_obj['svc_port'] = port
- svc_obj['svc_port_end'] = port_end
-
- if split:
- range_names = range_names[:-1]
- svc_obj['svc_member_refs'] = range_names
- svc_obj['svc_member_names'] = range_names
- svc_obj['svc_typ'] = 'group'
- svc_obj['svc_port'] = None
- svc_obj['svc_port_end'] = None
-
- svc_objects.extend([svc_obj])
-
diff --git a/roles/importer/files/importer/fortiosmanagementREST/fOS_user.py b/roles/importer/files/importer/fortiosmanagementREST/fOS_user.py
deleted file mode 100644
index 9d5b2d8281..0000000000
--- a/roles/importer/files/importer/fortiosmanagementREST/fOS_user.py
+++ /dev/null
@@ -1,37 +0,0 @@
-from fwo_const import list_delimiter
-
-def normalize_users(full_config, config2import, import_id, user_scope):
- users = []
- for scope in user_scope:
- if scope in full_config:
- for user_orig in full_config[scope]:
- name = None
- type = 'simple'
- color = None
- member_names = None
- comment = None
-
- if 'member' in user_orig:
- type = 'group'
- member_names = ''
- for member in user_orig['member']:
- member_names += member['name'] + list_delimiter
- member_names = member_names[:-1]
- if 'name' in user_orig:
- name = str(user_orig['name'])
- if 'comment' in user_orig:
- comment = str(user_orig['comment'])
- if 'color' in user_orig and str(user_orig['color']) != 0:
- color = str(user_orig['color'])
-
- users.extend([{'user_typ': type,
- 'user_name': name,
- 'user_color': color,
- 'user_uid': name,
- 'user_comment': comment,
- 'user_member_refs': member_names,
- 'user_member_names': member_names,
- 'control_id': import_id
- }])
-
- config2import.update({'user_objects': users})
diff --git a/roles/importer/files/importer/fortiosmanagementREST/fOS_zone.py b/roles/importer/files/importer/fortiosmanagementREST/fOS_zone.py
deleted file mode 100644
index b9e41a1e68..0000000000
--- a/roles/importer/files/importer/fortiosmanagementREST/fOS_zone.py
+++ /dev/null
@@ -1,29 +0,0 @@
-
-def normalize_zones(full_config, config2import, import_id):
- zones = []
- for orig_zone in full_config['zone_objects']['zone_list']:
- zone = {}
- zone.update({'zone_name': orig_zone})
- zone.update({'control_id': import_id})
- zones.append(zone)
-
- config2import.update({'zone_objects': zones})
-
-
-def add_zone_if_missing (config2import, zone_string, import_id):
- # adding zone if it not yet exists
-
- # also transforming any into global (normalized global zone)
- if zone_string == 'any':
- zone_string = 'global'
- if zone_string is not None:
- if 'zone_objects' not in config2import: # no zones yet? add empty zone_objects array
- config2import.update({'zone_objects': []})
- zone_exists = False
- for zone in config2import['zone_objects']:
- if zone_string == zone['zone_name']:
- zone_exists = True
- if not zone_exists:
- config2import['zone_objects'].append({'zone_name': zone_string, 'control_id': import_id})
- return zone_string
-
\ No newline at end of file
diff --git a/roles/importer/files/importer/fortiosmanagementREST/fwcommon.py b/roles/importer/files/importer/fortiosmanagementREST/fwcommon.py
deleted file mode 100644
index 2dc583714f..0000000000
--- a/roles/importer/files/importer/fortiosmanagementREST/fwcommon.py
+++ /dev/null
@@ -1,115 +0,0 @@
-import sys
-import json
-from common import importer_base_dir
-sys.path.append(importer_base_dir + '/fortiosmanagementREST')
-import fOS_user
-import fOS_service
-import fOS_zone
-import fOS_rule
-import fOS_network
-import fOS_getter
-from curses import raw
-from fwo_log import getFwoLogger
-# from fOS_gw_networking import getInterfacesAndRouting, normalize_network_data
-from fwo_data_networking import get_ip_of_interface_obj
-
-from fwo_const import list_delimiter, nat_postfix, fwo_config_filename
-from fwo_config import readConfig
-from fwo_api import setAlert, create_data_issue
-
-
-nw_obj_types = ['firewall/address', 'firewall/address6', 'firewall/addrgrp',
- 'firewall/addrgrp6', 'firewall/ippool', 'firewall/vip',
- 'firewall/internet-service', 'firewall/internet-service-group']
- # internet-service is not a service as such but is used as dest (mainly)
-svc_obj_types = ['application/list', 'application/group',
- # 'application/categories',
- #'application/custom',
- 'firewall.service/custom',
- 'firewall.service/group'
- ]
-
-# build the product of all scope/type combinations
-nw_obj_scope = ['nw_obj_' + s1 for s1 in nw_obj_types]
-svc_obj_scope = ['svc_obj_' + s1 for s1 in svc_obj_types]
-
-# zone_types = ['zones_global', 'zones_adom']
-
-user_obj_types = ['user/local', 'user/group']
-user_scope = ['user_obj_' + s1 for s1 in user_obj_types]
-
-
-def has_config_changed(full_config, mgm_details, force=False):
- # dummy - may be filled with real check later on
- return True
-
-
-def get_config(config2import, full_config, current_import_id, mgm_details, limit=100, force=False, jwt=''):
- logger = getFwoLogger()
- if full_config == {}: # no native config was passed in, so getting it from FortiManager
- parsing_config_only = False
- else:
- parsing_config_only = True
-
- # fmgr API login
- if not parsing_config_only: # no native config was passed in, so getting it from FortiManager
- fm_api_url = 'https://' + mgm_details['hostname'] + ':' + str(mgm_details['port']) + '/api/v2'
- sid = mgm_details['import_credential']['secret']
-
- if not parsing_config_only: # no native config was passed in, so getting it from FortiManager
- getObjects(sid, fm_api_url, full_config, limit, nw_obj_types, svc_obj_types)
- # getInterfacesAndRouting(
- # sid, fm_api_url, full_config, mgm_details['devices'], limit)
-
- # adding global zone first:
- fOS_zone.add_zone_if_missing (config2import, 'global', current_import_id)
-
- # initialize all rule dicts
- fOS_rule.initializeRulebases(full_config)
- for dev in mgm_details['devices']:
- fOS_rule.getAccessPolicy(sid, fm_api_url, full_config, limit)
- # fOS_rule.getNatPolicy(sid, fm_api_url, full_config, limit)
-
- # now we normalize relevant parts of the raw config and write the results to config2import dict
- # currently reading zone from objects for backward compat with FortiManager 6.x
- # fmgr_zone.normalize_zones(full_config, config2import, current_import_id)
-
- # write normalized networking data to config2import
- # this is currently not written to the database but only used for natting decisions
- # later we will probably store the networking info in the database as well as a basis
- # for path analysis
-
- # normalize_network_data(full_config, config2import, mgm_details)
-
- fOS_user.normalize_users(
- full_config, config2import, current_import_id, user_scope)
- fOS_network.normalize_nwobjects(
- full_config, config2import, current_import_id, nw_obj_scope, jwt=jwt, mgm_id=mgm_details['id'])
- fOS_service.normalize_svcobjects(
- full_config, config2import, current_import_id, svc_obj_scope)
- fOS_zone.add_zone_if_missing (config2import, 'global', current_import_id)
-
- fOS_rule.normalize_access_rules(
- full_config, config2import, current_import_id, mgm_details=mgm_details, jwt=jwt)
- # fOS_rule.normalize_nat_rules(
- # full_config, config2import, current_import_id, jwt=jwt)
- # fOS_network.remove_nat_ip_entries(config2import)
- return 0
-
-
-def getObjects(sid, fm_api_url, raw_config, limit, nw_obj_types, svc_obj_types):
- # get network objects:
- for object_type in nw_obj_types:
- fOS_getter.update_config_with_fortiOS_api_call(
- raw_config, fm_api_url + "/cmdb/" + object_type + "?access_token=" + sid, "nw_obj_" + object_type, limit=limit)
-
- # get service objects:
- for object_type in svc_obj_types:
- fOS_getter.update_config_with_fortiOS_api_call(
- raw_config, fm_api_url + "/cmdb/" + object_type + "?access_token=" + sid, "svc_obj_" + object_type, limit=limit)
-
- # get user objects:
- for object_type in user_obj_types:
- fOS_getter.update_config_with_fortiOS_api_call(
- raw_config, fm_api_url + "/cmdb/" + object_type + "?access_token=" + sid, "user_obj_" + object_type, limit=limit)
-
diff --git a/roles/importer/files/importer/fortiosmanagementREST/unused_fOS_gw_networking.py b/roles/importer/files/importer/fortiosmanagementREST/unused_fOS_gw_networking.py
deleted file mode 100644
index 2bb2126b3b..0000000000
--- a/roles/importer/files/importer/fortiosmanagementREST/unused_fOS_gw_networking.py
+++ /dev/null
@@ -1,276 +0,0 @@
-from asyncio.log import logger
-from fwo_log import getFwoLogger
-from netaddr import IPAddress, IPNetwork
-from functools import cmp_to_key
-import traceback
-import fOS_getter as fOS_getter
-import fwo_globals
-from fwo_data_networking import Route, Interface
-from fwo_data_networking import getRouteDestination
-
-def normalize_network_data(native_config, normalized_config, mgm_details):
-
- logger = getFwoLogger()
-
- normalized_config.update({'routing': {}, 'interfaces': {} })
-
- for dev_id, plain_dev_name, plain_vdom_name, full_vdom_name in get_all_dev_names(mgm_details['devices']):
- normalized_config.update({'routing': [], 'interfaces': []})
-
- if 'routing-table-ipv4/' + full_vdom_name not in native_config:
- logger.warning('could not find routing data routing-table-ipv4/' + full_vdom_name)
- logger.warning('native configs contains the following keys ' + str(native_config.keys()))
- normalized_config['networking'][full_vdom_name]['routingv4'] = []
- else:
- for route in native_config['routing-table-ipv4/' + full_vdom_name]:
- #gateway = None if route['gateway']=='0.0.0.0' else route['gateway'] # local network
- normRoute = Route(dev_id, route['gateway'], route['ip_mask'], interface=route['interface'], metric=route['metric'], distance=route['distance'])
- normalized_config['routing'].append(normRoute)
-
- if 'routing-table-ipv6/' + full_vdom_name not in native_config:
- logger.warning('could not find routing data routing-table-ipv6/' + full_vdom_name)
- if fwo_globals.debug_level>5:
- logger.warning('native configs contains the following keys ' + str(native_config.keys()))
- normalized_config['networking'][full_vdom_name]['routingv6'] = []
- else:
- for route in native_config['routing-table-ipv6/' + full_vdom_name]:
- #gateway = None if route['gateway']=='::' else route['gateway'] # local network
- normRoute = Route(dev_id, route['gateway'], route['ip_mask'], metric=route['metric'],
- distance=route['distance'], interface=route['interface'], ip_version=6)
- normalized_config['routing'].append(normRoute)
-
- normalized_config['routing'].sort(key=getRouteDestination,reverse=True)
-
- for interface in native_config['interfaces_per_device/' + full_vdom_name]:
- if interface['ipv6']['ip6-address']!='::/0':
- ipv6, netmask_bits = interface['ipv6']['ip6-address'].split('/')
- normIfV6 = Interface(dev_id, interface['name'], IPAddress(ipv6), netmask_bits, ip_version=6)
- normalized_config['interfaces'].append(normIfV6)
-
- if interface['ip']!=['0.0.0.0','0.0.0.0']:
- ipv4 = IPAddress(interface['ip'][0])
- netmask_bits = IPAddress(interface['ip'][1]).netmask_bits()
- normIfV4 = Interface(dev_id, interface['name'], ipv4, netmask_bits, ip_version=4)
- normalized_config['interfaces'].append(normIfV4)
-
- #devices_without_default_route = get_devices_without_default_route(normalized_config)
- #if len(devices_without_default_route)>0:
- # logger.warning('found devices without default route')
-
-
-def get_matching_route(destination_ip, routing_table):
-
- logger = getFwoLogger()
-
- def route_matches(ip, destination):
- ip_n = IPNetwork(ip).cidr
- dest_n = IPNetwork(destination).cidr
- return ip_n in dest_n or dest_n in ip_n
-
-
- if len(routing_table)==0:
- logger.error('src nat behind interface: encountered empty routing table')
- return None
-
- for route in routing_table:
- if route_matches(destination_ip, route['destination']):
- return route
-
- logger.warning('src nat behind interface: found no matching route in routing table - no default route?!')
- return None
-
-
-def get_ip_of_interface(interface, interface_list=[]):
-
- interface_details = next((sub for sub in interface_list if sub['name'] == interface), None)
-
- if interface_details is not None and 'ipv4' in interface_details:
- return interface_details['ipv4']
- else:
- return None
-
-
-def sort_reverse(ar_in, key):
-
- def comp(left, right):
- l_submask = int(left[key].split("/")[1])
- r_submask = int(right[key].split("/")[1])
- return l_submask - r_submask
-
- return sorted(ar_in, key=cmp_to_key(comp), reverse=True)
-
-
-# strip off last part of a string separated by separator
-def strip_off_last_part(string_in, separator='_'):
- string_out = string_in
- if separator in string_in: # strip off final _xxx part
- str_ar = string_in.split(separator)
- str_ar.pop()
- string_out = separator.join(str_ar)
- return string_out
-
-
-def get_last_part(string_in, separator='_'):
- string_out = ''
- if separator in string_in: # strip off _vdom_name
- str_ar = string_in.split(separator)
- string_out = str_ar.pop()
- return string_out
-
-
-def get_plain_device_names_without_vdoms(devices):
- device_array = []
- for dev in devices:
- dev_name = strip_off_last_part(dev["name"])
- if dev_name not in device_array:
- device_array.append(dev_name)
- return device_array
-
-
-# only getting one vdom as currently assuming routing to be
-# the same for all vdoms on a device
-def get_device_names_plus_one_vdom(devices):
- device_array = []
- device_array_with_vdom = []
- for dev in devices:
- dev_name = strip_off_last_part(dev["name"])
- vdom_name = get_last_part(dev["name"])
- if dev_name not in device_array:
- device_array.append(dev_name)
- device_array_with_vdom.append([dev_name, vdom_name])
- return device_array_with_vdom
-
-
-# getting devices and their vdom names
-def get_device_plus_full_vdom_names(devices):
- device_array_with_vdom = []
- for dev in devices:
- dev_name = strip_off_last_part(dev["name"])
- vdom_name = dev["name"]
- device_array_with_vdom.append([dev_name, vdom_name])
- return device_array_with_vdom
-
-
-# getting devices and their vdom names
-def get_all_dev_names(devices):
- device_array_with_vdom = []
- for dev in devices:
- dev_id = dev["id"]
- dev_name = strip_off_last_part(dev["name"])
- plain_vdom_name = get_last_part(dev["name"])
- full_vdom_name = dev["name"]
- device_array_with_vdom.append([dev_id, dev_name, plain_vdom_name, full_vdom_name])
- return device_array_with_vdom
-
-
-# get network information (currently only used for source nat)
-def getInterfacesAndRouting(sid, fm_api_url, raw_config, adom_name, devices, limit):
-
- logger = getFwoLogger()
- # strip off vdom names, just deal with the plain device
- device_array = get_all_dev_names(devices)
-
- for dev_id, plain_dev_name, plain_vdom_name, full_vdom_name in device_array:
- logger.info("dev_name: " + plain_dev_name + ", full vdom_name: " + full_vdom_name)
-
- # getting interfaces of device
- all_interfaces_payload = {
- "id": 1,
- "params": [
- {
- "fields": [ "name", "ip" ],
- "filter": [ "vdom", "==", plain_vdom_name ],
- "sub fetch": {
- "client-options": {
- "subfetch hidden": 1
- },
- "dhcp-snooping-server-list": {
- "subfetch hidden": 1
- },
- "egress-queues": {
- "subfetch hidden": 1
- },
- "ipv6": {
- "fields": [
- "ip6-address"
- ],
- "sub fetch": {
- "dhcp6-iapd-list": {
- "subfetch hidden": 1
- },
- "ip6-delegated-prefix-list": {
- "subfetch hidden": 1
- },
- "ip6-extra-addr": {
- "subfetch hidden": 1
- },
- "ip6-prefix-list": {
- "subfetch hidden": 1
- },
- "vrrp6": {
- "subfetch hidden": 1
- }
- }
- },
- "l2tp-client-settings": {
- "subfetch hidden": 1
- },
- "secondaryip": {
- "subfetch hidden": 1
- },
- "tagging": {
- "subfetch hidden": 1
- },
- "vrrp": {
- "subfetch hidden": 1
- },
- "wifi-networks": {
- "subfetch hidden": 1
- }
- }
- }
- ]
- }
- try: # get interfaces from top level device (not vdom)
- fOS_getter.update_config_with_fortinet_api_call(
- raw_config, sid, fm_api_url, "/pm/config/device/" + plain_dev_name + "/global/system/interface",
- "interfaces_per_device/" + full_vdom_name, payload=all_interfaces_payload, limit=limit, method="get")
- except:
- logger.warning("error while getting interfaces of device " + plain_vdom_name + ", vdom=" + plain_vdom_name + ", ignoring, traceback: " + str(traceback.format_exc()))
-
- # now getting routing information
- for ip_version in ["ipv4", "ipv6"]:
- payload = { "params": [ { "data": {
- "target": ["adom/" + adom_name + "/device/" + plain_dev_name],
- "action": "get",
- "resource": "/api/v2/monitor/router/" + ip_version + "/select?&vdom="+ plain_vdom_name } } ] }
- try: # get routing table per vdom
- routing_helper = {}
- routing_table = []
- fOS_getter.update_config_with_fortinet_api_call(
- routing_helper, sid, fm_api_url, "/sys/proxy/json",
- "routing-table-" + ip_version + '/' + full_vdom_name,
- payload=payload, limit=limit, method="exec")
-
- if "routing-table-" + ip_version + '/' + full_vdom_name in routing_helper:
- routing_helper = routing_helper["routing-table-" + ip_version + '/' + full_vdom_name]
- if len(routing_helper)>0 and 'response' in routing_helper[0] and 'results' in routing_helper[0]['response']:
- routing_table = routing_helper[0]['response']['results']
- else:
- logger.warning("got empty " + ip_version + " routing table from device " + full_vdom_name + ", ignoring")
- routing_table = []
- except:
- logger.warning("could not get routing table for device " + full_vdom_name + ", ignoring") # exception " + str(traceback.format_exc()))
- routing_table = []
-
- # now storing the routing table:
- raw_config.update({"routing-table-" + ip_version + '/' + full_vdom_name: routing_table})
-
-
-def get_device_from_package(package_name, mgm_details):
- logger = getFwoLogger()
- for dev in mgm_details['devices']:
- if dev['local_rulebase_name'] == package_name:
- return dev['id']
- logger.debug('get_device_from_package - could not find device for package "' + package_name + '"')
- return None
diff --git a/roles/importer/files/importer/azure2022ff/__init__.py b/roles/importer/files/importer/fw_modules/azure2022ff/__init__.py
similarity index 100%
rename from roles/importer/files/importer/azure2022ff/__init__.py
rename to roles/importer/files/importer/fw_modules/azure2022ff/__init__.py
diff --git a/roles/importer/files/importer/fw_modules/azure2022ff/fwcommon.py b/roles/importer/files/importer/fw_modules/azure2022ff/fwcommon.py
new file mode 100644
index 0000000000..65b4174a5f
--- /dev/null
+++ b/roles/importer/files/importer/fw_modules/azure2022ff/fwcommon.py
@@ -0,0 +1,10 @@
+from model_controllers.fwconfigmanagerlist_controller import FwConfigManagerListController
+from model_controllers.import_state_controller import ImportStateController
+from models.fw_common import FwCommon
+
+
+class Azure2022ffCommon(FwCommon):
+ def get_config(
+ self, config_in: FwConfigManagerListController, import_state: ImportStateController
+ ) -> tuple[int, FwConfigManagerListController]:
+ raise NotImplementedError("Azure 2022 ff is not supported yet in the new python importer.")
diff --git a/roles/importer/files/importer/checkpointR8x/__init__.py b/roles/importer/files/importer/fw_modules/checkpointR8x/__init__.py
similarity index 100%
rename from roles/importer/files/importer/checkpointR8x/__init__.py
rename to roles/importer/files/importer/fw_modules/checkpointR8x/__init__.py
diff --git a/roles/importer/files/importer/fw_modules/checkpointR8x/cp_const.py b/roles/importer/files/importer/fw_modules/checkpointR8x/cp_const.py
new file mode 100644
index 0000000000..7eb27f3018
--- /dev/null
+++ b/roles/importer/files/importer/fw_modules/checkpointR8x/cp_const.py
@@ -0,0 +1,101 @@
+details_level = "standard"
+details_level_objects = "standard"
+details_level_group_objects = "full"
+use_object_dictionary = True
+with_hits = True
+
+dummy_ip = "0.0.0.0/32"
+
+# the following is the static across all installations unique any obj uid
+# cannot fetch the Any object via API (<=1.7) at the moment
+# therefore we have a workaround adding the object manually (as svc and nw)
+any_obj_uid = "97aeb369-9aea-11d5-bd16-0090272ccb30"
+none_obj_uid = "97aeb36a-9aea-11d5-bd16-0090272ccb30"
+internet_obj_uid = "f99b1488-7510-11e2-8668-87656188709b"
+# TODO: read this from config (from API 1.6 on it is fetched)
+
+original_obj_uid = "85c0f50f-6d8a-4528-88ab-5fb11d8fe16c"
+# used for nat only (both svc and nw obj)
+
+local_nw_obj_table_names = [
+ "hosts",
+ "networks",
+ "groups",
+ "address-ranges",
+ "multicast-address-ranges",
+ "groups-with-exclusion",
+ "gateways-and-servers",
+ "simple-gateways",
+ "dns-domains",
+ "interoperable-devices",
+ "security-zones",
+ "access-roles",
+ "CpmiVoipSipDomain",
+ "CpmiVoipMgcpDomain",
+ "gsn_handover_group",
+]
+
+# the global objects need to be fetched only once per super manager
+global_nw_obj_table_names = ["updatable-objects", "dynamic-objects"]
+
+nw_obj_table_names = local_nw_obj_table_names + global_nw_obj_table_names
+
+# simple as in: no groups
+simple_svc_obj_types = [
+ "services-tcp",
+ "services-udp",
+ "services-dce-rpc",
+ "services-rpc",
+ "services-other",
+ "services-icmp",
+ "services-icmp6",
+ "services-sctp",
+ "services-gtp",
+]
+
+local_group_svc_obj_types = ["service-groups"]
+global_group_svc_obj_types = ["application-site-categories", "application-sites"]
+group_svc_obj_types = local_group_svc_obj_types + global_group_svc_obj_types
+
+local_svc_obj_table_names = local_group_svc_obj_types + simple_svc_obj_types # + [ 'CpmiAnyObject' ]
+global_svc_obj_table_names = [*global_group_svc_obj_types, "CpmiAnyObject"]
+svc_obj_table_names = local_svc_obj_table_names + global_svc_obj_table_names
+
+local_api_obj_types = (
+ local_nw_obj_table_names + local_svc_obj_table_names
+) # all obj table names to look at during import
+global_api_obj_types = (
+ global_nw_obj_table_names + global_svc_obj_table_names
+) # all global obj table names to look at during import
+api_obj_types = nw_obj_table_names + svc_obj_table_names # all obj table names to look at during import
+
+types_to_remove_globals_from = ["service-groups"]
+
+obj_types_full_fetch_needed = [
+ "access-roles",
+ "groups",
+ "groups-with-exclusion",
+ "updatable-objects",
+ "gateways-and-servers",
+ "services-other",
+ *group_svc_obj_types,
+]
+
+cp_specific_object_types = [ # used for fetching enrichment data via "get object" separately (no specific API call)
+ "simple-gateway",
+ "simple-cluster",
+ "CpmiVsClusterNetobj",
+ "CpmiVsxClusterNetobj",
+ "CpmiVsxClusterMember",
+ "CpmiVsNetobj",
+ "CpmiAnyObject",
+ "CpmiVsxNetobj",
+ "CpmiClusterMember",
+ "CpmiGatewayPlain",
+ "CpmiHostCkp",
+ "CpmiGatewayCluster",
+ "checkpoint-host",
+ "cluster-member",
+ "CpmiVoipSipDomain",
+ "CpmiVoipMgcpDomain",
+]
diff --git a/roles/importer/files/importer/fw_modules/checkpointR8x/cp_gateway.py b/roles/importer/files/importer/fw_modules/checkpointR8x/cp_gateway.py
new file mode 100644
index 0000000000..725467049b
--- /dev/null
+++ b/roles/importer/files/importer/fw_modules/checkpointR8x/cp_gateway.py
@@ -0,0 +1,70 @@
+from typing import Any
+
+from fwo_log import FWOLogger
+from models.import_state import ImportState
+
+"""
+ normalize all gateway details
+"""
+
+
+def normalize_gateways(native_config: dict[str, Any], import_state: ImportState, normalized_config: dict[str, Any]):
+ normalized_config["gateways"] = []
+ normalize_rulebase_links(native_config, normalized_config)
+ normalize_interfaces(native_config, import_state, normalized_config)
+ normalize_routing(native_config, import_state, normalized_config)
+
+
+def normalize_rulebase_links(native_config: dict[str, Any], normalized_config: dict[str, Any]):
+ gw_range = range(len(native_config["gateways"]))
+ for gw_id in gw_range:
+ gw_uid = native_config["gateways"][gw_id]["uid"]
+ if not gw_in_normalized_config(normalized_config, gw_uid):
+ gw_normalized = create_normalized_gateway(native_config, gw_id)
+ normalized_config["gateways"].append(gw_normalized)
+ for gw_normalized in normalized_config["gateways"]:
+ if gw_normalized["Uid"] == gw_uid:
+ gw_normalized["RulebaseLinks"] = get_normalized_rulebase_link(native_config, gw_id)
+ break
+
+
+def get_normalized_rulebase_link(native_config: dict[str, Any], gw_id: int) -> list[dict[str, Any]]:
+ links = native_config.get("gateways", {})[gw_id].get("rulebase_links")
+ for link in links:
+ if "type" in link:
+ link["link_type"] = link["type"]
+ del link["type"]
+ else:
+ FWOLogger.warning("No type in rulebase link: " + str(link))
+
+ # Remove from_rulebase_uid and from_rule_uid if link_type is initial
+ if link["link_type"] == "initial":
+ if link["from_rulebase_uid"] is not None:
+ link["from_rulebase_uid"] = None
+ if link["from_rule_uid"] is not None:
+ link["from_rule_uid"] = None
+ return links
+
+
+def create_normalized_gateway(native_config: dict[str, Any], gw_id: int) -> dict[str, Any]:
+ gw: dict[str, Any] = {}
+ gw["Uid"] = native_config["gateways"][gw_id]["uid"]
+ gw["Name"] = native_config["gateways"][gw_id]["name"]
+ gw["Interfaces"] = []
+ gw["Routing"] = []
+ gw["RulebaseLinks"] = []
+ return gw
+
+
+def normalize_interfaces(native_config: dict[str, Any], import_state: ImportState, normalized_config: dict[str, Any]):
+ # TODO: Implement this
+ pass
+
+
+def normalize_routing(native_config: dict[str, Any], import_state: ImportState, normalized_config: dict[str, Any]):
+ # TODO: Implement this
+ pass
+
+
+def gw_in_normalized_config(normalized_config: dict[str, Any], gw_uid: str) -> bool:
+ return any(gw["Uid"] == gw_uid for gw in normalized_config["gateways"])
diff --git a/roles/importer/files/importer/fw_modules/checkpointR8x/cp_getter.py b/roles/importer/files/importer/fw_modules/checkpointR8x/cp_getter.py
new file mode 100644
index 0000000000..7853faf8b3
--- /dev/null
+++ b/roles/importer/files/importer/fw_modules/checkpointR8x/cp_getter.py
@@ -0,0 +1,971 @@
+# library for API get functions
+import json
+import time
+from datetime import datetime
+from typing import Any
+
+import fwo_const
+import fwo_globals
+import requests
+from fw_modules.checkpointR8x import cp_const, cp_network
+from fwo_exceptions import FwApiError, FwApiResponseDecodingError, FwLoginFailedError, FwoImporterError
+from fwo_log import FWOLogger
+from model_controllers.management_controller import ManagementController
+from services.service_provider import ServiceProvider
+
+# Constants for status values
+STATUS_IN_PROGRESS = "in progress"
+STATUS_SUCCEEDED = "succeeded"
+STATUS_FAILED = "failed"
+
+
+def cp_api_call(url: str, command: str, json_payload: dict[str, Any], sid: str | None, show_progress: bool = False):
+ url += command
+ request_headers = {"Content-Type": "application/json"}
+ if sid: # only not set for login
+ request_headers.update({"X-chkp-sid": sid})
+
+ FWOLogger.debug(f"api call '{command}'", 9)
+ if command != "login": # do not log passwords
+ FWOLogger.debug("json_payload: " + str(json_payload), 10)
+
+ try:
+ r = requests.post(url, json=json_payload, headers=request_headers, verify=fwo_globals.verify_certs)
+ except requests.exceptions.RequestException as _:
+ if "password" in json.dumps(json_payload):
+ exception_text = "\nerror while sending api_call containing credential information to url '" + str(url)
+ else:
+ exception_text = (
+ "\nerror while sending api_call to url '"
+ + str(url)
+ + "' with payload '"
+ + json.dumps(json_payload, indent=2)
+ + "' and headers: '"
+ + json.dumps(request_headers, indent=2)
+ )
+ raise FwApiError(exception_text)
+ if show_progress:
+ print(".", end="", flush=True) # noqa: T201
+
+ try:
+ json_response = r.json()
+ except Exception:
+ raise FwApiResponseDecodingError(f"checkpointR8x:api_call: response is not in valid json format: {r.text}")
+ return json_response
+
+
+def login(mgm_details: ManagementController):
+ payload = {"user": mgm_details.import_user, "password": mgm_details.secret}
+ domain = mgm_details.get_domain_string()
+ if domain != "":
+ payload.update({"domain": domain})
+ base_url = mgm_details.build_fw_api_string()
+ FWOLogger.debug(f"login - login to url {base_url} with user {mgm_details.import_user}", 3)
+ response = cp_api_call(base_url, "login", payload, "")
+ if "sid" not in response:
+ exception_text = f"getter ERROR: did not receive a sid, api call: {base_url}"
+ raise FwLoginFailedError(exception_text)
+ return response["sid"]
+
+
+def logout(url: str, sid: str):
+ FWOLogger.debug("logout from url " + url, 3)
+ return cp_api_call(url, "logout", {}, sid)
+
+
+def process_single_task(task: dict[str, Any]) -> tuple[str, int]:
+ """Process a single task and return status and result code."""
+ FWOLogger.debug("task: " + json.dumps(task), 6)
+
+ if "status" not in task:
+ FWOLogger.error("no status in task")
+ return STATUS_FAILED, -1
+
+ status = task["status"]
+
+ if STATUS_SUCCEEDED in status:
+ result = check_task_details_for_changes(task)
+ return status, result
+ if status == STATUS_FAILED:
+ FWOLogger.debug("show-changes - status: failed -> no changes found")
+ return status, 0
+ if status == STATUS_IN_PROGRESS:
+ FWOLogger.debug("status: in progress")
+ return status, 0
+ FWOLogger.error("unknown status: " + status)
+ return STATUS_FAILED, -1
+
+
+def process_changes_task(base_url: str, task_id: dict[str, Any], sid: str) -> int:
+ """Process the changes task and return the result."""
+ sleeptime = 1
+ status = STATUS_IN_PROGRESS
+
+ while status == STATUS_IN_PROGRESS:
+ time.sleep(sleeptime)
+ tasks = cp_api_call(base_url, "show-task", task_id, sid)
+
+ if "tasks" not in tasks:
+ FWOLogger.error("no tasks in task response")
+ return -1
+
+ for task in tasks["tasks"]:
+ status, result = process_single_task(task)
+ if status != STATUS_IN_PROGRESS:
+ return result
+
+ sleeptime += 2
+ if sleeptime > 40: # noqa: PLR2004
+ FWOLogger.error("task took too long, aborting")
+ return -1
+
+ return 0
+
+
+def check_task_details_for_changes(task: dict[str, Any]) -> int:
+ """Check task details to see if changes were found."""
+ for detail in task["task-details"]:
+ if detail["changes"]:
+ FWOLogger.debug("status: succeeded -> changes found")
+ return 1
+ FWOLogger.debug("status: succeeded -> but no changes found")
+ return 0
+
+
+def get_changes(sid: str, api_host: str, api_port: str, fromdate: str) -> int:
+ dt_object = datetime.fromisoformat(fromdate)
+ dt_truncated = dt_object.replace(microsecond=0) # Truncate microseconds
+ fromdate = dt_truncated.isoformat()
+
+ payload = {"from-date": fromdate, "details-level": "uid"}
+ FWOLogger.debug("payload: " + json.dumps(payload))
+ base_url = "https://" + api_host + ":" + str(api_port) + "/web_api/"
+ task_id = cp_api_call(base_url, "show-changes", payload, sid)
+
+ FWOLogger.debug("task_id: " + json.dumps(task_id))
+ return process_changes_task(base_url, task_id, sid)
+
+
+def get_policy_structure(
+ api_v_url: str,
+ sid: str,
+ show_params_policy_structure: dict[str, Any],
+ manager_details: ManagementController,
+ policy_structure: list[dict[str, Any]] | None = None,
+) -> int:
+ if policy_structure is None:
+ policy_structure = []
+
+ current = 0
+ total = current + 1
+
+ show_params_policy_structure.update({"offset": current})
+
+ while current < total:
+ packages, current, total = get_show_packages_via_api(api_v_url, sid, show_params_policy_structure)
+
+ for package in packages["packages"]:
+ current_package, already_fetched_package = parse_package(package, manager_details)
+ if not already_fetched_package:
+ continue
+ add_access_layers_to_current_package(package, current_package)
+
+ # in future threat-layers may be fetched analog to add_access_layers_to_current_package
+ policy_structure.append(current_package)
+
+ return 0
+
+
+def get_show_packages_via_api(
+ api_v_url: str, sid: str, show_params_policy_structure: dict[str, Any]
+) -> tuple[dict[str, Any], int, int]:
+ try:
+ packages = cp_api_call(api_v_url, "show-packages", show_params_policy_structure, sid)
+ except Exception:
+ raise FwApiError("could not return 'show-packages'")
+
+ if "total" in packages:
+ total = packages["total"]
+ else:
+ FWOLogger.error("packages do not contain total field")
+ FWOLogger.warning("sid: " + sid)
+ FWOLogger.warning("api_v_url: " + api_v_url)
+ for key, value in show_params_policy_structure.items():
+ FWOLogger.warning("show_params_policy_structure " + key + ": " + str(value))
+ for key, value in packages.items():
+ FWOLogger.warning("packages " + key + ": " + str(value))
+ raise FwApiError("packages do not contain total field")
+
+ if total == 0:
+ current = 0
+ elif "to" in packages:
+ current = packages["to"]
+ else:
+ raise FwApiError("packages do not contain to field")
+ return packages, current, total
+
+
+def parse_package(package: dict[str, Any], manager_details: ManagementController) -> tuple[dict[str, Any], bool]:
+ already_fetched_package = False
+ current_package = {}
+ if "installation-targets" in package and package["installation-targets"] == "all":
+ if not already_fetched_package:
+ current_package: dict[str, Any] = {
+ "name": package["name"],
+ "uid": package["uid"],
+ "targets": [{"name": "all", "uid": "all"}],
+ "access-layers": [],
+ }
+ already_fetched_package = True
+
+ elif "installation-targets-revision" in package:
+ for installation_target in package["installation-targets-revision"]:
+ if is_valid_installation_target(installation_target, manager_details):
+ if not already_fetched_package:
+ current_package = {
+ "name": package["name"],
+ "uid": package["uid"],
+ "targets": [],
+ "access-layers": [],
+ }
+ already_fetched_package = True
+
+ current_package["targets"].append(
+ {"name": installation_target["target-name"], "uid": installation_target["target-uid"]}
+ )
+ else:
+ FWOLogger.warning("installation target in package: " + package["uid"] + " is missing name or uid")
+ return current_package, already_fetched_package
+
+
+def is_valid_installation_target(installation_target: dict[str, Any], manager_details: ManagementController) -> bool:
+ """Ensures that target is defined as gateway in database"""
+ if "target-name" in installation_target and "target-uid" in installation_target:
+ for device in manager_details.devices:
+ if (
+ device["name"] == installation_target["target-name"]
+ and device["uid"] == installation_target["target-uid"]
+ ):
+ return True
+ return False
+
+
+def add_access_layers_to_current_package(package: dict[str, Any], current_package: dict[str, Any]) -> None:
+ if "access-layers" in package:
+ for access_layer in package["access-layers"]:
+ if "name" in access_layer and "uid" in access_layer:
+ current_package["access-layers"].append(
+ {"name": access_layer["name"], "uid": access_layer["uid"], "domain": access_layer["domain"]["uid"]}
+ )
+ else:
+ raise FwApiError("access layer in package: " + package["uid"] + " is missing name or uid")
+
+
+def fetch_global_assignments_chunk(
+ api_v_url: str, sid: str, show_params_policy_structure: dict[str, Any]
+) -> tuple[dict[str, Any], int, int]:
+ """Fetch a chunk of global assignments from the API."""
+ try:
+ assignments = cp_api_call(api_v_url, "show-global-assignments", show_params_policy_structure, sid)
+ except Exception:
+ FWOLogger.error("could not return 'show-global-assignments'")
+ raise FwoImporterError('could not return "show-global-assignments"')
+
+ if "total" not in assignments:
+ log_global_assignments_error(sid, api_v_url, show_params_policy_structure, assignments)
+ raise FwoImporterError('global assignments do not contain "total" field')
+
+ total = assignments["total"]
+
+ if total == 0:
+ current = 0
+ else:
+ if "to" not in assignments:
+ raise FwoImporterError('global assignments do not contain "to" field')
+ current = assignments["to"]
+
+ return assignments, current, total
+
+
+def log_global_assignments_error(
+ sid: str, api_v_url: str, show_params_policy_structure: dict[str, Any], assignments: dict[str, Any]
+) -> None:
+ """Log error information for global assignments debugging."""
+ FWOLogger.warning("sid: " + sid)
+ FWOLogger.warning("api_v_url: " + api_v_url)
+ for key, value in show_params_policy_structure.items():
+ FWOLogger.warning("show_params_policy_structure " + key + ": " + str(value))
+ for key, value in assignments.items():
+ FWOLogger.warning("global assignments " + key + ": " + str(value))
+
+
+def parse_global_assignment(assignment: dict[str, Any]) -> dict[str, Any]:
+ """Parse a single global assignment object."""
+ if "type" not in assignment or assignment["type"] != "global-assignment":
+ raise FwoImporterError("global assignment with unexpected type")
+
+ return {
+ "uid": assignment["uid"],
+ "global-domain": {"uid": assignment["global-domain"]["uid"], "name": assignment["global-domain"]["name"]},
+ "dependent-domain": {
+ "uid": assignment["dependent-domain"]["uid"],
+ "name": assignment["dependent-domain"]["name"],
+ },
+ "global-access-policy": assignment["global-access-policy"],
+ }
+
+
+def get_global_assignments(api_v_url: str, sid: str, show_params_policy_structure: dict[str, Any]) -> list[Any]:
+ current = 0
+ total = current + 1
+ show_params_policy_structure.update({"offset": current})
+ global_assignments: list[dict[str, Any]] = []
+
+ while current < total:
+ assignments, current, total = fetch_global_assignments_chunk(api_v_url, sid, show_params_policy_structure)
+
+ # parse global assignments
+ for assignment in assignments["objects"]:
+ global_assignment = parse_global_assignment(assignment)
+ global_assignments.append(global_assignment)
+
+ return global_assignments
+
+
+def get_rulebases(
+ api_v_url: str,
+ sid: str | None,
+ show_params_rules: dict[str, Any],
+ native_config_domain: dict[str, Any] | None,
+ device_config: dict[str, Any] | None,
+ policy_rulebases_uid_list: list[str],
+ is_global: bool = False,
+ access_type: str = "access",
+ rulebase_uid: str | None = None,
+ rulebase_name: str | None = None,
+) -> list[str]:
+ # i access_type : access / nat
+ native_config_rulebase_key = "rulebases"
+ current_rulebase = {}
+
+ if native_config_domain is None:
+ native_config_domain = {"rulebases": [], "nat_rulebases": []}
+ if device_config is None:
+ device_config = {"rulebase_links": []}
+
+ if access_type == "access":
+ native_config_rulebase_key = "rulebases"
+ elif access_type == "nat":
+ native_config_rulebase_key = "nat_rulebases"
+ else:
+ FWOLogger.error('access_type is neither "access" nor "nat", but ' + access_type)
+
+ # get uid of rulebase
+ if rulebase_uid is None:
+ if rulebase_name is not None:
+ rulebase_uid = get_uid_of_rulebase(rulebase_name, api_v_url, access_type, sid)
+ else:
+ FWOLogger.error("must provide either rulebaseUid or rulebaseName")
+ policy_rulebases_uid_list.append(rulebase_uid) # type: ignore # TODO: get_uid_of_rulebase can return None but in theory should not # noqa: PGH003
+
+ # search all rulebases in nativeConfigDomain and import if rulebase is not already fetched
+ fetched_rulebase_list: list[str] = []
+ for fetched_rulebase in native_config_domain[native_config_rulebase_key]:
+ fetched_rulebase_list.append(fetched_rulebase["uid"])
+ if fetched_rulebase["uid"] == rulebase_uid:
+ current_rulebase = fetched_rulebase
+ break
+
+ # get rulebase in chunks
+ if rulebase_uid not in fetched_rulebase_list:
+ current_rulebase = get_rulebases_in_chunks(
+ rulebase_uid, # type: ignore # noqa: PGH003
+ show_params_rules,
+ api_v_url,
+ access_type,
+ sid, # type: ignore # noqa: PGH003
+ native_config_domain, # type: ignore #TODO: check if None check is needed if yes, change type # noqa: PGH003
+ ) # type: ignore # TODO: rulebaseUid can be None but in theory should not # noqa: PGH003
+ native_config_domain[native_config_rulebase_key].append(current_rulebase)
+
+ # use recursion to get inline layers
+ return get_inline_layers_recursively(
+ current_rulebase,
+ device_config,
+ native_config_domain,
+ api_v_url,
+ sid,
+ show_params_rules,
+ is_global,
+ policy_rulebases_uid_list,
+ )
+
+
+def get_uid_of_rulebase(
+ rulebase_name: str, api_v_url: str, access_type: str, sid: str | None
+) -> str | None: # TODO: what happens if rulebaseUid None? Error?
+ rulebase_uid = None
+ get_rulebase_uid_params: dict[str, Any] = {
+ "name": rulebase_name,
+ "limit": 1,
+ "use-object-dictionary": False,
+ "details-level": "uid",
+ "show-hits": False,
+ }
+ try:
+ rulebase_for_uid = cp_api_call(api_v_url, "show-" + access_type + "-rulebase", get_rulebase_uid_params, sid)
+ rulebase_uid = rulebase_for_uid["uid"]
+ except Exception:
+ FWOLogger.error("could not find uid for rulebase name=" + rulebase_name)
+
+ return rulebase_uid
+
+
+def get_rulebases_in_chunks(
+ rulebase_uid: str,
+ show_params_rules: dict[str, Any],
+ api_v_url: str,
+ access_type: str,
+ sid: str,
+ native_config_domain: dict[str, Any],
+) -> dict[str, Any]:
+ current_rulebase: dict[str, Any] = {"uid": rulebase_uid, "name": "", "chunks": []}
+ show_params_rules.update({"uid": rulebase_uid})
+ current = 0
+ total = current + 1
+
+ while current < total:
+ show_params_rules.update({"offset": current})
+
+ try:
+ rulebase = cp_api_call(api_v_url, "show-" + access_type + "-rulebase", show_params_rules, sid)
+ if current_rulebase["name"] == "" and "name" in rulebase:
+ current_rulebase.update({"name": rulebase["name"]})
+ except Exception:
+ FWOLogger.error("could not find rulebase uid=" + rulebase_uid)
+
+ service_provider = ServiceProvider()
+ global_state = service_provider.get_global_state()
+ description = f"failed to get show-access-rulebase {rulebase_uid}"
+ global_state.import_state.api_call.create_data_issue(severity=2, description=description)
+ raise FwApiError("")
+
+ resolve_checkpoint_uids_via_object_dict(
+ rulebase, native_config_domain, current_rulebase, rulebase_uid, show_params_rules
+ )
+ total, current = control_while_loop_in_get_rulebases_in_chunks(
+ current_rulebase, rulebase, sid, api_v_url, show_params_rules
+ )
+
+ return current_rulebase
+
+
+def resolve_checkpoint_uids_via_object_dict(
+ rulebase: dict[str, Any],
+ native_config_domain: dict[str, Any],
+ current_rulebase: dict[str, Any],
+ rulebase_uid: str,
+ show_params_rules: dict[str, Any],
+) -> None:
+ """
+ Checkpoint stores some rulefields as uids, function translates them to names
+ """
+ try:
+ for rule_field in ["source", "destination", "service", "action", "track", "install-on", "time"]:
+ resolve_ref_list_from_object_dictionary(rulebase, rule_field, native_config_domain=native_config_domain)
+ current_rulebase["chunks"].append(rulebase)
+ except Exception:
+ FWOLogger.error("error while getting a field of layer " + rulebase_uid + ", params: " + str(show_params_rules))
+
+
+def control_while_loop_in_get_rulebases_in_chunks(
+ current_rulebase: dict[str, Any],
+ rulebase: dict[str, Any],
+ sid: str,
+ api_v_url: str,
+ show_params_rules: dict[str, Any],
+) -> tuple[int, int]:
+ total = 0
+ if "total" in rulebase:
+ total = rulebase["total"]
+ else:
+ FWOLogger.error(
+ "rulebase does not contain total field, get_rulebase_chunk_from_api found garbled json "
+ + str(current_rulebase)
+ )
+ FWOLogger.warning("sid: " + sid)
+ FWOLogger.warning("api_v_url: " + api_v_url)
+ for key, value in show_params_rules.items():
+ FWOLogger.warning("show_params_rules " + key + ": " + str(value))
+ for key, value in rulebase.items():
+ FWOLogger.warning("rulebase " + key + ": " + str(value))
+
+ if total == 0:
+ current = 0
+ elif "to" in rulebase:
+ current = rulebase["to"]
+ else:
+ raise FwoImporterError(
+ "get_nat_rules_from_api - rulebase does not contain to field, get_rulebase_chunk_from_api found garbled json "
+ + str(rulebase)
+ )
+ return total, current
+
+
+def get_inline_layers_recursively(
+ current_rulebase: dict[str, Any],
+ device_config: dict[str, Any],
+ native_config_domain: dict[str, Any],
+ api_v_url: str,
+ sid: str | None,
+ show_params_rules: dict[str, Any],
+ is_global: bool,
+ policy_rulebases_uid_list: list[str],
+) -> list[str]:
+ """
+ Takes current_rulebase, splits sections into sub-rulebases and searches for layerguards to fetch
+ """
+ current_rulebase_uid = current_rulebase["uid"]
+ for rulebase_chunk in current_rulebase["chunks"]:
+ # search in case of access rulebase only
+ if "rulebase" in rulebase_chunk:
+ for section in rulebase_chunk["rulebase"]:
+ section_link, current_rulebase_uid = section_traversal_and_links(
+ section, current_rulebase_uid, device_config, is_global
+ )
+
+ for rule in section_link["rulebase"]:
+ if "inline-layer" in rule:
+ # add link to inline layer for current device
+ device_config["rulebase_links"].append(
+ {
+ "from_rulebase_uid": current_rulebase_uid,
+ "from_rule_uid": rule["uid"],
+ "to_rulebase_uid": rule["inline-layer"],
+ "type": "inline",
+ "is_initial": False,
+ "is_global": is_global,
+ "is_section": False,
+ }
+ )
+
+ # get inline layer
+ policy_rulebases_uid_list = get_rulebases(
+ api_v_url,
+ sid,
+ show_params_rules,
+ native_config_domain,
+ device_config,
+ policy_rulebases_uid_list,
+ is_global=is_global,
+ access_type="access",
+ rulebase_uid=rule["inline-layer"],
+ )
+
+ return policy_rulebases_uid_list
+
+
+def section_traversal_and_links(
+ section: dict[str, Any], current_rulebase_uid: str, device_config: dict[str, Any], is_global: bool
+) -> tuple[dict[str, Any], str]:
+ """
+ If section is actually rule, fake it to be section and link sections as self-contained rulebases
+ """
+ # if no section is used, create dummy section
+ dummy_section = False
+ is_section = True
+ if section["type"] != "access-section":
+ section = {"type": "access-section", "uid": section["uid"], "rulebase": [section]}
+ dummy_section = True
+
+ # define placeholder rules as concatenated rulebase
+ if dummy_section and section["rulebase"][0]["type"] == "place-holder":
+ dummy_section = False
+ is_section = False
+
+ # define section chain
+ if not dummy_section:
+ device_config["rulebase_links"].append(
+ {
+ "from_rulebase_uid": current_rulebase_uid,
+ "from_rule_uid": None,
+ "to_rulebase_uid": section["uid"],
+ "type": "concatenated",
+ "is_global": is_global,
+ "is_initial": False,
+ "is_section": is_section,
+ }
+ )
+ current_rulebase_uid = section["uid"]
+
+ return section, current_rulebase_uid
+
+
+def get_placeholder_in_rulebase(rulebase: dict[str, Any]) -> tuple[str | None, str | None]:
+ placeholder_rule_uid = None
+ placeholder_rulebase_uid = None
+ for rulebase_chunk in rulebase["chunks"]:
+ # search in case of access rulebase only
+ if "rulebase" in rulebase_chunk:
+ for section in rulebase_chunk["rulebase"]:
+ # if no section is used, use dummy section
+ section_link = section
+ if section["type"] != "access-section":
+ section_link: dict[str, Any] = {"type": "access-section", "rulebase": [section]}
+
+ for rule in section_link["rulebase"]:
+ placeholder_rule_uid, placeholder_rulebase_uid = assign_placeholder_uids(
+ rulebase, section, rule, placeholder_rule_uid, placeholder_rulebase_uid
+ )
+
+ return placeholder_rule_uid, placeholder_rulebase_uid
+
+
+def assign_placeholder_uids(
+ rulebase: dict[str, Any],
+ section: dict[str, Any],
+ rule: dict[str, Any],
+ placeholder_rule_uid: str | None,
+ placeholder_rulebase_uid: str | None,
+) -> tuple[str | None, str | None]:
+ if rule["type"] == "place-holder":
+ placeholder_rule_uid = rule["uid"]
+ placeholder_rulebase_uid = section["uid"] if "uid" in section else rulebase["uid"]
+ return placeholder_rule_uid, placeholder_rulebase_uid
+
+
+def get_nat_rules_from_api_as_dict(
+ api_v_url: str, sid: str, show_params_rules: dict[str, Any], native_config_domain: dict[str, Any] | None = None
+):
+ if native_config_domain is None:
+ native_config_domain = {}
+ nat_rules: dict[str, list[Any]] = {"nat_rule_chunks": []}
+ current = 0
+ total = current + 1
+ while current < total:
+ show_params_rules["offset"] = current
+ FWOLogger.debug("params: " + str(show_params_rules))
+ rulebase = cp_api_call(api_v_url, "show-nat-rulebase", show_params_rules, sid)
+
+ for rule_field in [
+ "original-source",
+ "original-destination",
+ "original-service",
+ "translated-source",
+ "translated-destination",
+ "translated-service",
+ "action",
+ "track",
+ "install-on",
+ "time",
+ ]:
+ resolve_ref_list_from_object_dictionary(rulebase, rule_field, native_config_domain=native_config_domain)
+
+ nat_rules["nat_rule_chunks"].append(rulebase)
+ if "total" in rulebase:
+ total = rulebase["total"]
+ else:
+ FWOLogger.error(
+ "get_nat_rules_from_api - rulebase does not contain total field, get_rulebase_chunk_from_api found garbled json "
+ + str(nat_rules)
+ )
+ if total == 0:
+ current = 0
+ elif "to" in rulebase:
+ current = rulebase["to"]
+ else:
+ raise FwApiError(
+ "get_nat_rules_from_api - rulebase does not contain to field, get_rulebase_chunk_from_api found garbled json "
+ + str(nat_rules)
+ )
+ return nat_rules
+
+
+def find_element_by_uid(array: list[dict[str, Any]], uid: str | None) -> dict[str, Any] | None:
+ for el in array:
+ if "uid" in el and el["uid"] == uid:
+ return el
+ return None
+
+
+def resolve_ref_from_object_dictionary(
+ uid: str | None,
+ obj_dict: list[dict[str, Any]],
+ native_config_domain: dict[str, Any] | None = None,
+ field_name: str | None = None,
+) -> dict[str, Any] | None:
+ if native_config_domain is None:
+ native_config_domain = {}
+ matched_obj = find_element_by_uid(obj_dict, uid)
+
+ if matched_obj is None: # object not in dict - need to fetch it from API
+ if field_name != "track" and uid != "29e53e3d-23bf-48fe-b6b1-d59bd88036f9":
+ # 29e53e3d-23bf-48fe-b6b1-d59bd88036f9 is a track object uid (track None) which is not in the object dictionary, but used in some rules
+ if field_name is None:
+ field_name = "unknown"
+ if uid is None:
+ uid = "unknown"
+ FWOLogger.warning(f"object of type {field_name} with uid {uid} not found in object dictionary")
+ return None
+ # there are some objects (at least CpmiVoipSipDomain) which are not API-gettable with show-objects (only with show-object "UID")
+ # these must be added to the (network) objects tables
+ if matched_obj["type"] in ["CpmiVoipSipDomain", "CpmiVoipMgcpDomain", "gsn_handover_group"]:
+ FWOLogger.info(
+ f"adding {matched_obj['type']} '{matched_obj['name']}' object manually, because it is not retrieved by show objects API command"
+ )
+ color = matched_obj.get("color", "black")
+ native_config_domain["objects"].append(
+ {
+ "type": matched_obj["type"],
+ "chunks": [
+ {
+ "objects": [
+ {
+ "uid": matched_obj["uid"],
+ "name": matched_obj["name"],
+ "color": color,
+ "type": matched_obj["type"],
+ "domain": matched_obj["domain"],
+ }
+ ]
+ }
+ ],
+ }
+ )
+
+ return matched_obj
+
+
+# resolving all uid references using the object dictionary
+# dealing with a single chunk
+def resolve_ref_list_from_object_dictionary(
+ rulebase: list[dict[str, Any]] | dict[str, Any],
+ value: str,
+ obj_dicts: list[dict[str, Any]] | None = None,
+ native_config_domain: dict[str, Any] | None = None,
+): # TODO: what is objDict: I think it should be a list of dicts
+ if native_config_domain is None:
+ native_config_domain = {}
+ if obj_dicts is None:
+ obj_dicts = []
+ if isinstance(rulebase, dict) and "objects-dictionary" in rulebase:
+ obj_dicts = rulebase["objects-dictionary"]
+ if isinstance(rulebase, list): # found a list of rules
+ for rule in rulebase:
+ if value in rule:
+ categorize_value_for_resolve_ref(rule, value, obj_dicts, native_config_domain) # type: ignore # noqa: PGH003
+ if "rulebase" in rule:
+ resolve_ref_list_from_object_dictionary(
+ rule["rulebase"], value, obj_dicts=obj_dicts, native_config_domain=native_config_domain
+ )
+ elif "rulebase" in rulebase:
+ resolve_ref_list_from_object_dictionary(
+ rulebase["rulebase"], value, obj_dicts=obj_dicts, native_config_domain=native_config_domain
+ )
+
+
+def categorize_value_for_resolve_ref(
+ rule: dict[str, Any], value: str, obj_dict: list[dict[str, Any]], native_config_domain: dict[str, Any]
+):
+ value_list: list[Any] = []
+ if isinstance(rule[value], str): # assuming single uid
+ rule[value] = resolve_ref_from_object_dictionary(
+ rule[value], obj_dict, native_config_domain=native_config_domain, field_name=value
+ )
+ elif "type" in rule[value]: # e.g. track
+ rule[value] = resolve_ref_from_object_dictionary(
+ rule[value]["type"], obj_dict, native_config_domain=native_config_domain, field_name=value
+ )
+ else: # assuming list of rules
+ value_list.extend(
+ [
+ resolve_ref_from_object_dictionary(
+ rule_id, obj_dict, native_config_domain=native_config_domain, field_name=value
+ )
+ for rule_id in rule[value]
+ ]
+ )
+
+ rule[value] = value_list # replace ref list with object list
+
+
+def handle_cpmi_any_object(obj: dict[str, Any]) -> dict[str, Any]:
+ """Handle CpmiAnyObject type objects."""
+ color = obj.get("color", "black")
+
+ if obj["name"] == "Any":
+ return {
+ "type": "hosts",
+ "chunks": [
+ {
+ "objects": [
+ {
+ "uid": obj["uid"],
+ "name": obj["name"],
+ "color": color,
+ "comments": "any nw object checkpoint (hard coded)",
+ "type": "network",
+ "ipv4-address": fwo_const.ANY_IP_IPV4,
+ "domain": obj["domain"],
+ }
+ ]
+ }
+ ],
+ }
+ if obj["name"] == "None": # None service or network object
+ return {
+ "type": "hosts",
+ "chunks": [
+ {
+ "objects": [
+ {
+ "uid": obj["uid"],
+ "name": obj["name"],
+ "color": color,
+ "comments": "none nw object checkpoint (hard coded)",
+ "type": "group",
+ "domain": obj["domain"],
+ }
+ ]
+ }
+ ],
+ }
+ return {}
+
+
+def handle_gateway_objects(obj: dict[str, Any]) -> dict[str, Any]:
+ """Handle various gateway and network member objects."""
+ color = obj.get("color", "black")
+ return {
+ "type": "hosts",
+ "chunks": [
+ {
+ "objects": [
+ {
+ "uid": obj["uid"],
+ "name": obj["name"],
+ "color": color,
+ "comments": obj["comments"],
+ "type": "host",
+ "ipv4-address": cp_network.get_ip_of_obj(obj),
+ "domain": obj["domain"],
+ }
+ ]
+ }
+ ],
+ }
+
+
+def handle_global_object(obj: dict[str, Any]) -> dict[str, Any]:
+ """Handle Global type objects."""
+ color = obj.get("color", "black")
+ return {
+ "type": "hosts",
+ "chunks": [
+ {
+ "objects": [
+ {
+ "uid": obj["uid"],
+ "name": obj["name"],
+ "color": color,
+ "comments": obj["comments"],
+ "type": "host",
+ "ipv4-address": fwo_const.ANY_IP_IPV4,
+ "domain": obj["domain"],
+ }
+ ]
+ }
+ ],
+ }
+
+
+def handle_updatable_objects(obj: dict[str, Any]) -> dict[str, Any]:
+ """Handle updatable objects and VoIP domains."""
+ color = obj.get("color", "black")
+ return {
+ "type": "hosts",
+ "chunks": [
+ {
+ "objects": [
+ {
+ "uid": obj["uid"],
+ "name": obj["name"],
+ "color": color,
+ "comments": obj["comments"],
+ "type": "host",
+ "domain": obj["domain"],
+ }
+ ]
+ }
+ ],
+ }
+
+
+def handle_network_zone_objects(obj: dict[str, Any]) -> dict[str, Any]:
+ """Handle Internet and security-zone objects."""
+ color = obj.get("color", "black")
+ return {
+ "type": "hosts",
+ "chunks": [
+ {
+ "objects": [
+ {
+ "uid": obj["uid"],
+ "name": obj["name"],
+ "color": color,
+ "comments": obj["comments"],
+ "type": "network",
+ "ipv4-address": fwo_const.ANY_IP_IPV4,
+ "domain": obj["domain"],
+ }
+ ]
+ }
+ ],
+ }
+
+
+def get_object_details_from_api(uid_missing_obj: str, sid: str = "", apiurl: str = "") -> dict[str, Any]:
+ FWOLogger.debug(f"getting {uid_missing_obj} from API", 6)
+
+ show_params_host = {"details-level": "full", "uid": uid_missing_obj} # need to get the full object here
+ try:
+ obj = cp_api_call(apiurl, "show-object", show_params_host, sid)
+ except Exception as e:
+ raise FwoImporterError(f"error while trying to get details for object with uid {uid_missing_obj}: {e}")
+
+ if obj is None:
+ raise FwoImporterError(f"None received while trying to get details for object with uid {uid_missing_obj}")
+
+ if "object" not in obj:
+ if "code" in obj:
+ FWOLogger.warning("broken ref in CP DB uid=" + uid_missing_obj + ": " + obj["code"])
+ else:
+ FWOLogger.warning("broken ref in CP DB uid=" + uid_missing_obj)
+ return {}
+
+ obj = obj["object"]
+ obj_type = obj["type"]
+
+ # Handle different object types
+ if obj_type == "CpmiAnyObject":
+ return handle_cpmi_any_object(obj)
+ if obj_type in [
+ "simple-gateway",
+ "CpmiGatewayPlain",
+ "interop",
+ "multicast-address-range",
+ "CpmiVsClusterMember",
+ "CpmiVsxClusterMember",
+ "CpmiVsxNetobj",
+ ]:
+ return handle_gateway_objects(obj)
+ if obj_type == "Global":
+ return handle_global_object(obj)
+ if obj_type in ["updatable-object", "CpmiVoipSipDomain", "CpmiVoipMgcpDomain", "gsn_handover_group"]:
+ return handle_updatable_objects(obj)
+ if obj_type in ["Internet", "security-zone"]:
+ return handle_network_zone_objects(obj)
+ if obj_type == "access-role" or obj_type in cp_const.api_obj_types:
+ return obj
+ FWOLogger.warning(f"missing nw obj of unexpected type '{obj_type}': {uid_missing_obj}")
+ return {}
diff --git a/roles/importer/files/importer/fw_modules/checkpointR8x/cp_network.py b/roles/importer/files/importer/fw_modules/checkpointR8x/cp_network.py
new file mode 100644
index 0000000000..446a2662c4
--- /dev/null
+++ b/roles/importer/files/importer/fw_modules/checkpointR8x/cp_network.py
@@ -0,0 +1,268 @@
+import ipaddress
+import json
+from typing import Any
+
+import fwo_const
+from fw_modules.checkpointR8x import cp_const
+from fw_modules.fortiadom5ff.fmgr_network import add_member_names_for_nw_group
+from fwo_base import cidr_to_range
+from fwo_const import ANY_IP_END, ANY_IP_START, LIST_DELIMITER
+from fwo_log import FWOLogger
+from services.service_provider import ServiceProvider
+
+
+def normalize_network_objects(
+ full_config: dict[str, Any], config2import: dict[str, Any], import_id: int, mgm_id: int = 0
+):
+ nw_objects: list[dict[str, Any]] = []
+ global_domain = initialize_global_domain(full_config["objects"])
+
+ for obj_dict in full_config["objects"]:
+ collect_nw_objects(obj_dict, nw_objects, global_domain, mgm_id=mgm_id)
+
+ for nw_obj in nw_objects:
+ nw_obj.update({"control_id": import_id})
+ if nw_obj["obj_typ"] == "interoperable-device":
+ nw_obj.update({"obj_typ": "external-gateway"})
+ if nw_obj["obj_typ"] == "CpmiVoipSipDomain":
+ FWOLogger.info("found VOIP object - tranforming to empty group")
+ nw_obj.update({"obj_typ": "group"})
+ set_dummy_ip_for_object_without_ip(nw_obj)
+
+ for idx in range(len(nw_objects) - 1):
+ if nw_objects[idx]["obj_typ"] == "group":
+ add_member_names_for_nw_group(idx, nw_objects)
+
+ config2import.update({"network_objects": nw_objects})
+
+
+def set_dummy_ip_for_object_without_ip(nw_obj: dict[str, Any]) -> None:
+ if nw_obj["obj_typ"] != "group" and (nw_obj["obj_ip"] is None or nw_obj["obj_ip"] == ""):
+ FWOLogger.warning(
+ "found object without IP :" + nw_obj["obj_name"] + " (type=" + nw_obj["obj_typ"] + ") - setting dummy IP"
+ )
+ nw_obj.update({"obj_ip": fwo_const.DUMMY_IP})
+ nw_obj.update({"obj_ip_end": fwo_const.DUMMY_IP})
+
+
+def initialize_global_domain(objects: list[dict[str, Any]]) -> dict[str, Any]:
+ """
+ Returns CP Global Domain for MDS and standalone domain otherwise
+ """
+ if len(objects) == 0:
+ FWOLogger.warning("No objects found in full config, cannot initialize global domain")
+ return {}
+
+ if "domain_uid" not in objects[0] or "domain_name" not in objects[0]:
+ FWOLogger.debug("No domain information found in objects, this seems to be a standalone management")
+ return {}
+
+ return {"domain": {"uid": objects[0]["domain_uid"], "name": objects[0]["domain_name"]}}
+
+
+def collect_nw_objects(
+ object_table: dict[str, Any], nw_objects: list[dict[str, Any]], global_domain: dict[str, Any], mgm_id: int = 0
+) -> None:
+ """
+ Collect nw_objects from object tables and write them into global nw_objects dict
+ """
+ if object_table["type"] not in cp_const.nw_obj_table_names:
+ return
+ for chunk in object_table["chunks"]:
+ if "objects" in chunk:
+ for obj in chunk["objects"]:
+ if is_obj_already_collected(nw_objects, obj):
+ continue
+ member_refs, member_names = handle_members(obj)
+ ip_addr = get_ip_of_obj(obj, mgm_id=mgm_id)
+ obj_type, first_ip, last_ip = handle_object_type_and_ip(obj, ip_addr)
+ comments = get_comment_and_color_of_obj(obj)
+
+ nw_objects.append(
+ {
+ "obj_uid": obj["uid"],
+ "obj_name": obj["name"],
+ "obj_color": obj["color"],
+ "obj_comment": comments,
+ "obj_domain": get_domain_uid(obj, global_domain),
+ "obj_typ": obj_type,
+ "obj_ip": first_ip,
+ "obj_ip_end": last_ip,
+ "obj_member_refs": member_refs,
+ "obj_member_names": member_names,
+ }
+ )
+
+
+def get_domain_uid(obj: dict[str, Any], global_domain: dict[str, Any]) -> str | dict[str, Any] | None:
+ """
+ Returns the domain UID for the given object.
+ If the object has a 'domain' key with a 'uid', it returns that UID.
+ Otherwise, it returns the global domain UID.
+ """
+ if "domain" not in obj or "uid" not in obj["domain"]:
+ return obj.update({"domain": global_domain}) # TODO: check if the None value is wanted
+ return obj["domain"]["uid"]
+
+
+def is_obj_already_collected(nw_objects: list[dict[str, Any]], obj: dict[str, Any]) -> bool:
+ if "uid" not in obj:
+ FWOLogger.warning("found nw_object without uid: " + str(obj))
+ return False
+
+ if "domain" in obj:
+ for already_collected_obj in nw_objects:
+ if (
+ obj["uid"] == already_collected_obj["obj_uid"]
+ and obj["domain"]["uid"] == already_collected_obj["obj_domain"]
+ ):
+ return True
+ else:
+ FWOLogger.warning("found nw_object without domain: " + obj["uid"])
+
+ return False
+
+
+def handle_members(obj: dict[str, Any]) -> tuple[str | None, str | None]:
+ """
+ Gets group member uids, currently no member_names
+ """
+ member_refs = None
+ member_names = None
+ if "members" in obj:
+ member_refs = ""
+ member_names = ""
+ for member in obj["members"]:
+ member_refs += member + LIST_DELIMITER
+ member_refs = member_refs[:-1]
+ if obj["members"] == "":
+ obj["members"] = None
+ return member_refs, member_names
+
+
+def handle_object_type_and_ip(obj: dict[str, Any], ip_addr: str | None) -> tuple[str, str | None, str | None]:
+ obj_type = "undef"
+ ip_array = cidr_to_range(ip_addr)
+ first_ip = None
+ last_ip = None
+ if len(ip_array) == 2: # noqa: PLR2004
+ first_ip = ip_array[0]
+ last_ip = ip_array[1]
+ elif len(ip_array) == 1:
+ first_ip = ip_array[0]
+ last_ip = None
+
+ if "type" in obj:
+ obj_type = obj["type"]
+
+ if obj_type == "updatable-object":
+ first_ip = ANY_IP_START
+ last_ip = ANY_IP_END
+ obj_type = "dynamic_net_obj"
+
+ if obj_type in ["group-with-exclusion", "security-zone", "dynamic-object"]:
+ obj_type = "group"
+ # TODO: handle exclusion groups correctly
+
+ if obj_type == "dns-domain":
+ obj_type = "domain"
+ first_ip = ANY_IP_START
+ last_ip = ANY_IP_END
+
+ if obj_type == "security-zone":
+ first_ip = ANY_IP_START
+ last_ip = ANY_IP_END
+ obj_type = "network"
+
+ if obj_type in ["address-range", "multicast-address-range"]:
+ obj_type = "ip_range"
+ if "-" in str(ip_addr):
+ first_ip, last_ip = str(ip_addr).split("-")
+ else:
+ FWOLogger.warning(
+ "parse_network::collect_nw_objects - found range object '"
+ + obj["name"]
+ + "' without hyphen: "
+ + ip_addr
+ )
+ elif obj_type in cp_const.cp_specific_object_types:
+ FWOLogger.debug(f"rewriting non-standard cp-host-type '{obj['name']}' with object type '{obj_type}' to host", 6)
+ FWOLogger.debug("obj_dump:" + json.dumps(obj, indent=3), 6)
+ obj_type = "host"
+
+ return obj_type, first_ip, last_ip
+
+
+def get_comment_and_color_of_obj(obj: dict[str, Any]) -> str | None:
+ """
+ Returns comment and sets missing color to black
+ """
+ comments = None if "comments" not in obj or obj["comments"] == "" else obj["comments"]
+ if "color" not in obj or obj["color"] == "" or obj["color"] == "none":
+ obj["color"] = "black"
+ return comments
+
+
+def validate_ip_address(address: str) -> bool:
+ try:
+ ipaddress.ip_network(address)
+ return True
+ except ValueError:
+ return False
+
+
+def get_ip_of_obj(obj: dict[str, Any], mgm_id: int | None = None) -> str | None:
+ if "ipv4-address" in obj:
+ ip_addr = obj["ipv4-address"]
+ elif "ipv6-address" in obj:
+ ip_addr = obj["ipv6-address"]
+ elif "subnet4" in obj:
+ ip_addr = obj["subnet4"] + "/" + str(obj["mask-length4"])
+ elif "subnet6" in obj:
+ ip_addr = obj["subnet6"] + "/" + str(obj["mask-length6"])
+ elif "ipv4-address-first" in obj and "ipv4-address-last" in obj:
+ ip_addr = obj["ipv4-address-first"] + "-" + str(obj["ipv4-address-last"])
+ elif "ipv6-address-first" in obj and "ipv6-address-last" in obj:
+ ip_addr = obj["ipv6-address-first"] + "-" + str(obj["ipv6-address-last"])
+ else:
+ ip_addr = None
+
+ ## fix malformed ip addresses (should not regularly occur and constitutes a data issue in CP database)
+ if ip_addr is None or (
+ "type" in obj and (obj["type"] == "address-range" or obj["type"] == "multicast-address-range")
+ ):
+ pass # ignore None and ranges here
+ elif not validate_ip_address(ip_addr):
+ alert_description = "object is not a valid ip address (" + str(ip_addr) + ")"
+ service_provider = ServiceProvider()
+ global_state = service_provider.get_global_state()
+ api_call = global_state.import_state.api_call
+ api_call.create_data_issue(
+ severity=2, obj_name=obj["name"], object_type=obj["type"], description=alert_description, mgm_id=mgm_id
+ )
+ alert_description = (
+ "object '" + obj["name"] + "' (type=" + obj["type"] + ") is not a valid ip address (" + str(ip_addr) + ")"
+ )
+ api_call.set_alert(
+ title="import error",
+ severity=2,
+ description=alert_description,
+ source="import",
+ alert_code=17,
+ mgm_id=mgm_id,
+ )
+ ip_addr = fwo_const.DUMMY_IP # setting syntactically correct dummy ip
+ return ip_addr
+
+
+def make_host(ip_in: str) -> str | None:
+ ip_obj: ipaddress.IPv4Address | ipaddress.IPv6Address = ipaddress.ip_address(ip_in)
+
+ # If it's a valid address, append the appropriate CIDR notation
+ if isinstance(ip_obj, ipaddress.IPv4Address):
+ return f"{ip_in}/32"
+ if isinstance(
+ ip_obj, ipaddress.IPv6Address
+ ): # TODO: check if just else is sufficient # type: ignore # noqa: PGH003
+ return f"{ip_in}/128"
+ return None
diff --git a/roles/importer/files/importer/fw_modules/checkpointR8x/cp_rule.py b/roles/importer/files/importer/fw_modules/checkpointR8x/cp_rule.py
new file mode 100644
index 0000000000..a0aa82a2d7
--- /dev/null
+++ b/roles/importer/files/importer/fw_modules/checkpointR8x/cp_rule.py
@@ -0,0 +1,511 @@
+import ast
+import json
+from typing import Any
+
+from fwo_base import sanitize, sort_and_join_refs
+from fwo_const import DEFAULT_SECTION_HEADER_TEXT, LIST_DELIMITER
+from fwo_exceptions import FwoImporterErrorInconsistenciesError
+from fwo_log import FWOLogger
+from models.import_state import ImportState
+from models.rule import RuleNormalized
+from models.rule_enforced_on_gateway import RuleEnforcedOnGatewayNormalized
+from models.rulebase import Rulebase
+
+uid_to_name_map: dict[str, str] = {}
+
+"""
+ new import format which takes the following cases into account without duplicating any rules in the DB:
+ - single rulebase used on more than one gw
+ - global policies enforced on more than one gws
+ - inline layers (CP)
+ - migrate section headers from rule to ordering element
+ ...
+"""
+
+
+def normalize_rulebases(
+ native_config: dict[str, Any],
+ native_config_global: dict[str, Any] | None,
+ import_state: ImportState,
+ normalized_config_dict: dict[str, Any],
+ normalized_config_global: dict[str, Any] | None,
+ is_global_loop_iteration: bool,
+):
+ normalized_config_dict["policies"] = []
+
+ # fill uid_to_name_map:
+ for nw_obj in normalized_config_dict["network_objects"]:
+ uid_to_name_map[nw_obj["obj_uid"]] = nw_obj["obj_name"]
+
+ fetched_rulebase_uids: list[str] = []
+ if normalized_config_global is not None and normalized_config_global != {}:
+ fetched_rulebase_uids.extend(
+ [normalized_rulebase_global.uid for normalized_rulebase_global in normalized_config_global["policies"]]
+ )
+ for gateway in native_config["gateways"]:
+ normalize_rulebases_for_each_link_destination(
+ gateway,
+ fetched_rulebase_uids,
+ native_config,
+ native_config_global,
+ is_global_loop_iteration,
+ import_state,
+ normalized_config_dict,
+ normalized_config_global, # type: ignore # TODO: check if normalized_config_global can be None, I am pretty sure it cannot be None here # noqa: PGH003
+ ) # TODO: parse nat rulebase here
+
+
+def normalize_rulebases_for_each_link_destination(
+ gateway: dict[str, Any],
+ fetched_rulebase_uids: list[str],
+ native_config: dict[str, Any],
+ native_config_global: dict[str, Any] | None,
+ is_global_loop_iteration: bool,
+ import_state: ImportState,
+ normalized_config_dict: dict[str, Any],
+ normalized_config_global: dict[str, Any],
+):
+ for rulebase_link in gateway["rulebase_links"]:
+ if rulebase_link["to_rulebase_uid"] not in fetched_rulebase_uids and rulebase_link["to_rulebase_uid"] != "":
+ rulebase_to_parse, is_section, is_placeholder = find_rulebase_to_parse(
+ native_config["rulebases"], rulebase_link["to_rulebase_uid"]
+ )
+ # search in global rulebase
+ found_rulebase_in_global = False
+ if rulebase_to_parse == {} and not is_global_loop_iteration and native_config_global is not None:
+ rulebase_to_parse, is_section, is_placeholder = find_rulebase_to_parse(
+ native_config_global["rulebases"], rulebase_link["to_rulebase_uid"]
+ )
+ found_rulebase_in_global = True
+ if rulebase_to_parse == {}:
+ FWOLogger.warning("found to_rulebase link without rulebase in nativeConfig: " + str(rulebase_link))
+ continue
+ normalized_rulebase = initialize_normalized_rulebase(rulebase_to_parse, import_state.mgm_details.uid)
+ parse_rulebase(
+ rulebase_to_parse, is_section, is_placeholder, normalized_rulebase, gateway, native_config["policies"]
+ )
+ fetched_rulebase_uids.append(rulebase_link["to_rulebase_uid"])
+
+ if found_rulebase_in_global:
+ normalized_config_global["policies"].append(normalized_rulebase)
+ else:
+ normalized_config_dict["policies"].append(normalized_rulebase)
+
+
+def find_rulebase_to_parse(rulebase_list: list[dict[str, Any]], rulebase_uid: str) -> tuple[dict[str, Any], bool, bool]:
+ """
+ Decide if input rulebase is true rulebase, section or placeholder
+ """
+ for rulebase in rulebase_list:
+ if rulebase["uid"] == rulebase_uid:
+ return rulebase, False, False
+ rulebase_to_parse, is_section, is_placeholder = find_rulebase_to_parse_in_case_of_chunk(rulebase, rulebase_uid)
+ if rulebase_to_parse != {}:
+ return rulebase_to_parse, is_section, is_placeholder
+
+ # handle case: no rulebase found
+ return {}, False, False
+
+
+def find_rulebase_to_parse_in_case_of_chunk(
+ rulebase: dict[str, Any], rulebase_uid: str
+) -> tuple[dict[str, Any], bool, bool]:
+ is_section = False
+ rulebase_to_parse = {}
+ for chunk in rulebase["chunks"]:
+ for section in chunk["rulebase"]:
+ if section["uid"] == rulebase_uid:
+ if section["type"] == "place-holder":
+ return section, False, True
+ rulebase_to_parse, is_section = find_rulebase_to_parse_in_case_of_section(
+ is_section, rulebase_to_parse, section
+ )
+ return rulebase_to_parse, is_section, False
+
+
+def find_rulebase_to_parse_in_case_of_section(
+ is_section: bool, rulebase_to_parse: dict[str, Any], section: dict[str, Any]
+) -> tuple[dict[str, Any], bool]:
+ if is_section:
+ rulebase_to_parse = concatenat_sections_across_chunks(rulebase_to_parse, section)
+ else:
+ is_section = True
+ rulebase_to_parse = section
+ return rulebase_to_parse, is_section
+
+
+def concatenat_sections_across_chunks(rulebase_to_parse: dict[str, Any], section: dict[str, Any]) -> dict[str, Any]:
+ if "to" in rulebase_to_parse and "from" in section:
+ if rulebase_to_parse["to"] + 1 == section["from"]:
+ if rulebase_to_parse["name"] == section["name"]:
+ for rule in section["rulebase"]:
+ rulebase_to_parse["rulebase"].append(rule)
+ rulebase_to_parse["to"] = section["to"]
+ else:
+ raise FwoImporterErrorInconsistenciesError("Inconsistent naming in Checkpoint Chunks.")
+ else:
+ raise FwoImporterErrorInconsistenciesError("Inconsistent numbering in Checkpoint Chunks.")
+ else:
+ raise FwoImporterErrorInconsistenciesError("Broken format in Checkpoint Chunks.")
+ return rulebase_to_parse
+
+
+def initialize_normalized_rulebase(rulebase_to_parse: dict[str, Any], mgm_uid: str) -> Rulebase:
+ rulebase_name = rulebase_to_parse.get("name", DEFAULT_SECTION_HEADER_TEXT)
+ rulebase_uid = rulebase_to_parse["uid"]
+ return Rulebase(uid=rulebase_uid, name=rulebase_name, mgm_uid=mgm_uid, rules={})
+
+
+def parse_rulebase(
+ rulebase_to_parse: dict[str, Any],
+ is_section: bool,
+ is_placeholder: bool,
+ normalized_rulebase: Rulebase,
+ gateway: dict[str, Any],
+ policy_structure: list[dict[str, Any]],
+):
+ if is_section:
+ for rule in rulebase_to_parse["rulebase"]:
+ # delte_v sind import_id, parent_uid, config2import wirklich egal? Dann können wir diese argumente löschen - NAT ACHTUNG
+ parse_single_rule(rule, normalized_rulebase, normalized_rulebase.uid, None, gateway, policy_structure)
+
+ FWOLogger.debug("parsed rulebase " + normalized_rulebase.uid, 4)
+ elif is_placeholder:
+ parse_single_rule(
+ rulebase_to_parse, normalized_rulebase, normalized_rulebase.uid, None, gateway, policy_structure
+ )
+ else:
+ parse_rulebase_chunk(rulebase_to_parse, normalized_rulebase, gateway, policy_structure)
+
+
+def parse_rulebase_chunk(
+ rulebase_to_parse: dict[str, Any],
+ normalized_rulebase: Rulebase,
+ gateway: dict[str, Any],
+ policy_structure: list[dict[str, Any]],
+):
+ for chunk in rulebase_to_parse["chunks"]:
+ for rule in chunk["rulebase"]:
+ if "rule-number" in rule:
+ parse_single_rule(rule, normalized_rulebase, normalized_rulebase.uid, None, gateway, policy_structure)
+ else:
+ FWOLogger.debug("found unparsable rulebase: " + str(rulebase_to_parse), 9)
+
+
+def accept_malformed_parts(objects: dict[str, Any] | list[dict[str, Any]], part: str = "") -> dict[str, Any]:
+ FWOLogger.debug(f"about to accept malformed rule part ({part}): {objects!s}")
+
+ # if we are dealing with a list with one element, resolve the list
+ if isinstance(objects, list) and len(objects) == 1:
+ objects = objects[0]
+
+ if isinstance(objects, dict):
+ if part == "action":
+ return {"action": objects.get("name", None)}
+ if part == "install-on":
+ return {"install-on": objects.get("name", None)}
+ if part == "time":
+ return {"time": objects.get("name", None)}
+ if part == "track":
+ return {"track": objects.get("type", {}).get("name", None)}
+ FWOLogger.warning(f"found no uid or name in rule part ({part}): {objects!s}")
+ return {}
+ FWOLogger.warning(f"objects is not a dictionary: {objects!s}")
+ return {}
+
+
+def parse_rule_part(
+ objects: dict[str, Any] | list[dict[str, Any] | None] | None, part: str = "source"
+) -> dict[str, Any]:
+ address_objects: dict[str, Any] = {}
+
+ if objects is None:
+ FWOLogger.debug(
+ f"rule part {part} is None: {objects!s}, which is normal for track field in inline layer guards"
+ )
+ return None # type: ignore # noqa: PGH003#TODO: check if this is ok or should raise an Exception
+
+ if "chunks" in objects: # for chunks of actions?!
+ address_objects.update(
+ parse_rule_part(objects["chunks"], part=part) # type: ignore # noqa: PGH003 # TODO: This Has to be refactored
+ ) # need to parse chunk first
+
+ if isinstance(objects, dict):
+ return _parse_single_address_object(address_objects, objects, part)
+ # assuming list of objects
+ for obj in objects:
+ if obj is None:
+ FWOLogger.warning(f"found list with a single None obj: {objects!s}")
+ continue
+ if "chunks" in obj:
+ address_objects.update(
+ parse_rule_part(obj["chunks"], part=part)
+ ) # need to parse chunk first # TODO: check if this is ok or should raise an Exception
+ elif "objects" in obj:
+ for o in obj["objects"]:
+ address_objects.update(
+ parse_rule_part(o, part=part)
+ ) # need to parse chunk first # TODO: check if this is ok or should raise an Exception
+ return address_objects
+ elif "type" in obj: # found checkpoint object
+ _parse_obj_with_type(obj, address_objects)
+ else:
+ return accept_malformed_parts(objects, part=part) # type: ignore # TODO: check if this is ok or should raise an Exception # noqa: PGH003
+
+ if "" in address_objects.values():
+ FWOLogger.warning("found empty name in one rule part (" + part + "): " + str(address_objects))
+
+ return address_objects
+
+
+def _parse_single_address_object(address_objects: dict[str, Any], objects: dict[str, Any], part: str):
+ if "uid" in objects and "name" in objects:
+ address_objects[objects["uid"]] = objects["name"]
+ return address_objects
+ return accept_malformed_parts(objects, part=part)
+
+
+def _parse_obj_with_type(obj: dict[str, Any], address_objects: dict[str, Any]) -> None:
+ if obj["type"] == "LegacyUserAtLocation":
+ address_objects[obj["uid"]] = obj["name"]
+
+ elif obj["type"] == "access-role":
+ _parse_obj_with_access_role(obj, address_objects)
+ else: # standard object
+ address_objects[obj["uid"]] = obj["name"]
+
+
+def _parse_obj_with_access_role(obj: dict[str, Any], address_objects: dict[str, Any]) -> None:
+ if "networks" not in obj:
+ address_objects[obj["uid"]] = obj["name"] # adding IA without IP info, TODO: get full networks details here!
+ return
+ if isinstance(obj["networks"], str): # just a single source
+ if obj["networks"] == "any":
+ address_objects[obj["uid"]] = obj["name"] + "@" + "Any"
+ else:
+ address_objects[obj["uid"]] = obj["name"] + "@" + obj["networks"]
+ else: # more than one source
+ for nw in obj["networks"]:
+ nw_resolved = resolve_nwobj_uid_to_name(nw)
+ if nw_resolved == "":
+ address_objects[obj["uid"]] = obj["name"]
+ else:
+ address_objects[obj["uid"]] = obj["name"] + "@" + nw_resolved
+
+
+def parse_single_rule(
+ native_rule: dict[str, Any],
+ rulebase: Rulebase,
+ layer_name: str,
+ parent_uid: str | None,
+ gateway: dict[str, Any],
+ policy_structure: list[dict[str, Any]],
+):
+ # reference to domain rule layer, filling up basic fields
+ if not (
+ "type" in native_rule and native_rule["type"] != "place-holder" and "rule-number" in native_rule
+ ): # standard rule, no section header
+ return
+ # the following objects might come in chunks:
+ source_objects: dict[str, str] = parse_rule_part(native_rule["source"], "source")
+ rule_src_ref, rule_src_name = sort_and_join_refs(list(source_objects.items()))
+
+ dst_objects: dict[str, str] = parse_rule_part(native_rule["destination"], "destination")
+ rule_dst_ref, rule_dst_name = sort_and_join_refs(list(dst_objects.items()))
+ svc_objects: dict[str, str] = parse_rule_part(native_rule["service"], "service")
+ rule_svc_ref, rule_svc_name = sort_and_join_refs(list(svc_objects.items()))
+ rule_enforced_on_gateways = parse_rule_enforced_on_gateway(gateway, policy_structure, native_rule=native_rule)
+ list_of_gw_uids = sorted({enforceEntry.dev_uid for enforceEntry in rule_enforced_on_gateways})
+ str_list_of_gw_uids = LIST_DELIMITER.join(list_of_gw_uids) if list_of_gw_uids else None
+
+ rule_track = _parse_track(native_rule=native_rule)
+
+ action_objects = parse_rule_part(native_rule["action"], "action")
+ if action_objects is not None: # type: ignore # TODO: this should be never None # noqa: PGH003
+ rule_action = LIST_DELIMITER.join(action_objects.values()) # expecting only a single action
+ else:
+ rule_action = None
+ FWOLogger.warning("found rule without action: " + str(native_rule))
+
+ time_objects = parse_rule_part(native_rule["time"], "time")
+ rule_time = LIST_DELIMITER.join(time_objects.values()) if time_objects else None
+
+ # starting with the non-chunk objects
+ rule_name = native_rule.get("name")
+
+ # new in v8.0.3:
+ rule_custom_fields = native_rule.get("custom-fields")
+
+ # we leave out all last_admin info for now
+ last_change_admin = None
+
+ parent_rule_uid = _parse_parent_rule_uid(parent_uid, native_rule=native_rule)
+
+ # new in v5.5.1:
+ rule_type = native_rule.get("rule_type", "access")
+
+ comments = native_rule.get("comments")
+ if comments == "":
+ comments = None
+
+ if "hits" in native_rule and "last-date" in native_rule["hits"] and "iso-8601" in native_rule["hits"]["last-date"]:
+ last_hit = native_rule["hits"]["last-date"]["iso-8601"]
+ else:
+ last_hit = None
+
+ rule: dict[str, Any] = {
+ "rule_num": 0,
+ "rule_num_numeric": 0,
+ "rulebase_name": sanitize(layer_name),
+ "rule_disabled": not bool(native_rule["enabled"]),
+ "rule_src_neg": bool(native_rule["source-negate"]),
+ "rule_src": sanitize(rule_src_name),
+ "rule_src_refs": sanitize(rule_src_ref),
+ "rule_dst_neg": bool(native_rule["destination-negate"]),
+ "rule_dst": sanitize(rule_dst_name),
+ "rule_dst_refs": sanitize(rule_dst_ref),
+ "rule_svc_neg": bool(native_rule["service-negate"]),
+ "rule_svc": sanitize(rule_svc_name),
+ "rule_svc_refs": sanitize(rule_svc_ref),
+ "rule_action": sanitize(rule_action, lower=True),
+ "rule_track": sanitize(rule_track, lower=True),
+ "rule_installon": sanitize(str_list_of_gw_uids),
+ "rule_time": sanitize(rule_time),
+ "rule_name": sanitize(rule_name),
+ "rule_uid": sanitize(native_rule["uid"]),
+ "rule_custom_fields": sanitize(rule_custom_fields),
+ "rule_implied": False,
+ "rule_type": sanitize(rule_type),
+ "last_change_admin": sanitize(last_change_admin),
+ "parent_rule_uid": sanitize(parent_rule_uid),
+ "last_hit": sanitize(last_hit),
+ }
+ if comments is not None:
+ rule["rule_comment"] = sanitize(comments)
+ rulebase.rules.update({rule["rule_uid"]: RuleNormalized(**rule)})
+
+
+def _parse_parent_rule_uid(parent_uid: str | None, native_rule: dict[str, Any]) -> str | None:
+ # new in v5.1.17:
+ if "parent_rule_uid" in native_rule:
+ FWOLogger.debug(
+ "found rule (uid=" + native_rule["uid"] + ") with parent_rule_uid set: " + native_rule["parent_rule_uid"]
+ )
+ parent_rule_uid = native_rule["parent_rule_uid"]
+ else:
+ parent_rule_uid = parent_uid
+
+ if parent_rule_uid == "":
+ parent_rule_uid = None
+
+ return parent_rule_uid
+
+
+def _parse_track(native_rule: dict[str, Any]) -> str:
+ if isinstance(native_rule["track"], str):
+ rule_track = native_rule["track"]
+ else:
+ track_objects = parse_rule_part(native_rule["track"], "track")
+ rule_track = "none" if track_objects is None else LIST_DELIMITER.join(track_objects.values()) # type: ignore[union-attr] # TODO: should never be None
+ return rule_track
+
+
+def parse_rule_enforced_on_gateway(
+ gateway: dict[str, Any], policy_structure: list[dict[str, Any]], native_rule: dict[str, Any]
+) -> list[RuleEnforcedOnGatewayNormalized]:
+ """
+ Parse rule enforcement information from native rule.
+
+ Args:
+ native_rule: The native rule dictionary containing install-on information
+
+ Returns:
+ list of RuleEnforcedOnGatewayNormalized objects
+
+ Raises:
+ ValueError: If nativeRule is None or empty
+
+ """
+ if not native_rule:
+ raise ValueError("Native rule cannot be empty")
+
+ enforce_entries: list[RuleEnforcedOnGatewayNormalized] = []
+ all_target_gw_names_dict = parse_rule_part(native_rule["install-on"], "install-on")
+
+ for target_uid in all_target_gw_names_dict:
+ target_name = all_target_gw_names_dict[target_uid]
+ if target_name == "Policy Targets": # or target == 'Any'
+ device_uid_list = find_devices_for_current_policy(gateway, policy_structure)
+ for device_uid in device_uid_list:
+ enforce_entry = RuleEnforcedOnGatewayNormalized(rule_uid=native_rule["uid"], dev_uid=device_uid)
+ enforce_entries.append(enforce_entry)
+ else:
+ enforce_entry = RuleEnforcedOnGatewayNormalized(rule_uid=native_rule["uid"], dev_uid=target_uid)
+ enforce_entries.append(enforce_entry)
+ return enforce_entries
+
+
+def find_devices_for_current_policy(gateway: dict[str, Any], policy_structure: list[dict[str, Any]]) -> list[str]:
+ device_uid_list: list[str] = []
+ for policy in policy_structure:
+ for target in policy["targets"]:
+ if target["uid"] == gateway["uid"]:
+ device_uid_list.extend([device["uid"] for device in policy["targets"]])
+ return device_uid_list
+
+
+def resolve_nwobj_uid_to_name(nw_obj_uid: str) -> str:
+ if nw_obj_uid in uid_to_name_map:
+ return uid_to_name_map[nw_obj_uid]
+ FWOLogger.warning("could not resolve network object with uid " + nw_obj_uid)
+ return ""
+
+
+# delete_v: left here only for nat case
+def check_and_add_section_header(
+ src_rulebase: dict[str, Any],
+ target_rulebase: Rulebase,
+ layer_name: str,
+ import_id: str,
+ section_header_uids: set[str],
+):
+ # TODO: re-implement
+ raise NotImplementedError("check_and_add_section_header is not implemented yet.")
+
+
+def insert_section_header_rule(
+ _target_rulebase: Rulebase,
+ _section_name: str,
+ _layer_name: str,
+ _import_id: str,
+ _src_rulebase_uid: str,
+ _section_header_uids: set[str],
+ _parent_uid: str,
+):
+ # TODO: re-implement
+ return
+
+
+def ensure_json(raw: str) -> Any:
+ """
+ Tries to parse the given string as valid JSON.
+ Falls back to ast.literal_eval() if the JSON is using single quotes
+ or is otherwise not strictly compliant.
+
+ Args:
+ raw: The input string containing JSON-like data.
+
+ Returns:
+ The parsed Python object (e.g., dict, list, str, int, etc.).
+
+ Raises:
+ ValueError: If neither JSON parsing nor literal_eval() succeed.
+
+ """
+ try:
+ return json.loads(raw)
+ except json.JSONDecodeError:
+ try:
+ return ast.literal_eval(raw)
+ except (ValueError, SyntaxError) as e:
+ raise ValueError(f"Invalid JSON or literal: {e}") from e
diff --git a/roles/importer/files/importer/fw_modules/checkpointR8x/cp_service.py b/roles/importer/files/importer/fw_modules/checkpointR8x/cp_service.py
new file mode 100644
index 0000000000..046a2406ea
--- /dev/null
+++ b/roles/importer/files/importer/fw_modules/checkpointR8x/cp_service.py
@@ -0,0 +1,208 @@
+import re
+from typing import Any
+
+from fw_modules.checkpointR8x import cp_const
+from fwo_const import LIST_DELIMITER
+from fwo_exceptions import FwoImporterErrorInconsistenciesError
+
+
+# collect_svcobjects writes svc info into global users dict
+def collect_svc_objects(object_table: dict[str, Any], svc_objects: list[dict[str, Any]]):
+ if object_table["type"] in cp_const.svc_obj_table_names:
+ typ = "undef"
+ if object_table["type"] in cp_const.group_svc_obj_types:
+ typ = "group"
+ if object_table["type"] in cp_const.simple_svc_obj_types:
+ typ = "simple"
+ for chunk in object_table["chunks"]:
+ if "objects" in chunk:
+ for obj in chunk["objects"]:
+ collect_single_svc_object(obj)
+ svc_objects.append(
+ {
+ "svc_uid": obj["uid"],
+ "svc_name": obj["name"],
+ "svc_color": obj["color"],
+ "svc_comment": obj["comments"],
+ "svc_domain": obj["domain_uid"],
+ "svc_typ": typ,
+ "svc_port": obj["port"],
+ "svc_port_end": obj["port_end"],
+ "svc_member_refs": obj["svc_member_refs"],
+ "svc_member_names": None,
+ "ip_proto": obj["proto"],
+ "svc_timeout": obj["session_timeout"],
+ "rpc_nr": obj["rpc_nr"],
+ }
+ )
+
+
+def _set_default_values(obj: dict[str, Any]):
+ """
+ Set default values for color, comments, and domain_uid.
+ """
+ if "color" not in obj or obj["color"] == "" or obj["color"] == "none":
+ obj["color"] = "black"
+
+ if "comments" not in obj or obj["comments"] == "":
+ obj["comments"] = None
+
+ obj["domain_uid"] = get_obj_domain_uid(obj)
+
+
+def _get_rpc_number(obj: dict[str, Any]) -> str | None:
+ """
+ Extract RPC number from interface-uuid or program-number.
+ Returns RPC number or None.
+ """
+ if "interface-uuid" in obj:
+ return obj["interface-uuid"]
+ if "program-number" in obj:
+ return obj["program-number"]
+ return None
+
+
+def _get_session_timeout(obj: dict[str, Any]) -> str | None:
+ """
+ Extract and stringify session timeout.
+ Returns session timeout as string or None.
+ """
+ if "session-timeout" in obj:
+ return str(obj["session-timeout"])
+ return None
+
+
+def _get_member_references(obj: dict[str, Any]) -> str | None:
+ """
+ Process members list and return concatenated member references.
+ Returns member reference string or None.
+ """
+ if "members" not in obj:
+ return None
+
+ member_refs = ""
+ for member in obj["members"]:
+ if isinstance(member, str):
+ member_refs += member + LIST_DELIMITER
+ elif isinstance(member, dict) and "uid" in member and isinstance(member["uid"], str):
+ member_refs += member["uid"] + LIST_DELIMITER
+ return member_refs[:-1] if member_refs else None
+
+
+def _get_protocol_number(obj: dict[str, Any]) -> int | None:
+ """
+ Extract and validate protocol number from object.
+ Returns validated protocol number or None.
+ """
+ proto_map = {"service-tcp": 6, "service-udp": 17, "service-icmp": 1}
+
+ proto = None
+ if "type" in obj and obj["type"] in proto_map:
+ proto = proto_map[obj["type"]]
+ elif "ip-protocol" in obj:
+ proto = obj["ip-protocol"]
+
+ return proto if proto is None or proto >= 0 else None
+
+
+def collect_single_svc_object(obj: dict[str, Any]) -> None:
+ """
+ Collects a single service object and appends its details to the svc_objects list.
+ Handles different types of service objects and normalizes port information.
+ """
+ obj["proto"] = _get_protocol_number(obj)
+
+ obj["svc_member_refs"] = _get_member_references(obj)
+ # svc_member_names are added later in add_member_names_for_svc_group()
+
+ obj["session_timeout"] = _get_session_timeout(obj)
+ obj["rpc_nr"] = _get_rpc_number(obj)
+
+ obj["port"], obj["port_end"] = normalize_port(obj)
+ _set_default_values(obj)
+
+
+def normalize_port(obj: dict[str, Any]) -> tuple[str | None, str | None]:
+ """
+ Normalizes the port information in the given object.
+ If the 'port' key exists, it processes the port value to handle ranges and special cases.
+ """
+ port = None
+ port_end = None
+ if "port" in obj:
+ port = str(obj["port"])
+ pattern = re.compile(r"^\>(\d+)$")
+ match = pattern.match(port)
+ if match:
+ return str(int(match.group()[1:]) + 1), str(65535)
+ pattern = re.compile(r"^\<(\d+)$")
+ match = pattern.match(port)
+ if match:
+ return str(1), str(int(match.group()[1:]) - 1)
+ pattern = re.compile(r"^(\d+)\-(\d+)$")
+ match = pattern.match(port)
+ if match:
+ match_result_list = match.group().split("-")
+ return match_result_list[0], match_result_list[1]
+
+ # standard port without "<>-"
+ pattern = re.compile(r"^(\d+)$")
+ match = pattern.match(port)
+ if match:
+ # port stays unchanged
+ port_end = port
+ else: # Any
+ pattern = re.compile(r"^(Any)$")
+ match = pattern.match(port)
+ if match:
+ port = str(1)
+ port_end = str(65535)
+ else: # e.g. suspicious cases
+ port = None
+ port_end = None
+ return port, port_end
+
+
+def get_obj_domain_uid(obj: dict[str, Any]) -> str:
+ """
+ Returns the domain UID for the given object.
+ If the object has a 'domain' key with a 'uid', it returns that UID.
+ Otherwise, it returns the global domain UID.
+ """
+ if "domain" in obj and "uid" in obj["domain"]:
+ return obj["domain"]["uid"]
+ return "DUMMY" # TODO: set domain uid correctly (updatable objects?)
+
+
+# return name of nw_objects element where obj_uid = uid
+def resolve_svc_uid_to_name(uid: str, svc_objects: list[dict[str, Any]]) -> str:
+ for obj in svc_objects:
+ if obj["svc_uid"] == uid:
+ return obj["svc_name"]
+ raise FwoImporterErrorInconsistenciesError("Service object member uid " + uid + " not found")
+
+
+def add_member_names_for_svc_group(idx: int, svc_objects: list[dict[str, Any]]) -> None:
+ member_names = ""
+ group = svc_objects.pop(idx)
+
+ if "svc_member_refs" in group and group["svc_member_refs"] is not None:
+ svc_member_refs = group["svc_member_refs"].split(LIST_DELIMITER)
+ for ref in svc_member_refs:
+ member_name = resolve_svc_uid_to_name(ref, svc_objects)
+ member_names += member_name + LIST_DELIMITER
+ group["svc_member_names"] = member_names[:-1]
+
+ svc_objects.insert(idx, group)
+
+
+def normalize_service_objects(full_config: dict[str, Any], config2import: dict[str, Any], import_id: int) -> None:
+ svc_objects: list[dict[str, Any]] = []
+ for obj_dict in full_config["objects"]:
+ collect_svc_objects(obj_dict, svc_objects)
+ for obj in svc_objects:
+ obj.update({"control_id": import_id})
+ for idx in range(len(svc_objects) - 1):
+ if svc_objects[idx]["svc_typ"] == "group":
+ add_member_names_for_svc_group(idx, svc_objects)
+ config2import.update({"service_objects": svc_objects})
diff --git a/roles/importer/files/importer/fw_modules/checkpointR8x/cp_user.py b/roles/importer/files/importer/fw_modules/checkpointR8x/cp_user.py
new file mode 100644
index 0000000000..d0fb437585
--- /dev/null
+++ b/roles/importer/files/importer/fw_modules/checkpointR8x/cp_user.py
@@ -0,0 +1,119 @@
+import json
+from typing import Any
+
+from fwo_log import FWOLogger
+
+
+def extract_access_role_user(src: dict[str, Any]) -> tuple[str, str, str, str | None, str | None]:
+ """Extract user data from access-role type source object."""
+ user_name = src["name"]
+ user_uid = src["uid"]
+ user_typ = "group"
+ user_comment = src.get("comments")
+ user_color = src.get("color")
+ if "users" in src:
+ user_typ = "simple"
+ return user_name, user_uid, user_typ, user_comment, user_color
+
+
+def extract_legacy_user_at_location(src: dict[str, Any]) -> tuple[str, str | None, str, str | None, str | None]:
+ """Extract user data from LegacyUserAtLocation type source object."""
+ user_str = src["name"]
+ user_ar = user_str.split("@")
+ user_name = user_ar[0]
+ user_uid = src.get("userGroup")
+ user_typ = "group"
+ user_comment = src.get("comments")
+ user_color = src.get("color")
+ return user_name, user_uid, user_typ, user_comment, user_color
+
+
+def normalize_user_data(user_comment: str | None, user_color: str | None) -> tuple[str | None, str]:
+ """Normalize user comment and color values."""
+ if user_comment == "":
+ user_comment = None
+ if user_color is None:
+ user_color = "black"
+ return user_comment, user_color
+
+
+def process_typed_source_object(src: dict[str, Any], users: dict[str, Any]) -> None:
+ """Process a source object that has a type field."""
+ if src["type"] == "access-role":
+ user_name, user_uid, user_typ, user_comment, user_color = extract_access_role_user(src)
+ elif src["type"] == "LegacyUserAtLocation":
+ user_name, user_uid, user_typ, user_comment, user_color = extract_legacy_user_at_location(src)
+ else:
+ return
+
+ user_comment, user_color = normalize_user_data(user_comment, user_color)
+
+ users.update(
+ {
+ user_name: {
+ "user_uid": user_uid,
+ "user_typ": user_typ,
+ "user_comment": user_comment,
+ "user_color": user_color,
+ }
+ }
+ )
+
+
+def process_untyped_source_object(src: dict[str, Any], users: dict[str, Any]) -> None:
+ """Process a source object that lacks a type field."""
+ FWOLogger.warning("found src user without type field: " + json.dumps(src))
+ if "name" in src and "uid" in src:
+ users.update({src["name"]: {"user_uid": src["uid"], "user_typ": "simple"}})
+
+
+def process_standard_rule(rule: dict[str, Any], users: dict[str, Any]) -> None:
+ """Process a standard rule to extract user information."""
+ if "type" not in rule or rule["type"] == "place-holder":
+ return
+
+ for src in rule["source"]:
+ if "type" in src:
+ process_typed_source_object(src, users)
+ else:
+ process_untyped_source_object(src, users)
+
+
+def collect_users_from_rule(rule: dict[str, Any], users: dict[str, Any]) -> None:
+ """Collect user information from a single rule."""
+ if "rule-number" in rule: # standard rule
+ process_standard_rule(rule, users)
+ else: # section
+ collect_users_from_rulebase(rule["rulebase"], users)
+
+
+# collect_users writes user info into global users dict
+def collect_users_from_rulebase(rulebase: dict[str, Any], users: dict[str, Any]) -> None:
+ if "rulebase_chunks" in rulebase:
+ for chunk in rulebase["rulebase_chunks"]:
+ if "rulebase" in chunk:
+ for rule in chunk["rulebase"]:
+ collect_users_from_rule(rule, users)
+ else:
+ for rule in rulebase:
+ collect_users_from_rule(rule, users) # type: ignore #TODO: refactor this # noqa: PGH003
+
+
+# the following is only used within new python-only importer:
+def parse_user_objects_from_rulebase(rulebase: dict[str, Any], users: dict[str, Any], import_id: str) -> None:
+ collect_users_from_rulebase(rulebase, users)
+ for user_name, user_info in users.items():
+ # TODO: get user info via API
+ _ = get_user_uid_from_cp_api(user_name)
+ # finally add the import id
+ user_info["control_id"] = import_id
+
+
+def get_user_uid_from_cp_api(user_name: str) -> str:
+ # show-object with UID
+ # dummy implementation returning the name as uid
+ return user_name
+
+
+def normalize_users_legacy() -> None:
+ raise NotImplementedError
diff --git a/roles/importer/files/importer/azure2022ff/discovery_logging.conf b/roles/importer/files/importer/fw_modules/checkpointR8x/discovery_logging.conf
similarity index 100%
rename from roles/importer/files/importer/azure2022ff/discovery_logging.conf
rename to roles/importer/files/importer/fw_modules/checkpointR8x/discovery_logging.conf
diff --git a/roles/importer/files/importer/fw_modules/checkpointR8x/fwcommon.py b/roles/importer/files/importer/fw_modules/checkpointR8x/fwcommon.py
new file mode 100644
index 0000000000..617630d2b8
--- /dev/null
+++ b/roles/importer/files/importer/fw_modules/checkpointR8x/fwcommon.py
@@ -0,0 +1,737 @@
+import time
+from copy import deepcopy
+from typing import Any
+
+import fwo_const
+import fwo_globals
+from fw_modules.checkpointR8x import cp_const, cp_gateway, cp_getter, cp_network, cp_rule, cp_service
+from fwo_base import ConfigAction
+from fwo_exceptions import FwLoginFailedError, FwoImporterError, ImportInterruptionError
+from fwo_log import FWOLogger
+from model_controllers.fwconfigmanagerlist_controller import FwConfigManagerListController
+from model_controllers.import_state_controller import ImportStateController
+from model_controllers.management_controller import ManagementController
+from models.fw_common import FwCommon
+from models.fwconfig_normalized import FwConfigNormalized
+from models.fwconfigmanagerlist import FwConfigManager
+from models.import_state import ImportState
+from utils.conversion_utils import convert_list_to_dict
+
+
+class CheckpointR8xCommon(FwCommon):
+ def has_config_changed(
+ self, full_config: FwConfigManagerListController, import_state: ImportStateController, force: bool = False
+ ) -> bool:
+ if full_config: # a config was passed in (read from file), so we assume that an import has to be done (simulating changes here)
+ return True
+
+ session_id: str = cp_getter.login(import_state.mgm_details)
+
+ if import_state.last_successful_import is None or import_state.last_successful_import == "" or force:
+ # if no last import time found or given or if force flag is set, do full import
+ result = True
+ else: # otherwise search for any changes since last import
+ result = (
+ cp_getter.get_changes(
+ session_id,
+ import_state.mgm_details.hostname,
+ str(import_state.mgm_details.port),
+ import_state.last_successful_import,
+ )
+ != 0
+ )
+
+ cp_getter.logout(import_state.mgm_details.buildFwApiString(), session_id)
+
+ return result > 0
+
+ def get_config(
+ self, config_in: FwConfigManagerListController, import_state: ImportStateController
+ ) -> tuple[int, FwConfigManagerListController]:
+ return get_config(config_in, import_state)
+
+
+def get_config(
+ config_in: FwConfigManagerListController, import_state: ImportStateController
+) -> tuple[int, FwConfigManagerListController]:
+ FWOLogger.debug("starting checkpointR8x/get_config")
+
+ parsing_config_only = (
+ not config_in.has_empty_config()
+ ) # no native config was passed in, so getting it from FW-Manager
+
+ if not parsing_config_only: # get config from cp fw mgr
+ starttime = int(time.time())
+ initialize_native_config(config_in, import_state.state)
+
+ start_time_temp = int(time.time())
+ FWOLogger.debug("checkpointR8x/get_config/getting objects ...")
+
+ if config_in.native_config is None:
+ raise FwoImporterError("native_config is None in get_config")
+
+ # IMPORTANT: cp api is expected to preserve order of refs in group objects (unlike refs in rules, which are sorted later)
+ result_get_objects = get_objects(config_in.native_config, import_state.state)
+ if result_get_objects > 0:
+ raise FwLoginFailedError("checkpointR8x/get_config/error while gettings objects")
+ FWOLogger.debug("checkpointR8x/get_config/fetched objects in " + str(int(time.time()) - start_time_temp) + "s")
+
+ start_time_temp = int(time.time())
+ FWOLogger.debug("checkpointR8x/get_config/getting rules ...")
+ result_get_rules = get_rules(config_in.native_config, import_state.state)
+ if result_get_rules > 0:
+ raise FwLoginFailedError("checkpointR8x/get_config/error while gettings rules")
+ FWOLogger.debug("checkpointR8x/get_config/fetched rules in " + str(int(time.time()) - start_time_temp) + "s")
+
+ duration = int(time.time()) - starttime
+ FWOLogger.debug("checkpointR8x/get_config - fetch duration: " + str(duration) + "s")
+
+ if config_in.contains_only_native():
+ sid: str = cp_getter.login(import_state.state.mgm_details)
+ normalized_config = normalize_config(import_state.state, config_in, parsing_config_only, sid)
+ FWOLogger.info("completed getting config")
+ return 0, normalized_config
+ # we already have a native config (from file import)
+ return 0, config_in
+
+
+def initialize_native_config(config_in: FwConfigManagerListController, import_state: ImportState) -> None:
+ """
+ Create domain structure in nativeConfig
+ """
+ manager_details_list = create_ordered_manager_list(import_state)
+ if config_in.native_config is None:
+ raise FwoImporterError("native_config is None in initialize_native_config")
+ config_in.native_config.update({"domains": []})
+ for manager_details in manager_details_list:
+ config_in.native_config["domains"].append(
+ {
+ "domain_name": manager_details.domain_name,
+ "domain_uid": manager_details.domain_uid,
+ "is-super-manager": manager_details.is_super_manager,
+ "management_name": manager_details.name,
+ "management_uid": manager_details.uid,
+ "objects": [],
+ "rulebases": [],
+ "nat_rulebases": [],
+ "gateways": [],
+ }
+ )
+
+
+def normalize_config(
+ import_state: ImportState, config_in: FwConfigManagerListController, parsing_config_only: bool, sid: str
+) -> FwConfigManagerListController:
+ native_and_normalized_config_dict_list: list[dict[str, Any]] = []
+
+ if config_in.native_config is None:
+ raise FwoImporterError("Did not get a native config to normalize.")
+
+ if "domains" not in config_in.native_config:
+ FWOLogger.error("No domains found in native config. Cannot normalize config.")
+ raise FwoImporterError("No domains found in native config. Cannot normalize config.")
+
+ # in case of mds, first nativ config domain is global
+ is_global_loop_iteration = False
+ native_config_global: dict[str, Any] = {}
+ normalized_config_global = {}
+ if config_in.native_config["domains"][0]["is-super-manager"]:
+ native_config_global = config_in.native_config["domains"][0]
+ is_global_loop_iteration = True
+
+ for native_conf in config_in.native_config["domains"]:
+ normalized_config_dict = deepcopy(fwo_const.EMPTY_NORMALIZED_FW_CONFIG_JSON_DICT)
+ normalize_single_manager_config(
+ native_conf,
+ native_config_global,
+ normalized_config_dict,
+ normalized_config_global,
+ import_state,
+ parsing_config_only,
+ sid,
+ is_global_loop_iteration,
+ )
+
+ native_and_normalized_config_dict_list.append({"native": native_conf, "normalized": normalized_config_dict})
+
+ if is_global_loop_iteration:
+ normalized_config_global = normalized_config_dict
+ is_global_loop_iteration = False
+
+ for native_and_normalized_config_dict in native_and_normalized_config_dict_list:
+ normalized_config = FwConfigNormalized(
+ action=ConfigAction.INSERT,
+ network_objects=convert_list_to_dict(
+ native_and_normalized_config_dict["normalized"]["network_objects"], "obj_uid"
+ ),
+ service_objects=convert_list_to_dict(
+ native_and_normalized_config_dict["normalized"]["service_objects"], "svc_uid"
+ ),
+ zone_objects=convert_list_to_dict(
+ native_and_normalized_config_dict["normalized"]["zone_objects"], "zone_name"
+ ),
+ rulebases=native_and_normalized_config_dict["normalized"]["policies"],
+ gateways=native_and_normalized_config_dict["normalized"]["gateways"],
+ )
+ manager = FwConfigManager(
+ manager_name=native_and_normalized_config_dict["native"]["management_name"],
+ manager_uid=native_and_normalized_config_dict["native"]["management_uid"],
+ is_super_manager=native_and_normalized_config_dict["native"]["is-super-manager"],
+ sub_manager_ids=[],
+ domain_name=native_and_normalized_config_dict["native"]["domain_name"],
+ domain_uid=native_and_normalized_config_dict["native"]["domain_uid"],
+ configs=[normalized_config],
+ )
+ config_in.ManagerSet.append(manager)
+
+ return config_in
+
+
+def normalize_single_manager_config(
+ native_config: dict[str, Any],
+ native_config_global: dict[str, Any],
+ normalized_config_dict: dict[str, Any],
+ normalized_config_global: dict[str, Any],
+ import_state: ImportState,
+ parsing_config_only: bool,
+ sid: str,
+ is_global_loop_iteration: bool,
+):
+ cp_network.normalize_network_objects(
+ native_config, normalized_config_dict, import_state.import_id, mgm_id=import_state.mgm_details.mgm_id
+ )
+ FWOLogger.info("completed normalizing network objects")
+ cp_service.normalize_service_objects(native_config, normalized_config_dict, import_state.import_id)
+ FWOLogger.info("completed normalizing service objects")
+ cp_gateway.normalize_gateways(native_config, import_state, normalized_config_dict)
+ cp_rule.normalize_rulebases(
+ native_config,
+ native_config_global,
+ import_state,
+ normalized_config_dict,
+ normalized_config_global,
+ is_global_loop_iteration,
+ )
+ if not parsing_config_only: # get config from cp fw mgr
+ cp_getter.logout(import_state.mgm_details.build_fw_api_string(), sid)
+ FWOLogger.info("completed normalizing rulebases")
+
+
+def get_rules(native_config: dict[str, Any], import_state: ImportState) -> int:
+ """
+ Main function to get rules. Divided into smaller sub-tasks for better readability and maintainability.
+ """
+ show_params_policy_structure: dict[str, Any] = {
+ "limit": import_state.fwo_config.api_fetch_size,
+ "details-level": "full",
+ }
+
+ global_assignments, global_policy_structure, global_domain, global_sid = None, None, None, None
+ manager_details_list = create_ordered_manager_list(import_state)
+ for manager_index, manager_details in enumerate(manager_details_list):
+ cp_manager_api_base_url = import_state.mgm_details.build_fw_api_string()
+
+ if manager_details.is_super_manager:
+ global_assignments, global_policy_structure, global_domain, global_sid = handle_super_manager(
+ manager_details, cp_manager_api_base_url, show_params_policy_structure
+ )
+
+ sid: str = cp_getter.login(manager_details)
+ policy_structure: list[dict[str, Any]] = []
+ cp_getter.get_policy_structure(
+ cp_manager_api_base_url,
+ sid,
+ show_params_policy_structure,
+ manager_details,
+ policy_structure=policy_structure,
+ )
+
+ process_devices(
+ manager_details,
+ policy_structure,
+ global_assignments,
+ global_policy_structure,
+ global_domain,
+ global_sid,
+ cp_manager_api_base_url,
+ sid,
+ native_config["domains"][
+ manager_index
+ ], # globalSid should not be None but is when the first manager is not supermanager
+ native_config["domains"][0],
+ import_state,
+ )
+ native_config["domains"][manager_index].update({"policies": policy_structure})
+
+ return 0
+
+
+def create_ordered_manager_list(import_state: ImportState) -> list[ManagementController]:
+ """
+ Creates list of manager details, supermanager is first
+ """
+ manager_details_list: list[ManagementController] = [deepcopy(import_state.mgm_details)]
+ if import_state.mgm_details.is_super_manager:
+ manager_details_list.extend([deepcopy(sub_manager) for sub_manager in import_state.mgm_details.sub_managers]) # type: ignore TODO: why we are adding submanagers as ManagementController?
+ return manager_details_list
+
+
+def handle_super_manager(
+ manager_details: ManagementController, cp_manager_api_base_url: str, show_params_policy_structure: dict[str, Any]
+) -> tuple[list[Any], None, Any | None, str]:
+ # global assignments are fetched from mds domain
+ mds_sid: str = cp_getter.login(manager_details)
+ global_policy_structure = None
+ global_domain = None
+ global_assignments = cp_getter.get_global_assignments(
+ cp_manager_api_base_url, mds_sid, show_params_policy_structure
+ )
+ global_sid = ""
+ # import global policies if at least one global assignment exists
+
+ if len(global_assignments) > 0:
+ if "global-domain" in global_assignments[0] and "uid" in global_assignments[0]["global-domain"]:
+ global_domain = global_assignments[0]["global-domain"]["uid"]
+
+ # policy structure is fetched from global domain
+ manager_details.domain_uid = global_domain
+ global_sid: str = cp_getter.login(manager_details)
+ cp_getter.get_policy_structure(
+ cp_manager_api_base_url,
+ global_sid,
+ show_params_policy_structure,
+ manager_details,
+ policy_structure=global_policy_structure,
+ )
+ else:
+ raise FwoImporterError(f"Unexpected global assignments: {global_assignments!s}")
+
+ return global_assignments, global_policy_structure, global_domain, global_sid
+
+
+def process_devices(
+ manager_details: ManagementController,
+ policy_structure: list[dict[str, Any]],
+ global_assignments: list[Any] | None,
+ global_policy_structure: list[dict[str, Any]] | None,
+ global_domain: str | None,
+ global_sid: str | None,
+ cp_manager_api_base_url: str,
+ sid: str,
+ native_config_domain: dict[str, Any],
+ native_config_global_domain: dict[str, Any],
+ import_state: ImportState,
+) -> None:
+ for device in manager_details.devices:
+ device_config: dict[str, Any] = initialize_device_config(device)
+ if not device_config:
+ continue
+
+ ordered_layer_uids: list[str] = get_ordered_layer_uids(
+ policy_structure, device_config, manager_details.get_domain_string()
+ )
+ if not ordered_layer_uids:
+ FWOLogger.warning(f"No ordered layers found for device: {device_config['name']}")
+ native_config_domain["gateways"].append(device_config)
+ continue
+
+ global_ordered_layer_count = 0
+ if import_state.mgm_details.is_super_manager:
+ global_ordered_layer_count = handle_global_rulebase_links(
+ manager_details,
+ import_state,
+ device_config,
+ global_assignments,
+ global_policy_structure,
+ global_domain,
+ global_sid,
+ ordered_layer_uids,
+ native_config_global_domain,
+ cp_manager_api_base_url,
+ )
+ else:
+ define_initial_rulebase(device_config, ordered_layer_uids, is_global=False)
+
+ add_ordered_layers_to_native_config(
+ ordered_layer_uids,
+ get_rules_params(import_state),
+ cp_manager_api_base_url,
+ sid,
+ native_config_domain,
+ device_config,
+ is_global=False,
+ global_ordered_layer_count=global_ordered_layer_count,
+ )
+
+ handle_nat_rules(device, native_config_domain, sid, import_state)
+
+ native_config_domain["gateways"].append(device_config)
+
+
+def initialize_device_config(device: dict[str, Any]) -> dict[str, Any]:
+ if "name" in device and "uid" in device:
+ return {"name": device["name"], "uid": device["uid"], "rulebase_links": []}
+ raise FwoImporterError(f"Device missing name or uid: {device}")
+
+
+def handle_global_rulebase_links(
+ manager_details: ManagementController,
+ import_state: ImportState,
+ device_config: dict[str, Any],
+ global_assignments: list[Any] | None,
+ global_policy_structure: list[dict[str, Any]] | None,
+ global_domain: str | None,
+ global_sid: str | None,
+ ordered_layer_uids: list[str],
+ native_config_global_domain: dict[str, Any],
+ cp_manager_api_base_url: str,
+) -> int:
+ """
+ Searches for global access policy for current device policy,
+ adds global ordered layers and defines global rulebase link
+ """
+ if global_assignments is None:
+ raise FwoImporterError("Global assignments is None in handle_global_rulebase_links")
+
+ if global_policy_structure is None:
+ raise FwoImporterError("Global policy structure is None in handle_global_rulebase_links")
+
+ for global_assignment in global_assignments:
+ if global_assignment["dependent-domain"]["uid"] != manager_details.get_domain_string():
+ continue
+ for global_policy in global_policy_structure:
+ if global_policy["name"] == global_assignment["global-access-policy"]:
+ global_ordered_layer_uids = get_ordered_layer_uids([global_policy], device_config, global_domain)
+ if not global_ordered_layer_uids:
+ FWOLogger.warning(f"No access layer for global policy: {global_policy['name']}")
+ break
+
+ global_ordered_layer_count = len(global_ordered_layer_uids)
+ global_policy_rulebases_uid_list = add_ordered_layers_to_native_config(
+ global_ordered_layer_uids,
+ get_rules_params(import_state),
+ cp_manager_api_base_url,
+ global_sid,
+ native_config_global_domain,
+ device_config,
+ is_global=True,
+ global_ordered_layer_count=global_ordered_layer_count,
+ )
+ define_global_rulebase_link(
+ device_config,
+ global_ordered_layer_uids,
+ ordered_layer_uids,
+ native_config_global_domain,
+ global_policy_rulebases_uid_list,
+ )
+
+ return global_ordered_layer_count
+
+ return 0
+
+
+def define_global_rulebase_link(
+ device_config: dict[str, Any],
+ global_ordered_layer_uids: list[str],
+ ordered_layer_uids: list[str],
+ native_config_global_domain: dict[str, Any],
+ global_policy_rulebases_uid_list: list[str],
+):
+ """
+ Links initial and placeholder rule for global rulebases
+ """
+ define_initial_rulebase(device_config, global_ordered_layer_uids, is_global=True)
+
+ # parse global rulebases, find place-holders and link local rulebases
+ placeholder_link_index = 0
+ for global_rulebase_uid in global_policy_rulebases_uid_list:
+ placeholder_rule_uid = ""
+ for rulebase in native_config_global_domain["rulebases"]:
+ if rulebase["uid"] == global_rulebase_uid:
+ placeholder_rule_uid, placeholder_rulebase_uid = cp_getter.get_placeholder_in_rulebase(rulebase)
+
+ if placeholder_rule_uid:
+ ordered_layer_uid = ""
+ # we might find more than one placeholder, may be unequal to number of domain ordered layers
+ if len(ordered_layer_uids) > placeholder_link_index:
+ ordered_layer_uid = ordered_layer_uids[placeholder_link_index]
+
+ device_config["rulebase_links"].append(
+ {
+ "from_rulebase_uid": placeholder_rulebase_uid,
+ "from_rule_uid": None,
+ "to_rulebase_uid": ordered_layer_uid,
+ "type": "domain",
+ "is_global": False,
+ "is_initial": False,
+ "is_section": False,
+ }
+ )
+
+ placeholder_link_index += 1
+
+
+def define_initial_rulebase(device_config: dict[str, Any], ordered_layer_uids: list[str], is_global: bool):
+ device_config["rulebase_links"].append(
+ {
+ "from_rulebase_uid": None,
+ "from_rule_uid": None,
+ "to_rulebase_uid": ordered_layer_uids[0],
+ "type": "ordered",
+ "is_global": is_global,
+ "is_initial": True,
+ "is_section": False,
+ }
+ )
+
+
+def get_rules_params(import_state: ImportState) -> dict[str, Any]:
+ return {
+ "limit": import_state.fwo_config.api_fetch_size,
+ "use-object-dictionary": cp_const.use_object_dictionary,
+ "details-level": "standard",
+ "show-hits": cp_const.with_hits,
+ }
+
+
+def handle_nat_rules(device: dict[str, Any], native_config_domain: dict[str, Any], sid: str, import_state: ImportState):
+ if device.get("package_name"):
+ show_params_rules: dict[str, Any] = {
+ "limit": import_state.fwo_config.api_fetch_size,
+ "use-object-dictionary": cp_const.use_object_dictionary,
+ "details-level": "standard",
+ "package": device["package_name"],
+ }
+ FWOLogger.debug(f"Getting NAT rules for package: {device['package_name']}", 4)
+ nat_rules = cp_getter.get_nat_rules_from_api_as_dict(
+ import_state.mgm_details.build_fw_api_string(),
+ sid,
+ show_params_rules,
+ native_config_domain=native_config_domain,
+ )
+ if nat_rules:
+ native_config_domain["nat_rulebases"].append(nat_rules)
+ else:
+ native_config_domain["nat_rulebases"].append({"nat_rule_chunks": []})
+ else:
+ native_config_domain["nat_rulebases"].append({"nat_rule_chunks": []})
+
+
+def add_ordered_layers_to_native_config(
+ ordered_layer_uids: list[str],
+ show_params_rules: dict[str, Any],
+ cp_manager_api_base_url: str,
+ sid: str | None,
+ native_config_domain: dict[str, Any],
+ device_config: dict[str, Any],
+ is_global: bool,
+ global_ordered_layer_count: int,
+) -> list[str]:
+ """
+ Fetches ordered layers and links them
+ """
+ policy_rulebases_uid_list = []
+ for ordered_layer_index, ordered_layer_uid in enumerate(ordered_layer_uids):
+ show_params_rules.update({"uid": ordered_layer_uid})
+
+ policy_rulebases_uid_list = cp_getter.get_rulebases(
+ cp_manager_api_base_url,
+ sid,
+ show_params_rules,
+ native_config_domain,
+ device_config,
+ policy_rulebases_uid_list,
+ is_global=is_global,
+ access_type="access",
+ rulebase_uid=ordered_layer_uid,
+ )
+
+ # link to next ordered layer
+ # in case of mds: domain ordered layers are linked once there is no global ordered layer counterpart
+ if (is_global or ordered_layer_index >= global_ordered_layer_count - 1) and (
+ ordered_layer_index < len(ordered_layer_uids) - 1
+ ):
+ device_config["rulebase_links"].append(
+ {
+ "from_rulebase_uid": ordered_layer_uid,
+ "from_rule_uid": None,
+ "to_rulebase_uid": ordered_layer_uids[ordered_layer_index + 1],
+ "type": "ordered",
+ "is_global": is_global,
+ "is_initial": False,
+ "is_section": False,
+ }
+ )
+
+ return policy_rulebases_uid_list
+
+
+def get_ordered_layer_uids(
+ policy_structure: list[dict[str, Any]], device_config: dict[str, Any], domain: str | None
+) -> list[str]:
+ """
+ Get UIDs of ordered layers for policy of device
+ """
+ ordered_layer_uids: list[str] = []
+ for policy in policy_structure:
+ found_target_in_policy = False
+ for target in policy["targets"]:
+ if target["uid"] == device_config["uid"] or target["uid"] == "all":
+ found_target_in_policy = True
+ if found_target_in_policy:
+ append_access_layer_uid(policy, domain, ordered_layer_uids)
+
+ return ordered_layer_uids
+
+
+def append_access_layer_uid(policy: dict[str, Any], domain: str | None, ordered_layer_uids: list[str]) -> None:
+ ordered_layer_uids.extend(
+ [
+ access_layer["uid"]
+ for access_layer in policy["access-layers"]
+ if access_layer["domain"] == domain or domain == ""
+ ]
+ )
+
+
+def get_objects(native_config_dict: dict[str, Any], import_state: ImportState) -> int:
+ show_params_objs = {"limit": import_state.fwo_config.api_fetch_size}
+ manager_details_list = create_ordered_manager_list(import_state)
+
+ # loop over sub-managers in case of mds
+ manager_index = 0
+ for manager_details in manager_details_list:
+ if manager_details.import_disabled and not import_state.force_import:
+ continue
+
+ is_stand_alone_manager = len(manager_details_list) == 1
+ if manager_details.is_super_manager or is_stand_alone_manager:
+ obj_type_array = cp_const.api_obj_types
+ else:
+ obj_type_array = cp_const.local_api_obj_types
+
+ if manager_details.is_super_manager:
+ # for super managers we need to get both the global domain data and the Check Point Data (perdefined objects)
+
+ # Check Point Data (perdefined objects)
+ manager_details.domain_name = ""
+ manager_details.domain_uid = "" # Check Point Data
+ get_objects_per_domain(
+ manager_details,
+ native_config_dict["domains"][0],
+ obj_type_array,
+ show_params_objs,
+ is_stand_alone_manager=is_stand_alone_manager,
+ )
+
+ # global domain containing the manually added global objects
+ manager_details.domain_name = "Global"
+ manager_details.domain_uid = "Global"
+ get_objects_per_domain(
+ manager_details,
+ native_config_dict["domains"][0],
+ obj_type_array,
+ show_params_objs,
+ is_stand_alone_manager=is_stand_alone_manager,
+ )
+ else:
+ get_objects_per_domain(
+ manager_details,
+ native_config_dict["domains"][manager_index],
+ obj_type_array,
+ show_params_objs,
+ is_stand_alone_manager=is_stand_alone_manager,
+ )
+
+ manager_index += 1
+ return 0
+
+
+def get_objects_per_domain(
+ manager_details: ManagementController,
+ native_domain: dict[str, Any],
+ obj_type_array: list[str],
+ show_params_objs: dict[str, Any],
+ is_stand_alone_manager: bool = True,
+) -> None:
+ sid = cp_getter.login(manager_details)
+ cp_url = manager_details.build_fw_api_string()
+ for obj_type in obj_type_array:
+ object_table = get_objects_per_type(obj_type, show_params_objs, sid, cp_url)
+ add_special_objects_to_global_domain(object_table, obj_type, sid, cp_api_url=cp_url)
+ if not is_stand_alone_manager and not manager_details.is_super_manager:
+ remove_predefined_objects_for_domains(object_table)
+ native_domain["objects"].append(object_table)
+
+
+def remove_predefined_objects_for_domains(object_table: dict[str, Any]) -> None:
+ if (
+ "chunks" in object_table
+ and "type" in object_table
+ and object_table["type"] in cp_const.types_to_remove_globals_from
+ ):
+ return
+
+ for chunk in object_table["chunks"]:
+ if "objects" in chunk:
+ for obj in chunk["objects"]:
+ domain_type = obj.get("domain", {}).get("domain-type", "")
+ if domain_type != "domain":
+ chunk["objects"].remove(obj)
+
+
+def get_objects_per_type(
+ obj_type: str, show_params_objs: dict[str, Any], sid: str, cp_manager_api_base_url: str
+) -> dict[str, Any]:
+ if fwo_globals.shutdown_requested:
+ raise ImportInterruptionError("Shutdown requested during object retrieval.")
+ if obj_type in cp_const.obj_types_full_fetch_needed:
+ show_params_objs.update({"details-level": cp_const.details_level_group_objects})
+ else:
+ show_params_objs.update({"details-level": cp_const.details_level_objects})
+ object_table: dict[str, Any] = {"type": obj_type, "chunks": []}
+ current = 0
+ total = current + 1
+ show_cmd = "show-" + obj_type
+ FWOLogger.debug("obj_type: " + obj_type, 6)
+
+ while current < total:
+ show_params_objs["offset"] = current
+ objects = cp_getter.cp_api_call(cp_manager_api_base_url, show_cmd, show_params_objs, sid)
+ if fwo_globals.shutdown_requested:
+ raise ImportInterruptionError("Shutdown requested during object retrieval.")
+
+ object_table["chunks"].append(objects)
+ if "total" in objects and "to" in objects:
+ total = objects["total"]
+ current = objects["to"]
+ FWOLogger.debug(obj_type + " current:" + str(current) + " of a total " + str(total), 6)
+ else:
+ current = total
+
+ return object_table
+
+
+def add_special_objects_to_global_domain(
+ object_table: dict[str, Any], obj_type: str, sid: str, cp_api_url: str
+) -> None:
+ """
+ Appends special objects Original, Any, None and Internet to global domain
+ """
+ # getting Original (NAT) object (both for networks and services)
+ orig_obj = cp_getter.get_object_details_from_api(cp_const.original_obj_uid, sid=sid, apiurl=cp_api_url)["chunks"][0]
+ any_obj = cp_getter.get_object_details_from_api(cp_const.any_obj_uid, sid=sid, apiurl=cp_api_url)["chunks"][0]
+ none_obj = cp_getter.get_object_details_from_api(cp_const.none_obj_uid, sid=sid, apiurl=cp_api_url)["chunks"][0]
+ internet_obj = cp_getter.get_object_details_from_api(cp_const.internet_obj_uid, sid=sid, apiurl=cp_api_url)[
+ "chunks"
+ ][0]
+
+ if obj_type == "networks":
+ object_table["chunks"].append(orig_obj)
+ object_table["chunks"].append(any_obj)
+ object_table["chunks"].append(none_obj)
+ object_table["chunks"].append(internet_obj)
+ if obj_type == "services-other":
+ object_table["chunks"].append(orig_obj)
+ object_table["chunks"].append(any_obj)
+ object_table["chunks"].append(none_obj)
diff --git a/roles/importer/files/importer/ciscofirepowerdomain7ff/__init__.py b/roles/importer/files/importer/fw_modules/ciscoasa9/__init__.py
similarity index 100%
rename from roles/importer/files/importer/ciscofirepowerdomain7ff/__init__.py
rename to roles/importer/files/importer/fw_modules/ciscoasa9/__init__.py
diff --git a/roles/importer/files/importer/fw_modules/ciscoasa9/asa_maps.py b/roles/importer/files/importer/fw_modules/ciscoasa9/asa_maps.py
new file mode 100644
index 0000000000..3a458011c3
--- /dev/null
+++ b/roles/importer/files/importer/fw_modules/ciscoasa9/asa_maps.py
@@ -0,0 +1,129 @@
+from typing import Any
+
+name_to_port: dict[str, dict[str, Any]] = {
+ "aol": {"port": 5190, "protocols": ["TCP"], "description": "America Online"},
+ "bgp": {"port": 179, "protocols": ["TCP"], "description": "Border Gateway Protocol, RFC 1163"},
+ "biff": {
+ "port": 512,
+ "protocols": ["UDP"],
+ "description": "Used by mail system to notify users that new mail is received",
+ },
+ "bootpc": {"port": 68, "protocols": ["UDP"], "description": "Bootstrap Protocol Client"},
+ "bootps": {"port": 67, "protocols": ["UDP"], "description": "Bootstrap Protocol Server"},
+ "chargen": {"port": 19, "protocols": ["TCP"], "description": "Character Generator"},
+ "cifs": {"port": 3020, "protocols": ["TCP", "UDP"], "description": "Common Internet File System"},
+ "citrix-ica": {
+ "port": 1494,
+ "protocols": ["TCP"],
+ "description": "Citrix Independent Computing Architecture (ICA) protocol",
+ },
+ "cmd": {
+ "port": 514,
+ "protocols": ["TCP"],
+ "description": "Similar to exec except that cmd has automatic authentication",
+ },
+ "ctiqbe": {"port": 2748, "protocols": ["TCP"], "description": "Computer Telephony Interface Quick Buffer Encoding"},
+ "daytime": {"port": 13, "protocols": ["TCP"], "description": "Day time, RFC 867"},
+ "discard": {"port": 9, "protocols": ["TCP", "UDP"], "description": "Discard"},
+ "dnsix": {"port": 195, "protocols": ["UDP"], "description": "DNSIX Session Management Module Audit Redirector"},
+ "domain": {"port": 53, "protocols": ["TCP", "UDP"], "description": "DNS"},
+ "echo": {"port": 7, "protocols": ["TCP", "UDP"], "description": "Echo"},
+ "exec": {"port": 512, "protocols": ["TCP"], "description": "Remote process execution"},
+ "finger": {"port": 79, "protocols": ["TCP"], "description": "Finger"},
+ "ftp": {"port": 21, "protocols": ["TCP"], "description": "File Transfer Protocol (control port)"},
+ "ftp-data": {"port": 20, "protocols": ["TCP"], "description": "File Transfer Protocol (data port)"},
+ "gopher": {"port": 70, "protocols": ["TCP"], "description": "Gopher"},
+ "h323": {"port": 1720, "protocols": ["TCP"], "description": "H.323 call signaling"},
+ "hostname": {"port": 101, "protocols": ["TCP"], "description": "NIC Host Name Server"},
+ "http": {"port": 80, "protocols": ["TCP", "UDP"], "description": "World Wide Web HTTP"},
+ "https": {"port": 443, "protocols": ["TCP"], "description": "HTTP over SSL"},
+ "ident": {"port": 113, "protocols": ["TCP"], "description": "Ident authentication service"},
+ "imap4": {"port": 143, "protocols": ["TCP"], "description": "Internet Message Access Protocol, version 4"},
+ "irc": {"port": 194, "protocols": ["TCP"], "description": "Internet Relay Chat protocol"},
+ "isakmp": {
+ "port": 500,
+ "protocols": ["UDP"],
+ "description": "Internet Security Association and Key Management Protocol",
+ },
+ "kerberos": {"port": 750, "protocols": ["TCP", "UDP"], "description": "Kerberos"},
+ "klogin": {"port": 543, "protocols": ["TCP"], "description": "KLOGIN"},
+ "kshell": {"port": 544, "protocols": ["TCP"], "description": "Korn Shell"},
+ "ldap": {"port": 389, "protocols": ["TCP"], "description": "Lightweight Directory Access Protocol"},
+ "ldaps": {"port": 636, "protocols": ["TCP"], "description": "Lightweight Directory Access Protocol (SSL)"},
+ "login": {"port": 513, "protocols": ["TCP"], "description": "Remote login"},
+ "lotusnotes": {"port": 1352, "protocols": ["TCP"], "description": "IBM Lotus Notes"},
+ "lpd": {"port": 515, "protocols": ["TCP"], "description": "Line Printer Daemon - printer spooler"},
+ "mobile-ip": {"port": 434, "protocols": ["UDP"], "description": "Mobile IP-Agent"},
+ "nameserver": {"port": 42, "protocols": ["UDP"], "description": "Host Name Server"},
+ "netbios-dgm": {"port": 138, "protocols": ["UDP"], "description": "NetBIOS Datagram Service"},
+ "netbios-ns": {"port": 137, "protocols": ["UDP"], "description": "NetBIOS Name Service"},
+ "netbios-ssn": {"port": 139, "protocols": ["TCP"], "description": "NetBIOS Session Service"},
+ "nfs": {"port": 2049, "protocols": ["TCP", "UDP"], "description": "Network File System - Sun Microsystems"},
+ "nntp": {"port": 119, "protocols": ["TCP"], "description": "Network News Transfer Protocol"},
+ "ntp": {"port": 123, "protocols": ["UDP"], "description": "Network Time Protocol"},
+ "pcanywhere-data": {"port": 5631, "protocols": ["TCP"], "description": "pcAnywhere data"},
+ "pcanywhere-status": {"port": 5632, "protocols": ["UDP"], "description": "pcAnywhere status"},
+ "pim-auto-rp": {
+ "port": 496,
+ "protocols": ["TCP", "UDP"],
+ "description": "Protocol Independent Multicast, reverse path flooding, dense mode",
+ },
+ "pop2": {"port": 109, "protocols": ["TCP"], "description": "Post Office Protocol - Version 2"},
+ "pop3": {"port": 110, "protocols": ["TCP"], "description": "Post Office Protocol - Version 3"},
+ "pptp": {"port": 1723, "protocols": ["TCP"], "description": "Point-to-Point Tunneling Protocol"},
+ "radius": {"port": 1645, "protocols": ["UDP"], "description": "Remote Authentication Dial-In User Service"},
+ "radius-acct": {
+ "port": 1646,
+ "protocols": ["UDP"],
+ "description": "Remote Authentication Dial-In User Service (accounting)",
+ },
+ "rip": {"port": 520, "protocols": ["UDP"], "description": "Routing Information Protocol"},
+ "rsh": {"port": 514, "protocols": ["TCP"], "description": "Remote Shell"},
+ "rtsp": {"port": 554, "protocols": ["TCP"], "description": "Real Time Streaming Protocol"},
+ "secureid-udp": {"port": 5510, "protocols": ["UDP"], "description": "SecureID over UDP"},
+ "sip": {"port": 5060, "protocols": ["TCP", "UDP"], "description": "Session Initiation Protocol"},
+ "smtp": {"port": 25, "protocols": ["TCP"], "description": "Simple Mail Transport Protocol"},
+ "snmp": {"port": 161, "protocols": ["UDP"], "description": "Simple Network Management Protocol"},
+ "snmptrap": {"port": 162, "protocols": ["UDP"], "description": "Simple Network Management Protocol - Trap"},
+ "sqlnet": {"port": 1521, "protocols": ["TCP"], "description": "Structured Query Language Network"},
+ "ssh": {"port": 22, "protocols": ["TCP"], "description": "Secure Shell"},
+ "sunrpc": {"port": 111, "protocols": ["TCP", "UDP"], "description": "Sun Remote Procedure Call"},
+ "syslog": {"port": 514, "protocols": ["UDP"], "description": "System Log"},
+ "tacacs": {
+ "port": 49,
+ "protocols": ["TCP", "UDP"],
+ "description": "Terminal Access Controller Access Control System Plus",
+ },
+ "talk": {"port": 517, "protocols": ["TCP", "UDP"], "description": "Talk"},
+ "telnet": {"port": 23, "protocols": ["TCP"], "description": "RFC 854 Telnet"},
+ "tftp": {"port": 69, "protocols": ["UDP"], "description": "Trivial File Transfer Protocol"},
+ "time": {"port": 37, "protocols": ["UDP"], "description": "Time"},
+ "uucp": {"port": 540, "protocols": ["TCP"], "description": "UNIX-to-UNIX Copy Program"},
+ "vxlan": {"port": 4789, "protocols": ["UDP"], "description": "Virtual eXtensible Local Area Network (VXLAN)"},
+ "who": {"port": 513, "protocols": ["UDP"], "description": "Who"},
+ "whois": {"port": 43, "protocols": ["TCP"], "description": "Who Is"},
+ "www": {"port": 80, "protocols": ["TCP", "UDP"], "description": "World Wide Web"},
+ "xdmcp": {"port": 177, "protocols": ["UDP"], "description": "X Display Manager Control Protocol"},
+}
+
+protocol_map = {
+ "ah": 51,
+ "eigrp": 88,
+ "esp": 50,
+ "gre": 47,
+ "icmp": 1,
+ "icmp6": 58,
+ "igmp": 2,
+ "igrp": 9,
+ "ipinip": 4,
+ "ipsec": 50,
+ "nos": 94,
+ "ospf": 89,
+ "pcp": 108,
+ "pim": 103,
+ "pptp": 47,
+ "sctp": 132,
+ "snp": 77,
+ "tcp": 6,
+ "udp": 17,
+}
diff --git a/roles/importer/files/importer/fw_modules/ciscoasa9/asa_models.py b/roles/importer/files/importer/fw_modules/ciscoasa9/asa_models.py
new file mode 100644
index 0000000000..59a7a559df
--- /dev/null
+++ b/roles/importer/files/importer/fw_modules/ciscoasa9/asa_models.py
@@ -0,0 +1,198 @@
+from __future__ import annotations
+
+from typing import Literal
+
+from pydantic import BaseModel
+
+
+class AsaEnablePassword(BaseModel):
+ password: str
+ encryption_function: str
+
+
+class AsaServiceModule(BaseModel):
+ name: str
+ keepalive_timeout: int
+ keepalive_counter: int
+
+
+class Names(BaseModel):
+ name: str
+ ip_address: str
+ description: str | None = None
+
+
+class Interface(BaseModel):
+ name: str
+ nameif: str
+ bridge_group: str | None = None
+ security_level: int
+ ip_address: str | None = None
+ subnet_mask: str | None = None
+ additional_settings: list[str]
+ description: str | None = None
+
+
+class AsaNetworkObject(BaseModel):
+ name: str
+ ip_address: str
+ ip_address_end: str | None = None # for range objects
+ subnet_mask: str | None = None
+ fqdn: str | None = None
+ description: str | None = None
+
+
+class AsaNetworkObjectGroup(BaseModel):
+ name: str
+ objects: list[AsaNetworkObjectGroupMember]
+ description: str | None = None
+
+
+class AsaNetworkObjectGroupMember(BaseModel):
+ kind: Literal["object", "object-group", "host", "hostv6", "subnet", "subnetv6"]
+ value: str
+ mask: str | None = None
+
+
+class AsaServiceObject(BaseModel):
+ name: str
+ protocol: Literal["tcp", "udp", "ip", "tcp-udp", "icmp", "gre"]
+ dst_port_eq: str | None = None
+ dst_port_range: tuple[str, str] | None = None
+ description: str | None = None
+
+
+class AsaServiceObjectGroup(BaseModel):
+ name: str
+ proto_mode: Literal["tcp", "udp", "tcp-udp"] | None
+ ports_eq: dict[str, list[str]] # protocol -> list of ports
+ ports_range: dict[str, list[tuple[str, str]]] # protocol -> list of (start_port, end_port)
+ nested_refs: list[str]
+ protocols: list[str]
+ description: str | None
+
+
+class AsaProtocolGroup(BaseModel):
+ name: str
+ protocols: list[str]
+ description: str | None = None
+
+
+class EndpointKind(BaseModel):
+ kind: Literal[
+ "any",
+ "host",
+ "subnet",
+ "object",
+ "object-group",
+ "service",
+ "protocol-group",
+ "protocol",
+ "eq",
+ "range",
+ "service-group",
+ ]
+ value: str
+ mask: str | None = None
+
+
+class AccessListEntry(BaseModel):
+ acl_name: str
+ action: Literal["permit", "deny"]
+ protocol: EndpointKind # Changed to use EndpointKind for kind and value
+ src: EndpointKind
+ dst: EndpointKind
+ dst_port: EndpointKind # Changed to use EndpointKind for kind and value
+ inactive: bool = False # Added field for inactive flag
+ description: str | None = None
+
+
+class AccessList(BaseModel):
+ name: str
+ entries: list[AccessListEntry]
+
+
+class AccessGroupBinding(BaseModel):
+ acl_name: str
+ direction: Literal["in", "out"]
+ interface: str
+
+
+class NatRule(BaseModel):
+ object_name: str
+ src_if: str
+ dst_if: str
+ nat_type: Literal["dynamic", "static"] = "dynamic"
+ translated_object: str | None = None
+
+
+class Route(BaseModel):
+ interface: str
+ destination: str
+ netmask: str
+ next_hop: str
+ distance: int | None = None
+
+
+class MgmtAccessRule(BaseModel):
+ protocol: Literal["http", "ssh", "telnet"]
+ source_ip: str
+ source_mask: str
+ interface: str
+
+
+class ClassMap(BaseModel):
+ name: str
+ matches: list[str] = [] # e.g., ["default-inspection-traffic"]
+
+
+class DnsInspectParameters(BaseModel):
+ message_length_max_client: Literal["auto", "default"] | int | None = None
+ message_length_max: int | None = None
+ tcp_inspection: bool = True # "no tcp-inspection" -> False
+
+
+class InspectionAction(BaseModel):
+ protocol: str # e.g., "dns", "ftp"
+ policy_map: str | None = None # e.g., "preset_dns_map" after "inspect dns preset_dns_map"
+
+
+class PolicyClass(BaseModel):
+ class_name: str # e.g., "inspection_default"
+ inspections: list[InspectionAction] = []
+
+
+class PolicyMap(BaseModel):
+ name: str # e.g., "global_policy" or "preset_dns_map"
+ type_str: str | None = None # e.g., "inspect dns" for typed maps
+ parameters_dns: DnsInspectParameters | None = None
+ classes: list[PolicyClass] = []
+
+
+class ServicePolicyBinding(BaseModel):
+ policy_map: str # e.g., "global_policy"
+ scope: Literal["global", "interface"] = "global"
+ interface: str | None = None
+
+
+class Config(BaseModel):
+ asa_version: str
+ hostname: str
+ enable_password: AsaEnablePassword
+ service_modules: list[AsaServiceModule]
+ additional_settings: list[str]
+ interfaces: list[Interface]
+ objects: list[AsaNetworkObject]
+ object_groups: list[AsaNetworkObjectGroup]
+ service_objects: list[AsaServiceObject] = []
+ service_object_groups: list[AsaServiceObjectGroup] = []
+ access_lists: list[AccessList] = []
+ access_group_bindings: list[AccessGroupBinding] = []
+ nat_rules: list[NatRule] = []
+ routes: list[Route] = []
+ mgmt_access: list[MgmtAccessRule] = []
+ names: list[Names] = []
+ class_maps: list[ClassMap] = []
+ policy_maps: list[PolicyMap] = []
+ service_policies: list[ServicePolicyBinding] = []
+ protocol_groups: list[AsaProtocolGroup] = []
diff --git a/roles/importer/files/importer/fw_modules/ciscoasa9/asa_network.py b/roles/importer/files/importer/fw_modules/ciscoasa9/asa_network.py
new file mode 100644
index 0000000000..fea99f07b6
--- /dev/null
+++ b/roles/importer/files/importer/fw_modules/ciscoasa9/asa_network.py
@@ -0,0 +1,373 @@
+"""
+ASA Network Object Management
+
+This module handles the normalization of network objects from ASA configurations.
+It manages both explicit network objects/groups and implicit network objects created from
+inline ACL or group definitions.
+"""
+
+import fwo_base
+import fwo_const
+from fw_modules.ciscoasa9.asa_models import (
+ AsaNetworkObject,
+ AsaNetworkObjectGroup,
+ AsaNetworkObjectGroupMember,
+ EndpointKind,
+ Names,
+)
+from fwo_log import FWOLogger
+from models.networkobject import NetworkObject
+from netaddr import IPAddress, IPNetwork
+
+
+def create_network_host(name: str, ip_address: str, comment: str | None, ip_version: int) -> NetworkObject:
+ """
+ Create a normalized host network object.
+
+ Args:
+ name: Object name/UID
+ ip_address: IP address
+ comment: Optional description
+ ip_version: IP version (4 or 6)
+
+ Returns:
+ Normalized NetworkObject instance
+
+ """
+ obj_ip = IPNetwork(f"{ip_address}/128", version=6) if ip_version == 6 else IPNetwork(f"{ip_address}/32") # noqa: PLR2004
+ return NetworkObject(
+ obj_uid=name,
+ obj_name=name,
+ obj_typ="host",
+ obj_ip=obj_ip,
+ obj_ip_end=obj_ip,
+ obj_color=fwo_const.DEFAULT_COLOR,
+ obj_comment=comment,
+ )
+
+
+def create_network_subnet(
+ name: str, ip_address: str, subnet_mask: str | None, comment: str | None, ip_version: int
+) -> NetworkObject:
+ """
+ Create a normalized network object.
+
+ Args:
+ name: Object name/UID
+ ip_address: Network address
+ subnet_mask: Subnet mask
+ comment: Optional description
+ ip_version: IP version (4 or 6)
+
+ Returns:
+ Normalized NetworkObject instance
+
+ """
+ if ip_version == 6: # noqa: PLR2004
+ # ip_address is expected to be in CIDR notation for IPv6
+ network = IPNetwork(ip_address, version=6)
+ ip_start = IPNetwork(f"{IPAddress(network.first)}/128", version=6)
+ ip_end = IPNetwork(f"{IPAddress(network.last)}/128", version=6)
+ else:
+ if subnet_mask is None:
+ raise ValueError("Subnet mask is required for IPv4 subnet objects.")
+ network = IPNetwork(f"{ip_address}/{subnet_mask}")
+ ip_start = IPNetwork(f"{ip_address}/32")
+ ip_end = IPNetwork(f"{IPAddress(network.first + network.size - 1)}/32")
+
+ return NetworkObject(
+ obj_uid=name,
+ obj_name=name,
+ obj_typ="network",
+ obj_ip=ip_start,
+ obj_ip_end=ip_end,
+ obj_color=fwo_const.DEFAULT_COLOR,
+ obj_comment=comment,
+ )
+
+
+def create_network_range(name: str, ip_start: str, ip_end: str, comment: str | None) -> NetworkObject:
+ """
+ Create a normalized range network object.
+
+ Args:
+ name: Object name/UID
+ ip_start: Start IP address
+ ip_end: End IP address
+ comment: Optional description
+
+ Returns:
+ Normalized NetworkObject instance
+
+ """
+ return NetworkObject(
+ obj_uid=name,
+ obj_name=name,
+ obj_typ="ip_range",
+ obj_ip=IPNetwork(f"{ip_start}/32"),
+ obj_ip_end=IPNetwork(f"{ip_end}/32"),
+ obj_color=fwo_const.DEFAULT_COLOR,
+ obj_comment=comment,
+ )
+
+
+def create_network_group_object(name: str, member_refs: list[str], comment: str | None = None) -> NetworkObject:
+ """
+ Create a network group object.
+
+ Args:
+ name: Group name/UID
+ member_refs: List of member network object references
+ comment: Optional description
+
+ Returns:
+ Normalized NetworkObject group instance
+
+ """
+ return NetworkObject(
+ obj_uid=name,
+ obj_name=name,
+ obj_typ="group",
+ obj_member_names=fwo_base.sort_and_join(member_refs),
+ obj_member_refs=fwo_base.sort_and_join(member_refs),
+ obj_color=fwo_const.DEFAULT_COLOR,
+ obj_comment=comment,
+ )
+
+
+def create_any_network_object() -> NetworkObject:
+ """
+ Create the special 'any' network object representing all addresses.
+
+ Returns:
+ Normalized NetworkObject for 'any'
+
+ """
+ return NetworkObject(
+ obj_uid="any",
+ obj_name="any",
+ obj_typ="network",
+ obj_member_names="",
+ obj_member_refs="",
+ obj_ip=IPNetwork("0.0.0.0/32"),
+ obj_ip_end=IPNetwork("255.255.255.255/32"),
+ obj_color=fwo_const.DEFAULT_COLOR,
+ obj_comment="network object created during import",
+ )
+
+
+def normalize_names(names: list[Names]) -> dict[str, NetworkObject]:
+ """
+ Normalize 'names' entries (simple IP-to-name mappings).
+
+ Args:
+ names: List of Names objects from ASA configuration
+
+ Returns:
+ Dictionary of normalized network objects keyed by obj_uid
+
+ """
+ network_objects: dict[str, NetworkObject] = {}
+
+ for name in names:
+ obj = create_network_host(name.name, name.ip_address, name.description, ip_version=4)
+ network_objects[name.name] = obj
+
+ return network_objects
+
+
+def normalize_network_objects(network_objects_list: list[AsaNetworkObject]) -> dict[str, NetworkObject]:
+ """
+ Normalize network objects from ASA configuration.
+
+ Args:
+ network_objects_list: List of AsaNetworkObject instances
+
+ Returns:
+ Dictionary of normalized network objects keyed by obj_uid
+
+ """
+ network_objects: dict[str, NetworkObject] = {}
+
+ for obj in network_objects_list:
+ if obj.fqdn is not None:
+ # handle FQDN objects as empty group for now /TODO
+ network_obj = create_network_group_object(obj.name, [], obj.description)
+ network_objects[obj.name] = network_obj
+ elif obj.ip_address and obj.subnet_mask:
+ # Network object with subnet mask
+ network_obj = create_network_subnet(
+ obj.name, obj.ip_address, obj.subnet_mask, obj.description, ip_version=4
+ )
+ network_objects[obj.name] = network_obj
+ elif obj.ip_address and obj.ip_address_end:
+ network_obj = create_network_range(obj.name, obj.ip_address, obj.ip_address_end, obj.description)
+ network_objects[obj.name] = network_obj
+ elif obj.ip_address:
+ # Host object (single IP address)
+ network_obj = create_network_host(obj.name, obj.ip_address, obj.description, ip_version=4)
+ network_objects[obj.name] = network_obj
+
+ return network_objects
+
+
+def normalize_network_object_groups(
+ object_groups: list[AsaNetworkObjectGroup], network_objects: dict[str, NetworkObject]
+) -> dict[str, NetworkObject]:
+ """
+ Normalize network object groups from ASA configuration.
+
+ Args:
+ object_groups: List of AsaNetworkObjectGroup instances
+ network_objects: Existing network objects dictionary to update
+ logger: Logger instance for warnings
+
+ Returns:
+ Updated network objects dictionary including groups
+
+ """
+ for group in object_groups:
+ member_refs: list[str] = []
+
+ for member in group.objects:
+ try:
+ # Use the modular function to create/get the member object
+ network_obj = get_network_group_member(member, network_objects)
+
+ # Add the reference to the member list
+ member_refs.append(network_obj.obj_uid)
+
+ except ValueError as e:
+ FWOLogger.warning(f"Error processing member in network object group '{group.name}': {e}")
+
+ group_obj = create_network_group_object(group.name, member_refs, group.description)
+ network_objects[group.name] = group_obj
+
+ return network_objects
+
+
+def get_network_group_member_host(member: AsaNetworkObjectGroupMember) -> NetworkObject:
+ """
+ Create a host network object for a network object group member.
+
+ Args:
+ member: Network object group member of kind 'host' or 'hostv6'
+
+ Returns:
+ NetworkObject instance
+
+ """
+ ip_version = 6 if member.kind == "hostv6" else 4
+ return create_network_host(member.value, member.value, None, ip_version=ip_version)
+
+
+def get_network_group_member_ref(member: AsaNetworkObjectGroupMember) -> str:
+ """
+ Get the reference string for a network object group member.
+
+ Args:
+ member: Network object group member
+ Returns:
+
+ Reference string for the member
+
+ """
+ if member.kind == "subnet":
+ if member.mask is None:
+ raise ValueError("Subnet mask is required for subnet member kind.")
+ return f"{member.value}/{member.mask}"
+ return member.value
+
+
+def create_network_group_member(ref: str, member: AsaNetworkObjectGroupMember) -> NetworkObject:
+ """
+ Create a network object for a network object group member.
+
+ Args:
+ ref: Reference string for the member
+ member: Network object group member
+ Returns:
+ NetworkObject instance
+
+ """
+ if member.kind == "host":
+ return create_network_host(ref, member.value, None, ip_version=4)
+ if member.kind == "hostv6":
+ return create_network_host(ref, member.value, None, ip_version=6)
+ if member.kind == "subnet":
+ return create_network_subnet(ref, member.value, member.mask, None, ip_version=4)
+ if member.kind == "subnetv6":
+ return create_network_subnet(ref, member.value, None, None, ip_version=6)
+ raise ValueError(f"Unsupported member kind '{member.kind}' in network object group.")
+
+
+def get_network_group_member(
+ member: AsaNetworkObjectGroupMember, network_objects: dict[str, NetworkObject]
+) -> NetworkObject:
+ """
+ Get network object for a network object group member reference. If it does not exist, create it.
+
+ Args:
+ member: Network object group member
+ network_objects: Dictionary of existing network objects
+
+ Returns:
+ NetworkObject instance
+
+ """
+ ref = get_network_group_member_ref(member)
+ if ref in network_objects:
+ return network_objects[ref]
+ if member.kind in ("object", "object-group"):
+ raise ValueError(f"Referenced network object '{ref}' not found in configuration.")
+
+ network_object = create_network_group_member(ref, member)
+ network_objects[network_object.obj_uid] = network_object
+ return network_object
+
+
+def get_network_rule_endpoint(endpoint: EndpointKind, network_objects: dict[str, NetworkObject]) -> NetworkObject:
+ """
+ Get network object for a rule endpoint. If it does not exist, create it.
+
+ Args:
+ endpoint: Rule endpoint (src or dst)
+ network_objects: Dictionary of existing network objects
+
+ Returns:
+ NetworkObject instance
+
+ """
+ network_object = None
+ if endpoint.kind == "host":
+ # Single host IP (e.g., 'host 10.0.0.1')
+ ref = endpoint.value
+ if ref in network_objects:
+ return network_objects[ref]
+ network_object = create_network_host(endpoint.value, endpoint.value, None, ip_version=4)
+ elif endpoint.kind == "subnet":
+ # Subnet with mask (e.g., '10.0.0.0 255.255.255.0')
+ # Object name is subnet in CIDR notation
+ ref = str(IPNetwork(f"{endpoint.value}/{endpoint.mask}"))
+ if ref in network_objects:
+ return network_objects[ref]
+ if endpoint.mask is None:
+ raise ValueError("Subnet mask is required for subnet endpoint kind.")
+ network_object = create_network_subnet(ref, endpoint.value, endpoint.mask, None, ip_version=4)
+ elif endpoint.kind == "any":
+ # 'any' keyword (0.0.0.0 - 255.255.255.255)
+ if "any" in network_objects:
+ return network_objects["any"]
+ network_object = create_any_network_object()
+ elif endpoint.kind in ("object", "object-group"):
+ # Reference to existing object or object-group - assume it already exists
+ ref = endpoint.value
+ ref_obj = network_objects.get(ref)
+ if not ref_obj:
+ raise ValueError(f"Referenced network object '{ref}' not found in configuration.")
+ return ref_obj
+ else:
+ raise ValueError(f"Unknown endpoint kind: {endpoint.kind}")
+
+ network_objects[network_object.obj_uid] = network_object
+ return network_object
diff --git a/roles/importer/files/importer/fw_modules/ciscoasa9/asa_normalize.py b/roles/importer/files/importer/fw_modules/ciscoasa9/asa_normalize.py
new file mode 100644
index 0000000000..b05e51af87
--- /dev/null
+++ b/roles/importer/files/importer/fw_modules/ciscoasa9/asa_normalize.py
@@ -0,0 +1,185 @@
+"""
+ASA Configuration Normalization
+
+This module handles the top-level normalization of ASA configurations,
+orchestrating the conversion from native ASA format to the normalized
+format used by the firewall orchestrator.
+"""
+
+from fw_modules.ciscoasa9.asa_models import Config
+
+# Import the new modular functions
+from fw_modules.ciscoasa9.asa_network import normalize_names, normalize_network_object_groups, normalize_network_objects
+from fw_modules.ciscoasa9.asa_rule import build_rulebases_from_access_lists
+from fw_modules.ciscoasa9.asa_service import (
+ create_protocol_any_service_objects,
+ normalize_service_object_groups,
+ normalize_service_objects,
+)
+from fwo_enums import ConfigAction
+from fwo_log import FWOLogger
+from model_controllers.fwconfigmanagerlist_controller import FwConfigManagerListController
+from models.fwconfig_normalized import FwConfigNormalized
+from models.gateway import Gateway
+from models.import_state import ImportState
+from models.networkobject import NetworkObject
+from models.rulebase_link import RulebaseLinkUidBased
+from models.serviceobject import ServiceObject
+
+
+def normalize_all_network_objects(native_config: Config) -> dict[str, NetworkObject]:
+ """
+ Normalize all network objects from the native ASA configuration.
+
+ This function processes:
+ - Named hosts (from 'names' command)
+ - Network objects (hosts and subnets)
+ - Network object groups
+
+ Args:
+ native_config: Parsed ASA configuration containing network objects.
+ logger: Logger instance for warnings and debug messages.
+
+ Returns:
+ Dictionary of normalized network objects keyed by obj_uid.
+
+ """
+ # Start with names (simple IP-to-name mappings)
+ network_objects = normalize_names(native_config.names)
+
+ # Add individual network objects
+ network_objects.update(normalize_network_objects(native_config.objects))
+
+ # Add network object groups
+ normalize_network_object_groups(native_config.object_groups, network_objects)
+
+ return network_objects
+
+
+def normalize_all_service_objects(native_config: Config) -> dict[str, ServiceObject]:
+ """
+ Normalize all service objects from the native ASA configuration.
+
+ This function processes:
+ - Individual service objects (with specific ports or port ranges)
+ - Default 'any' service objects for common protocols
+ - Service object groups (including mixed protocol groups)
+
+ Args:
+ native_config: Parsed ASA configuration containing service objects.
+
+ Returns:
+ Dictionary of normalized service objects keyed by svc_uid.
+
+ """
+ # Start with individual service objects
+ service_objects = normalize_service_objects(native_config.service_objects)
+
+ # Add default 'any' protocol service objects
+ service_objects.update(create_protocol_any_service_objects())
+
+ # Add service object groups
+ normalize_service_object_groups(native_config.service_object_groups, service_objects)
+
+ return service_objects
+
+
+def normalize_config(
+ config_in: FwConfigManagerListController, import_state: ImportState
+) -> FwConfigManagerListController:
+ """
+ Normalize the ASA configuration into a structured format for the database.
+
+ This function orchestrates the normalization process:
+ 1. Parse the native configuration
+ 2. Normalize network objects (hosts, networks, groups)
+ 3. Normalize service objects (ports, protocols, groups)
+ 4. Build rulebases from access lists (including inline object creation)
+ 5. Create gateway and rulebase links
+ 6. Construct the final normalized configuration
+
+ Args:
+ config_in: Configuration input details containing native config.
+ importState: Current import state with management details.
+
+ Returns:
+ Updated config_in with normalized configuration.
+
+ """
+ # Parse the native configuration into structured objects
+ native_config: Config = Config.model_validate(config_in.native_config)
+
+ # Step 1: Normalize network objects (names, objects, object-groups)
+ FWOLogger.debug("Normalizing network objects...")
+ network_objects = normalize_all_network_objects(native_config)
+
+ # Step 2: Normalize service objects (service objects with ports/protocols)
+ FWOLogger.debug("Normalizing service objects...")
+ service_objects = normalize_all_service_objects(native_config)
+
+ # Step 3: Build rulebases from access lists (this will create additional objects as needed)
+ FWOLogger.debug("Building rulebases from access lists...")
+ rulebases = build_rulebases_from_access_lists(
+ native_config.access_lists,
+ import_state.mgm_details.uid,
+ protocol_groups=native_config.protocol_groups,
+ network_objects=network_objects,
+ service_objects=service_objects,
+ )
+
+ # Step 4: Create rulebase links (ordered chain of rulebases)
+ rulebase_links: list[RulebaseLinkUidBased] = []
+ if len(rulebases) > 0:
+ # First rulebase is the initial entry point
+ rulebase_links.append(
+ RulebaseLinkUidBased(
+ to_rulebase_uid=rulebases[0].uid,
+ link_type="ordered",
+ is_initial=True,
+ is_global=False,
+ is_section=False,
+ )
+ )
+ # Link subsequent rulebases in order
+ [
+ RulebaseLinkUidBased(
+ from_rulebase_uid=rulebases[idx - 1].uid,
+ to_rulebase_uid=rulebases[idx].uid,
+ link_type="ordered",
+ is_initial=False,
+ is_global=False,
+ is_section=False,
+ )
+ for idx in range(1, len(rulebases))
+ ]
+
+ # Step 5: Create gateway object representing the ASA device
+ FWOLogger.debug("Creating gateway object...")
+ gateway = Gateway(
+ Uid=native_config.hostname,
+ Name=native_config.hostname,
+ Routing=[],
+ RulebaseLinks=rulebase_links,
+ GlobalPolicyUid=None,
+ EnforcedPolicyUids=[],
+ EnforcedNatPolicyUids=[],
+ ImportDisabled=False,
+ ShowInUI=True,
+ )
+
+ # Step 6: Construct the normalized configuration
+ FWOLogger.debug("Constructing normalized configuration...")
+ normalized_config = FwConfigNormalized(
+ action=ConfigAction.INSERT,
+ network_objects=network_objects,
+ service_objects=service_objects,
+ zone_objects={}, # ASA doesn't use zones like other firewalls
+ rulebases=rulebases,
+ gateways=[gateway],
+ )
+
+ # Update the configuration input with normalized data
+ config_in.ManagerSet[0].configs = [normalized_config]
+ config_in.ManagerSet[0].manager_uid = import_state.mgm_details.uid
+
+ return config_in
diff --git a/roles/importer/files/importer/fw_modules/ciscoasa9/asa_parser.py b/roles/importer/files/importer/fw_modules/ciscoasa9/asa_parser.py
new file mode 100644
index 0000000000..2770a06026
--- /dev/null
+++ b/roles/importer/files/importer/fw_modules/ciscoasa9/asa_parser.py
@@ -0,0 +1,395 @@
+import json
+import re
+from pathlib import Path
+from typing import TYPE_CHECKING
+
+from fw_modules.ciscoasa9.asa_models import (
+ AccessGroupBinding,
+ AccessList,
+ AccessListEntry,
+ AsaEnablePassword,
+ AsaNetworkObject,
+ AsaNetworkObjectGroup,
+ AsaProtocolGroup,
+ AsaServiceModule,
+ AsaServiceObject,
+ AsaServiceObjectGroup,
+ ClassMap,
+ Config,
+ Interface,
+ MgmtAccessRule,
+ Names,
+ NatRule,
+ PolicyMap,
+ Route,
+ ServicePolicyBinding,
+)
+from fw_modules.ciscoasa9.asa_parser_functions import (
+ clean_lines,
+ consume_block,
+ parse_access_list_entry,
+ parse_class_map_block,
+ parse_dns_inspect_policy_map_block,
+ parse_icmp_object_group_block,
+ parse_interface_block,
+ parse_network_object_block,
+ parse_network_object_group_block,
+ parse_policy_map_block,
+ parse_protocol_object_group_block,
+ parse_service_object_block,
+ parse_service_object_group_block,
+)
+from fwo_log import FWOLogger
+
+if TYPE_CHECKING:
+ from collections.abc import Callable
+
+
+def parse_asa_config(raw_config: str) -> Config:
+ lines = clean_lines(raw_config)
+
+ # Initialize state
+ state = _ParserState()
+
+ # Handler registry: (pattern, handler_function)
+ handlers: list[tuple[re.Pattern[str], Callable[[re.Match[str], str, list[str], int, _ParserState], int]]] = [
+ (re.compile(r"^ASA Version\s+(\S+)$", re.IGNORECASE), _handle_asa_version),
+ (re.compile(r"^hostname\s+(\S+)$", re.IGNORECASE), _handle_hostname),
+ (re.compile(r"^enable password\s+(\S+)\s+(\S+)$", re.IGNORECASE), _handle_enable_password),
+ (
+ re.compile(r"^service-module\s+(\S+)\s+keepalive-timeout\s+(\d+)$", re.IGNORECASE),
+ _handle_service_module_timeout,
+ ),
+ (
+ re.compile(r"^service-module\s+(\S+)\s+keepalive-counter\s+(\d+)$", re.IGNORECASE),
+ _handle_service_module_counter,
+ ),
+ (re.compile(r"^name\s+(\d{1,3}(?:\.\d{1,3}){3})\s+(\S+)(?:\s+description\s)?", re.IGNORECASE), _handle_name),
+ (re.compile(r"^interface\s+\S+", re.IGNORECASE), _handle_interface_block),
+ (re.compile(r"^object\s+network\s+\S+$", re.IGNORECASE), _handle_network_object_block),
+ (re.compile(r"^object-group\s+network\s+\S+$", re.IGNORECASE), _handle_network_object_group_block),
+ (re.compile(r"^object\s+service\s+\S+$", re.IGNORECASE), _handle_service_object_block),
+ (
+ re.compile(r"^object-group\s+service\s+\S+", re.IGNORECASE),
+ _handle_service_object_group,
+ ), # left intentionally without $
+ (re.compile(r"^object-group\s+icmp-type\s+\S+$", re.IGNORECASE), _handle_icmp_object_group_block),
+ (re.compile(r"^object-group\s+protocol\s+\S+$", re.IGNORECASE), _handle_protocol_object_group_block),
+ (re.compile(r"^access-list\s+\S+\s+extended\s+(permit|deny)\s+", re.IGNORECASE), _handle_access_list_entry),
+ (re.compile(r"^access-group\s+(\S+)\s+(in|out)\s+interface\s+(\S+)$", re.IGNORECASE), _handle_access_group),
+ (re.compile(r"^route\s+(\S+)\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)(?:\s+(\d+))?$", re.IGNORECASE), _handle_route),
+ (
+ re.compile(r"^(http|ssh|telnet)\s+(\d+\.\d+\.\d+\.\d+)\s+(\d+\.\d+\.\d+\.\d+)\s+(\S+)$", re.IGNORECASE),
+ _handle_mgmt_access,
+ ),
+ (re.compile(r"^class-map\s+\S+", re.IGNORECASE), _handle_class_map_block),
+ (
+ re.compile(r"^policy-map\s+type\s+inspect\s+dns\s+(\S+)$", re.IGNORECASE),
+ _handle_dns_inspect_policy_map_block,
+ ),
+ (re.compile(r"^policy-map\s+(\S+)$", re.IGNORECASE), _handle_policy_map_block),
+ (re.compile(r"^service-policy\s+(\S+)\s+(global|interface\s+\S+)$", re.IGNORECASE), _handle_service_policy),
+ ]
+
+ i = 0
+ while i < len(lines):
+ line = lines[i].strip()
+
+ if not line or line == "!":
+ i += 1
+ continue
+
+ handled = False
+ for pattern, handler in handlers:
+ match = pattern.match(line)
+ if match:
+ i = handler(match, line, lines, i, state)
+ handled = True
+ break
+
+ if not handled:
+ _handle_additional_settings(line, state)
+ i += 1
+
+ return _build_config(state)
+
+
+class _ParserState:
+ def __init__(self):
+ self.asa_version = ""
+ self.hostname = ""
+ self.enable_password: AsaEnablePassword | None = None
+ self.service_modules: list[AsaServiceModule] = []
+ self.names: list[Names] = []
+ self.interfaces: list[Interface] = []
+ self.net_objects: list[AsaNetworkObject] = []
+ self.net_obj_groups: list[AsaNetworkObjectGroup] = []
+ self.svc_objects: list[AsaServiceObject] = []
+ self.svc_obj_groups: list[AsaServiceObjectGroup] = []
+ self.access_lists_map: dict[str, list[AccessListEntry]] = {}
+ self.access_groups: list[AccessGroupBinding] = []
+ self.nat_rules: list[NatRule] = []
+ self.routes: list[Route] = []
+ self.mgmt_access: list[MgmtAccessRule] = []
+ self.additional_settings: list[str] = []
+ self.class_maps: list[ClassMap] = []
+ self.policy_maps: dict[str, PolicyMap] = {}
+ self.service_policies: list[ServicePolicyBinding] = []
+ self.protocol_groups: list[AsaProtocolGroup] = []
+
+
+def _handle_asa_version(match: re.Match[str], _line: str, _lines: list[str], i: int, state: _ParserState) -> int:
+ state.asa_version = match.group(1).strip()
+ return i + 1
+
+
+def _handle_hostname(match: re.Match[str], _line: str, _lines: list[str], i: int, state: _ParserState) -> int:
+ state.hostname = match.group(1)
+ return i + 1
+
+
+def _handle_enable_password(match: re.Match[str], _line: str, _lines: list[str], i: int, state: _ParserState) -> int:
+ state.enable_password = AsaEnablePassword(password=match.group(1), encryption_function=match.group(2))
+ return i + 1
+
+
+def _handle_service_module_timeout(
+ match: re.Match[str], _line: str, lines: list[str], i: int, state: _ParserState
+) -> int:
+ name = match.group(1)
+ timeout = int(match.group(2))
+ keepalive_counter = _find_keepalive_counter(lines, i, name)
+ state.service_modules.append(
+ AsaServiceModule(name=name, keepalive_timeout=timeout, keepalive_counter=keepalive_counter)
+ )
+ return i + 1
+
+
+def _find_keepalive_counter(lines: list[str], i: int, name: str) -> int:
+ for j in range(i + 1, min(i + 5, len(lines))):
+ m = re.match(
+ rf"^service-module\s+{re.escape(name)}\s+keepalive-counter\s+(\d+)$", lines[j].strip(), re.IGNORECASE
+ )
+ if m:
+ return int(m.group(1))
+ return 0
+
+
+def _handle_service_module_counter(
+ _match: re.Match[str], _line: str, _lines: list[str], i: int, _state: _ParserState
+) -> int:
+ return i + 1
+
+
+def _handle_name(match: re.Match[str], line: str, _lines: list[str], i: int, state: _ParserState) -> int:
+ ip, alias = match.group(1), match.group(2)
+ desc = line[match.end() :].strip() or None
+ state.names.append(Names(name=alias, ip_address=ip, description=desc))
+ return i + 1
+
+
+def _handle_interface_block(_match: re.Match[str], _line: str, lines: list[str], i: int, state: _ParserState) -> int:
+ block, new_i = consume_block(lines, i)
+ state.interfaces.append(parse_interface_block(block))
+ return new_i
+
+
+def _handle_network_object_block(
+ _match: re.Match[str], _line: str, lines: list[str], i: int, state: _ParserState
+) -> int:
+ block, new_i = consume_block(lines, i)
+ net_obj, pending_nat = parse_network_object_block(block)
+ if net_obj:
+ state.net_objects.append(net_obj)
+ if pending_nat:
+ state.nat_rules.append(pending_nat)
+ return new_i
+
+
+def _handle_network_object_group_block(
+ _match: re.Match[str], _line: str, lines: list[str], i: int, state: _ParserState
+) -> int:
+ block, new_i = consume_block(lines, i)
+ state.net_obj_groups.append(parse_network_object_group_block(block))
+ return new_i
+
+
+def _handle_service_object_block(
+ _match: re.Match[str], _line: str, lines: list[str], i: int, state: _ParserState
+) -> int:
+ block, new_i = consume_block(lines, i)
+ svc_obj = parse_service_object_block(block)
+ if svc_obj:
+ state.svc_objects.append(svc_obj)
+ return new_i
+
+
+def _handle_service_object_group(
+ _match: re.Match[str], _line: str, lines: list[str], i: int, state: _ParserState
+) -> int:
+ block, new_i = consume_block(lines, i)
+ state.svc_obj_groups.append(parse_service_object_group_block(block))
+ return new_i
+
+
+def _handle_icmp_object_group_block(
+ _match: re.Match[str], _line: str, lines: list[str], i: int, state: _ParserState
+) -> int:
+ block, new_i = consume_block(lines, i)
+ state.svc_obj_groups.append(parse_icmp_object_group_block(block))
+ return new_i
+
+
+def _handle_protocol_object_group_block(
+ _match: re.Match[str], _line: str, lines: list[str], i: int, state: _ParserState
+) -> int:
+ block, new_i = consume_block(lines, i)
+ state.protocol_groups.append(parse_protocol_object_group_block(block))
+ return new_i
+
+
+def _handle_access_list_entry(_match: re.Match[str], line: str, _lines: list[str], i: int, state: _ParserState) -> int:
+ try:
+ entry = parse_access_list_entry(line, state.protocol_groups, state.svc_objects, state.svc_obj_groups)
+ state.access_lists_map.setdefault(entry.acl_name, []).append(entry)
+ except Exception:
+ FWOLogger.warning(f"Failed to parse access-list entry: {line}")
+ return i + 1
+
+
+def _handle_access_group(match: re.Match[str], _line: str, _lines: list[str], i: int, state: _ParserState) -> int:
+ direction = match.group(2)
+ if direction not in ("in", "out"):
+ raise ValueError(f"Invalid direction value: {direction}")
+ state.access_groups.append(
+ AccessGroupBinding(acl_name=match.group(1), direction=direction, interface=match.group(3))
+ )
+ return i + 1
+
+
+def _handle_route(match: re.Match[str], _line: str, _lines: list[str], i: int, state: _ParserState) -> int:
+ state.routes.append(
+ Route(
+ interface=match.group(1),
+ destination=match.group(2),
+ netmask=match.group(3),
+ next_hop=match.group(4),
+ distance=int(match.group(5)) if match.group(5) else None,
+ )
+ )
+ return i + 1
+
+
+def _handle_mgmt_access(match: re.Match[str], _line: str, _lines: list[str], i: int, state: _ParserState) -> int:
+ protocol_str = match.group(1).lower()
+ if protocol_str not in ("http", "ssh", "telnet"):
+ raise ValueError(f"Invalid protocol for MgmtAccessRule: {protocol_str}")
+ state.mgmt_access.append(
+ MgmtAccessRule(
+ protocol=protocol_str, source_ip=match.group(2), source_mask=match.group(3), interface=match.group(4)
+ )
+ )
+ return i + 1
+
+
+def _handle_class_map_block(_match: re.Match[str], _line: str, lines: list[str], i: int, state: _ParserState) -> int:
+ block, new_i = consume_block(lines, i)
+ state.class_maps.append(parse_class_map_block(block))
+ return new_i
+
+
+def _handle_dns_inspect_policy_map_block(
+ match: re.Match[str], _line: str, lines: list[str], i: int, state: _ParserState
+) -> int:
+ block, new_i = consume_block(lines, i)
+ pm_name = match.group(1)
+ pm = parse_dns_inspect_policy_map_block(block, pm_name)
+ state.policy_maps[pm_name] = pm
+ return new_i
+
+
+def _handle_policy_map_block(match: re.Match[str], _line: str, lines: list[str], i: int, state: _ParserState) -> int:
+ block, new_i = consume_block(lines, i)
+ pm_name = match.group(1)
+ pm = parse_policy_map_block(block, pm_name)
+ state.policy_maps[pm_name] = pm
+ return new_i
+
+
+def _handle_service_policy(match: re.Match[str], _line: str, _lines: list[str], i: int, state: _ParserState) -> int:
+ pm_name = match.group(1)
+ scope_part = match.group(2).lower()
+ if scope_part == "global":
+ state.service_policies.append(ServicePolicyBinding(policy_map=pm_name, scope="global"))
+ else:
+ iface = scope_part.split()[1]
+ state.service_policies.append(ServicePolicyBinding(policy_map=pm_name, scope="interface", interface=iface))
+ return i + 1
+
+
+def _handle_additional_settings(line: str, state: _ParserState) -> None:
+ interesting_prefixes = (
+ "ftp mode",
+ "same-security-traffic",
+ "dynamic-access-policy-record",
+ "service-policy",
+ "user-identity",
+ "aaa ",
+ "icmp ",
+ "arp ",
+ "ssh version",
+ "no ssh",
+ "ssh cipher",
+ "ssh key-exchange",
+ "ssh timeout",
+ "http server enable",
+ "no asdm",
+ "asdm ",
+ "crypto ",
+ "threat-detection",
+ "ssl cipher",
+ )
+ for pref in interesting_prefixes:
+ if line.startswith(pref):
+ state.additional_settings.append(line)
+ break
+
+
+def _build_config(state: _ParserState) -> Config:
+ access_lists = [AccessList(name=name, entries=entries) for name, entries in state.access_lists_map.items()]
+
+ return Config(
+ asa_version=state.asa_version or "unknown",
+ hostname=state.hostname or "unknown",
+ enable_password=state.enable_password or AsaEnablePassword(password="", encryption_function=""),
+ service_modules=state.service_modules,
+ additional_settings=state.additional_settings,
+ interfaces=state.interfaces,
+ objects=state.net_objects,
+ object_groups=state.net_obj_groups,
+ service_objects=state.svc_objects,
+ service_object_groups=state.svc_obj_groups,
+ access_lists=access_lists,
+ access_group_bindings=state.access_groups,
+ nat_rules=state.nat_rules,
+ routes=state.routes,
+ mgmt_access=state.mgmt_access,
+ names=state.names,
+ class_maps=state.class_maps,
+ policy_maps=list(state.policy_maps.values()),
+ service_policies=state.service_policies,
+ protocol_groups=state.protocol_groups,
+ )
+
+
+# ───────────────────────── Example usage ─────────────────────────
+if __name__ == "__main__":
+ cfg_file = Path("ciscoasa9/asa.conf")
+
+ with cfg_file.open("r", encoding="utf-8") as f:
+ text = f.read()
+
+ config = parse_asa_config(text)
+
+ # You can dump the entire parsed config as JSON
+ FWOLogger.debug(json.dumps(config.model_dump(exclude_none=True)["names"], indent=2))
diff --git a/roles/importer/files/importer/fw_modules/ciscoasa9/asa_parser_functions.py b/roles/importer/files/importer/fw_modules/ciscoasa9/asa_parser_functions.py
new file mode 100644
index 0000000000..d9242bc8f1
--- /dev/null
+++ b/roles/importer/files/importer/fw_modules/ciscoasa9/asa_parser_functions.py
@@ -0,0 +1,698 @@
+import re
+
+from fw_modules.ciscoasa9.asa_models import (
+ AccessListEntry,
+ AsaNetworkObject,
+ AsaNetworkObjectGroup,
+ AsaNetworkObjectGroupMember,
+ AsaProtocolGroup,
+ AsaServiceObject,
+ AsaServiceObjectGroup,
+ ClassMap,
+ DnsInspectParameters,
+ EndpointKind,
+ InspectionAction,
+ Interface,
+ NatRule,
+ PolicyClass,
+ PolicyMap,
+)
+from fwo_log import FWOLogger
+
+
+def clean_lines(text: str) -> list[str]:
+ lines: list[str] = []
+ for raw in text.splitlines():
+ line = raw.rstrip()
+ # Skip leading metadata/comment lines starting with ':' (as in "show run")
+ if line.strip().startswith(":"):
+ continue
+ lines.append(line)
+ return lines
+
+
+def consume_block(lines: list[str], start_idx: int) -> tuple[list[str], int]:
+ """
+ Consume a block that starts at start_idx (matching start_re) and continues
+ until next top-level directive (blank line or line not starting with space)
+ or a '!' separator. Returns (block_lines, next_index).
+ """
+ block = [lines[start_idx]]
+ i = start_idx + 1
+ while i < len(lines):
+ line = lines[i]
+ if line.strip() == "!":
+ i += 1
+ break
+ if line.startswith(" "): # continuation/indented
+ block.append(line)
+ i += 1
+ continue
+ # another directive starts; end this block
+ break
+ return block, i
+
+
+def parse_endpoint(tokens: list[str]) -> tuple[EndpointKind, int]:
+ """
+ Parse an ACL endpoint from tokens; returns (EndpointKind, tokens_consumed).
+ Supported:
+ any
+ host A.B.C.D
+ object NAME
+ object-group NAME
+ A.B.C.D MASK
+ """
+ if not tokens:
+ return EndpointKind(kind="any", value="any"), 0
+
+ t0 = tokens[0]
+ if t0 == "any":
+ return EndpointKind(kind="any", value="any"), 1
+ if t0 == "host" and len(tokens) >= 2: # noqa: PLR2004
+ return EndpointKind(kind="host", value=tokens[1]), 2
+ if t0 == "object" and len(tokens) >= 2: # noqa: PLR2004
+ return EndpointKind(kind="object", value=tokens[1]), 2
+ if t0 == "object-group" and len(tokens) >= 2: # noqa: PLR2004
+ return EndpointKind(kind="object-group", value=tokens[1]), 2
+ # subnet notation: ip + mask
+ if (
+ len(tokens) >= 2 # noqa: PLR2004
+ and re.fullmatch(r"\d{1,3}(?:\.\d{1,3}){3}", tokens[0])
+ and re.fullmatch(r"\d{1,3}(?:\.\d{1,3}){3}", tokens[1])
+ ):
+ return EndpointKind(kind="subnet", value=tokens[0], mask=tokens[1]), 2
+ # fallback
+ return EndpointKind(kind="any", value="any"), 1
+
+
+def _find_description(blocks: list[str]) -> str | None:
+ """Helper to find description line in a block."""
+ return _find_line_with_prefix(list(blocks), "description ")
+
+
+def _find_line_with_prefix(block: list[str], prefix: str, only_first: bool = False) -> str | None:
+ """Helper to find a single value in an interface block by prefix."""
+ v = None
+ for b in list(block):
+ s = b.strip()
+ if s.startswith(prefix):
+ v = s.split()[1] if only_first else s[len(prefix) :].strip()
+ block.remove(b)
+ return v
+
+
+def _parse_interface_block_find_ip_address(block: list[str], prefix: str) -> tuple[str | None, str | None]:
+ """Helper to find IP address and mask in an interface block."""
+ ip = None
+ mask = None
+ for b in list(block):
+ s = b.strip()
+ if s.startswith(prefix):
+ parts = s.split()
+ if len(parts) >= 4: # noqa: PLR2004
+ is_valid_ip = parts[2].count(".") == 3 and parts[3].count(".") == 3 # noqa: PLR2004
+ if is_valid_ip:
+ ip, mask = parts[2], parts[3]
+ block.remove(b)
+ return ip, mask
+
+
+def parse_interface_block(block: list[str]) -> Interface:
+ """Parse an interface block and return an Interface object."""
+ if_name = block[0].split()[1]
+ blocks = list(block)[1:]
+
+ # Extract values and remove consumed lines from blocks
+ nameif = _find_line_with_prefix(blocks, "nameif ", only_first=True)
+ br = _find_line_with_prefix(blocks, "bridge-group ", only_first=True)
+ sec = _find_line_with_prefix(blocks, "security-level ", only_first=True)
+ sec = int(sec) if sec is not None else 0
+ ip, mask = _parse_interface_block_find_ip_address(blocks, "ip address ")
+ desc = _find_line_with_prefix(blocks, "description ")
+ # All non-consumed lines remain in blocks as additional
+
+ # Defaults for missing bits
+ nameif = nameif or if_name
+
+ return Interface(
+ name=if_name,
+ nameif=nameif,
+ bridge_group=br,
+ security_level=sec,
+ ip_address=ip,
+ subnet_mask=mask,
+ additional_settings=blocks,
+ description=desc,
+ )
+
+
+def _create_network_object_from_parts(
+ name: str,
+ host: str | None,
+ subnet: str | None,
+ mask: str | None,
+ ip_range: tuple[str, str] | None,
+ fqdn: str | None,
+ description: str | None,
+) -> AsaNetworkObject | None:
+ """Helper to create AsaNetworkObject from parts."""
+ if host and not subnet:
+ return AsaNetworkObject(
+ name=name, ip_address=host, ip_address_end=None, subnet_mask=None, fqdn=None, description=description
+ )
+ if subnet:
+ return AsaNetworkObject(
+ name=name, ip_address=subnet, ip_address_end=None, subnet_mask=mask, fqdn=None, description=description
+ )
+ if ip_range:
+ return AsaNetworkObject(
+ name=name,
+ ip_address=ip_range[0],
+ ip_address_end=ip_range[1],
+ subnet_mask=None,
+ fqdn=None,
+ description=description,
+ )
+ if fqdn:
+ return AsaNetworkObject(
+ name=name, ip_address="", ip_address_end=None, subnet_mask=None, fqdn=fqdn, description=description
+ )
+
+ FWOLogger.warning(f"Cannot create network object {name}: no valid address information provided. NAT object?")
+ return None
+
+
+def parse_network_object_block(block: list[str]) -> tuple[AsaNetworkObject | None, NatRule | None]:
+ """Parse an object network block. Returns (network_object, nat_rule)."""
+ obj_name = block[0].split()[2]
+ host = None
+ ip_range = None
+ subnet = None
+ mask = None
+ fqdn = None
+ desc = _find_description(block[1:])
+ pending_nat = None
+
+ for b in block[1:]:
+ s = b.strip()
+ mhost = re.match(r"^host\s+(\S+)$", s, re.IGNORECASE)
+ msub = re.match(r"^subnet\s+(\S+)\s+(\S+)$", s, re.IGNORECASE)
+ mrange = re.match(r"^range\s+(\S+)\s+(\S+)$", s, re.IGNORECASE)
+ mfqdn = re.match(r"^fqdn\s+v4\s+(\S+)$", s, re.IGNORECASE)
+ mnat = re.match(r"^nat\s+\(([^,]+),([^)]+)\)\s+(dynamic|static)\s+(\S+)$", s, re.IGNORECASE)
+
+ if mhost:
+ host = mhost.group(1)
+ elif msub:
+ subnet, mask = msub.group(1), msub.group(2)
+ elif mrange:
+ ip_range = mrange.group(1), mrange.group(2)
+ elif mfqdn:
+ fqdn = mfqdn.group(1)
+ elif mnat:
+ src_if = mnat.group(1).strip()
+ dst_if = mnat.group(2).strip()
+ ntype = mnat.group(3).lower()
+ tobj = mnat.group(4).lower()
+ if ntype == "dynamic":
+ nat_type = "dynamic"
+ elif ntype == "static":
+ nat_type = "static"
+ else:
+ raise ValueError(f"Unsupported NAT type in line: {s}")
+ pending_nat = NatRule(
+ object_name=obj_name,
+ src_if=src_if,
+ dst_if=dst_if,
+ nat_type=nat_type,
+ translated_object=(None if tobj == "interface" else tobj),
+ )
+
+ # Create network object if we have host/subnet/fqdn
+ net_obj = _create_network_object_from_parts(
+ name=obj_name,
+ host=host,
+ subnet=subnet,
+ mask=mask,
+ ip_range=ip_range,
+ fqdn=fqdn,
+ description=desc,
+ )
+
+ return net_obj, pending_nat
+
+
+def parse_network_object_group_block(block: list[str]) -> AsaNetworkObjectGroup:
+ """Parse an object-group network block."""
+ grp_name = block[0].split()[2]
+ desc = _find_description(block[1:])
+ members: list[AsaNetworkObjectGroupMember] = []
+
+ for b in block[1:]:
+ s = b.strip()
+ mobj = re.match(r"^network-object\s+object\s+(\S+)$", s, re.IGNORECASE)
+ mhost = re.match(r"^network-object\s+host\s+(\d+\.\d+\.\d+\.\d+)$", s, re.IGNORECASE)
+ mhostv6 = re.match(r"^network-object\s+host\s+(\S+)$", s, re.IGNORECASE) # e.g. 2001:db8:abcd::1
+ msub = re.match(r"^network-object\s+(\d+\.\d+\.\d+\.\d+)\s+(\d+\.\d+\.\d+\.\d+)$", s, re.IGNORECASE)
+ msubv6 = re.match(r"^network-object\s+(\S+)$", s, re.IGNORECASE) # e.g. 2001:db8:abcd::/40 or 2001::/12
+ mgroup = re.match(r"^group-object\s+(\S+)$", s, re.IGNORECASE)
+
+ if mobj:
+ ref = mobj.group(1)
+ members.append(AsaNetworkObjectGroupMember(kind="object", value=ref))
+ elif mhost:
+ ip = mhost.group(1)
+ members.append(AsaNetworkObjectGroupMember(kind="host", value=ip))
+ elif mhostv6:
+ ip = mhostv6.group(1)
+ members.append(AsaNetworkObjectGroupMember(kind="hostv6", value=ip))
+ elif msub:
+ ip = msub.group(1)
+ mask = msub.group(2)
+ members.append(AsaNetworkObjectGroupMember(kind="subnet", value=ip, mask=mask))
+ elif msubv6:
+ ip = msubv6.group(1)
+ members.append(AsaNetworkObjectGroupMember(kind="subnetv6", value=ip))
+ elif mgroup:
+ ref = mgroup.group(1)
+ members.append(AsaNetworkObjectGroupMember(kind="object-group", value=ref))
+
+ return AsaNetworkObjectGroup(name=grp_name, objects=members, description=desc)
+
+
+def parse_service_object_block(block: list[str]) -> AsaServiceObject | None:
+ """Parse an object service block."""
+ name = block[0].split()[2]
+ protocol = None
+ eq = None
+ prange = None
+ desc = _find_description(block[1:])
+
+ for b in block[1:]:
+ s = b.strip()
+ # e.g., "service tcp destination eq 1234"
+ meq = re.match(r"^service\s+(tcp|udp|ip)\s+destination\s+eq\s+(\S+)$", s, re.IGNORECASE)
+ mrange = re.match(r"^service\s+(tcp|udp|ip)\s+destination\s+range\s+(\S+)\s+(\S+)$", s, re.IGNORECASE)
+ micmp = re.match(r"^service\s+icmp.*$", s, re.IGNORECASE)
+ msvc = re.match(r"^service\s+(\S+)$", s, re.IGNORECASE)
+
+ if meq:
+ protocol = meq.group(1).lower()
+ eq = meq.group(2)
+ elif mrange:
+ protocol = mrange.group(1).lower()
+ prange = (mrange.group(2), mrange.group(3))
+ elif micmp:
+ protocol = "icmp"
+ elif msvc:
+ protocol = msvc.group(1).lower()
+
+ if protocol is None or protocol not in ("tcp", "udp", "ip", "icmp", "gre"):
+ FWOLogger.warning(f"Unsupported or missing protocol {protocol} in service object {name}")
+ return None # unsupported protocol
+
+ return AsaServiceObject(name=name, protocol=protocol, dst_port_eq=eq, dst_port_range=prange, description=desc)
+
+
+def _convert_ports_to_dicts(
+ ports_eq: list[tuple[str, str]], ports_range: list[tuple[str, tuple[str, str]]]
+) -> tuple[dict[str, list[str]], dict[str, list[tuple[str, str]]]]:
+ """
+ Convert port lists to dictionaries grouped by protocol.
+ Returns (ports_eq_dict, ports_range_dict).
+ """
+ ports_eq_dict: dict[str, list[str]] = {}
+ for proto, port in ports_eq:
+ if proto not in ports_eq_dict:
+ ports_eq_dict[proto] = []
+ ports_eq_dict[proto].append(port)
+
+ ports_range_dict: dict[str, list[tuple[str, str]]] = {}
+ for proto, prange in ports_range:
+ if proto not in ports_range_dict:
+ ports_range_dict[proto] = []
+ ports_range_dict[proto].append(prange)
+
+ return ports_eq_dict, ports_range_dict
+
+
+def _consume_port_objects(
+ service_group_block: list[str], proto_mode: str
+) -> tuple[list[tuple[str, str]], list[tuple[str, tuple[str, str]]]]:
+ """Helper to consume port-object lines from a service object group block."""
+ ports_eq: list[tuple[str, str]] = []
+ ports_range: list[tuple[str, tuple[str, str]]] = []
+
+ for b in list(service_group_block):
+ s = b.strip()
+ mport_eq = re.match(r"^port-object\s+eq\s+(\S+)$", s, re.IGNORECASE)
+ mport_range = re.match(r"^port-object\s+range\s+(\d+)\s+(\d+)$", s, re.IGNORECASE)
+
+ if mport_eq:
+ ports_eq.append((proto_mode, mport_eq.group(1)))
+ elif mport_range:
+ ports_range.append((proto_mode, (mport_range.group(1), mport_range.group(2))))
+ else:
+ continue
+ service_group_block.remove(b)
+
+ return ports_eq, ports_range
+
+
+def _consume_service_definitions(
+ service_group_block: list[str],
+) -> tuple[list[tuple[str, str]], list[tuple[str, tuple[str, str]]], list[str]]:
+ """Helper to consume service-object definitions from a service object group block."""
+ ports_eq: list[tuple[str, str]] = []
+ ports_range: list[tuple[str, tuple[str, str]]] = []
+ protocols: list[str] = [] # list of fully enabled protocols
+
+ for b in list(service_group_block):
+ s = b.strip()
+ mproto = re.match(r"^service-object\s+(tcp|udp|icmp|tcp-udp)$", s, re.IGNORECASE)
+ msvc_eq = re.match(
+ r"^service-object\s+(tcp|udp|icmp|tcp-udp)\s+(?:destination\s+)?eq\s+(\S+)$", s, re.IGNORECASE
+ )
+ msvc_range = re.match(
+ r"^service-object\s+(tcp|udp|icmp|tcp-udp)\s+(?:destination\s+)?range\s+(\S+)\s+(\S+)$", s, re.IGNORECASE
+ )
+ if mproto:
+ protocols.append(mproto.group(1).lower())
+ elif msvc_eq:
+ ports_eq.append((msvc_eq.group(1).lower(), msvc_eq.group(2)))
+ elif msvc_range:
+ ports_range.append((msvc_range.group(1).lower(), (msvc_range.group(2), msvc_range.group(3))))
+ else:
+ continue
+ service_group_block.remove(b)
+
+ return ports_eq, ports_range, protocols
+
+
+def _consume_service_references(service_group_block: list[str]) -> list[str]:
+ """Helper to consume service-object and group-object lines from a service object group block."""
+ nested_refs: list[str] = []
+
+ for b in service_group_block:
+ s = b.strip()
+ mobj = re.match(r"^service-object\s+object\s+(\S+)$", s, re.IGNORECASE)
+ mgrp = re.match(r"^group-object\s+(\S+)$", s, re.IGNORECASE)
+
+ if mobj:
+ nested_refs.append(mobj.group(1))
+ elif mgrp:
+ nested_refs.append(mgrp.group(1))
+ else:
+ continue
+ service_group_block.remove(b)
+
+ return nested_refs
+
+
+def parse_service_object_group_block(block: list[str]) -> AsaServiceObjectGroup:
+ """Parse an object-group service block."""
+ hdr = block[0].split()
+ name = hdr[2]
+
+ # Optional global protocol set for all defined ports/ranges
+ proto_mode = None
+ if len(hdr) >= 4: # noqa: PLR2004
+ pm = hdr[3].lower()
+ if pm in ("tcp", "udp", "tcp-udp"):
+ proto_mode = pm
+ else:
+ FWOLogger.warning(f"Unsupported proto_mode '{pm}' in service object group '{name}'")
+
+ desc = _find_description(block[1:])
+
+ ports_eq: list[tuple[str, str]] = []
+ ports_range: list[tuple[str, tuple[str, str]]] = []
+ nested_refs: list[str] = []
+ protocols: list[str] = []
+
+ if proto_mode:
+ ports_eq, ports_range = _consume_port_objects(block[1:], proto_mode)
+ else:
+ ports_eq, ports_range, protocols = _consume_service_definitions(block[1:])
+ nested_refs = _consume_service_references(block[1:])
+
+ # Convert port lists to dictionaries using helper function
+ ports_eq_dict, ports_range_dict = _convert_ports_to_dicts(ports_eq, ports_range)
+
+ return AsaServiceObjectGroup(
+ name=name,
+ proto_mode=proto_mode,
+ ports_eq=ports_eq_dict,
+ ports_range=ports_range_dict,
+ nested_refs=nested_refs,
+ protocols=protocols,
+ description=desc,
+ )
+
+
+def parse_class_map_block(block: list[str]) -> ClassMap:
+ """Parse a class-map block."""
+ name = block[0].split()[1]
+ matches: list[str] = []
+
+ for b in block[1:]:
+ s = b.strip()
+ mm = re.match(r"^match\s", s, re.IGNORECASE)
+ if mm:
+ matches.append(s[mm.end() :].strip())
+
+ return ClassMap(name=name, matches=matches)
+
+
+def _parse_dns_parameters_block(block: list[str], start_idx: int) -> tuple[DnsInspectParameters, int]:
+ """
+ Parse a 'parameters' sub-block within a DNS inspect policy-map.
+ Returns (DnsInspectParameters, next_index).
+ """
+ params = DnsInspectParameters()
+ k = start_idx + 1
+ while k < len(block) and block[k].startswith(" "): # double indent
+ t = block[k].strip()
+ m1 = re.match(r"^message-length\s+maximum\s+client\s+(auto|\d+)$", t, re.IGNORECASE)
+ m2 = re.match(r"^message-length\s+maximum\s+(\d+)$", t, re.IGNORECASE)
+ m3 = re.match(r"^no\s+tcp-inspection$", t, re.IGNORECASE)
+
+ if m1:
+ v = m1.group(1).lower()
+ params.message_length_max_client = "auto" if v == "auto" else int(v)
+ elif m2:
+ params.message_length_max = int(m2.group(1))
+ elif m3:
+ params.tcp_inspection = False
+ k += 1
+ return params, k
+
+
+def parse_dns_inspect_policy_map_block(block: list[str], pm_name: str) -> PolicyMap:
+ """Parse a policy-map type inspect dns block."""
+ pm = PolicyMap(name=pm_name, type_str="inspect dns")
+ params = DnsInspectParameters()
+
+ j = 1
+ while j < len(block):
+ s = block[j].strip()
+ if s == "parameters":
+ params, j = _parse_dns_parameters_block(block, j)
+ continue
+ j += 1
+
+ pm.parameters_dns = params
+ return pm
+
+
+def _parse_policy_class_block(block: list[str], start_idx: int) -> tuple[PolicyClass | None, int]:
+ """
+ Parse a 'class ' sub-block starting at start_idx.
+ Returns (PolicyClass or None, next_index).
+ """
+ if start_idx >= len(block):
+ return None, start_idx + 1
+
+ header = block[start_idx].strip()
+ mc = re.match(r"^class\s+(\S+)$", header, re.IGNORECASE)
+ if not mc:
+ return None, start_idx + 1
+
+ class_name = mc.group(1)
+ inspections: list[InspectionAction] = []
+ idx = start_idx + 1
+ # collect lines under this class (1 indent)
+ while idx < len(block) and block[idx].startswith(" "):
+ t = block[idx].strip()
+ mi = re.match(r"^inspect\s+(\S+)(?:\s+(\S+))?$", t, re.IGNORECASE)
+ if mi:
+ inspections.append(
+ InspectionAction(protocol=mi.group(1).lower(), policy_map=(mi.group(2) if mi.group(2) else None))
+ )
+ # ignore other class-level lines for now
+ idx += 1
+
+ return PolicyClass(class_name=class_name, inspections=inspections), idx
+
+
+def parse_policy_map_block(block: list[str], pm_name: str) -> PolicyMap:
+ """Parse a regular policy-map block."""
+ pm = PolicyMap(name=pm_name)
+
+ idx = 1
+ while idx < len(block):
+ cls, next_idx = _parse_policy_class_block(block, idx)
+ if cls is not None:
+ pm.classes.append(cls)
+ idx = next_idx
+
+ return pm
+
+
+def _parse_access_list_entry_protocol(
+ parts: list[str],
+ protocol_groups: list[AsaProtocolGroup],
+ svc_objects: list[AsaServiceObject],
+ svc_obj_groups: list[AsaServiceObjectGroup],
+) -> tuple[EndpointKind, list[str]]:
+ """
+ Parse the protocol part of an access-list entry.
+ Returns (protocol EndpointKind, remaining tokens list[str]).
+ """
+ # Determine protocol
+ protocol = None
+ tokens = [] # Ensure tokens is always initialized
+ if parts[4] == "object-group":
+ group_name = parts[5]
+ if any(group.name == group_name for group in protocol_groups):
+ protocol = EndpointKind(kind="protocol-group", value=group_name)
+ elif any(group.name == group_name for group in svc_obj_groups):
+ protocol = EndpointKind(kind="service-group", value=group_name)
+ else:
+ raise ValueError(f"Unknown object-group: {group_name}")
+ tokens = parts[6:]
+ elif parts[4] == "object":
+ obj_name = parts[5]
+ if any(obj.name == obj_name for obj in svc_objects):
+ protocol = EndpointKind(kind="service", value=obj_name)
+ else:
+ raise ValueError(f"Unknown service object: {obj_name}")
+ tokens = parts[6:]
+ else:
+ protocol = EndpointKind(kind="protocol", value=parts[4].lower())
+ tokens = parts[5:]
+
+ return protocol, tokens
+
+
+def _parse_access_list_entry_dest_port(tokens: list[str], protocol: EndpointKind) -> tuple[EndpointKind, list[str]]:
+ """
+ Parse the destination port part of an access-list entry.
+ Returns (dst_port EndpointKind, remaining tokens list[str]).
+ """
+ dst_port = EndpointKind(kind="any", value="any") # Default value
+ if len(tokens) >= 2 and tokens[0] == "eq": # noqa: PLR2004
+ dst_port = EndpointKind(kind="eq", value=tokens[1])
+ tokens = tokens[2:]
+ elif len(tokens) >= 3 and tokens[0] == "range": # noqa: PLR2004
+ dst_port = EndpointKind(kind="range", value=f"{tokens[1]} {tokens[2]}")
+ tokens = tokens[3:]
+ elif len(tokens) >= 2 and tokens[0] == "object-group": # noqa: PLR2004
+ dst_port = EndpointKind(kind="service-group", value=tokens[1])
+ tokens = tokens[2:]
+ elif len(tokens) >= 2 and tokens[0] == "object": # noqa: PLR2004
+ dst_port = EndpointKind(kind="service", value=tokens[1])
+ tokens = tokens[2:]
+
+ # If protocol is a service-group and dst_port is empty, set dst_port to the group name
+ if protocol.kind == "service-group" and dst_port.value == "any":
+ dst_port = EndpointKind(kind="service-group", value=protocol.value)
+ elif protocol.kind == "service" and dst_port.value == "any":
+ dst_port = EndpointKind(kind="service", value=protocol.value)
+
+ return dst_port, tokens
+
+
+def parse_access_list_entry(
+ line: str,
+ protocol_groups: list[AsaProtocolGroup],
+ svc_objects: list[AsaServiceObject],
+ svc_obj_groups: list[AsaServiceObjectGroup],
+) -> AccessListEntry:
+ """
+ Parse an access-list entry line and return an AccessListEntry object.
+ Handles various formats as specified in the requirements.
+ """
+ # Tokenize the line after 'access-list'
+ parts = line.split()
+ acl_name = parts[1] # Access list name
+ action = parts[3].lower() # Action (permit/deny)
+
+ # Parse protocol or protocol/service object-group
+ protocol, tokens = _parse_access_list_entry_protocol(parts, protocol_groups, svc_objects, svc_obj_groups)
+
+ # Parse source endpoint
+ src, consumed = parse_endpoint(tokens)
+ tokens = tokens[consumed:]
+
+ # Parse destination endpoint
+ dst, consumed = parse_endpoint(tokens)
+ tokens = tokens[consumed:]
+
+ # Parse destination port
+ dst_port, tokens = _parse_access_list_entry_dest_port(tokens, protocol)
+
+ # Optional inactive flag
+ inactive = "inactive" in tokens
+
+ # Ensure action is either 'permit' or 'deny' for type safety
+ action_literal = "permit" if action == "permit" else "deny"
+
+ return AccessListEntry(
+ acl_name=acl_name,
+ action=action_literal,
+ protocol=protocol,
+ src=src,
+ dst=dst,
+ dst_port=dst_port,
+ inactive=inactive,
+ )
+
+
+def parse_protocol_object_group_block(block: list[str]) -> AsaProtocolGroup:
+ """Parse an object-group protocol block."""
+ name = block[0].split()[2]
+ desc = _find_description(block[1:])
+ protocols: list[str] = []
+
+ for b in block[1:]:
+ s = b.strip()
+ mproto = re.match(r"^protocol-object\s+(\S+)$", s, re.IGNORECASE)
+
+ if mproto:
+ protocols.append(mproto.group(1))
+
+ return AsaProtocolGroup(name=name, protocols=protocols, description=desc)
+
+
+def parse_icmp_object_group_block(block: list[str]) -> AsaServiceObjectGroup:
+ """Parse an object-group icmp-type block."""
+ grp_name = block[0].split()[2]
+ desc = _find_description(block[1:])
+ objects: list[str] = []
+ for b in block[1:]:
+ s = b.strip()
+ mobj = re.match(r"^icmp-object\s+(\S+)$", s, re.IGNORECASE)
+ if mobj:
+ objects.append(mobj.group(1))
+
+ return AsaServiceObjectGroup(
+ name=grp_name,
+ proto_mode=None,
+ ports_eq={"icmp": objects},
+ ports_range={},
+ nested_refs=[],
+ protocols=[],
+ description=desc,
+ )
diff --git a/roles/importer/files/importer/fw_modules/ciscoasa9/asa_rule.py b/roles/importer/files/importer/fw_modules/ciscoasa9/asa_rule.py
new file mode 100644
index 0000000000..f512558585
--- /dev/null
+++ b/roles/importer/files/importer/fw_modules/ciscoasa9/asa_rule.py
@@ -0,0 +1,205 @@
+"""
+ASA Rule and Rulebase Management
+
+This module handles the creation of rules and rulebases from ASA access lists.
+It processes ACL entries and converts them into normalized rules with proper
+service, source, and destination references.
+"""
+
+import fwo_base
+from fw_modules.ciscoasa9.asa_models import AccessList, AccessListEntry, AsaProtocolGroup, EndpointKind
+from fw_modules.ciscoasa9.asa_network import get_network_rule_endpoint
+from fw_modules.ciscoasa9.asa_service import create_any_protocol_service, create_service_for_acl_entry
+from fwo_log import FWOLogger
+from models.networkobject import NetworkObject
+from models.rule import RuleAction, RuleNormalized, RuleTrack, RuleType
+from models.rulebase import Rulebase
+from models.serviceobject import ServiceObject
+from netaddr import IPNetwork
+
+
+def create_service_for_protocol_group_entry(
+ protocol_group_name: str, protocol_groups: list[AsaProtocolGroup], service_objects: dict[str, ServiceObject]
+) -> str:
+ """
+ Resolve service reference for a protocol group.
+
+ Args:
+ protocol_group_name: Name of the protocol group
+ protocol_groups: List of protocol groups for resolving references
+ service_objects: Dictionary of service objects to update if needed
+ Returns:
+ Service reference string
+
+ """
+ allowed_protocols = []
+ for pg in protocol_groups:
+ if pg.name == protocol_group_name:
+ allowed_protocols = pg.protocols
+ break
+
+ if allowed_protocols:
+ svc_refs: list[str] = []
+ for proto in allowed_protocols:
+ svc_ref = create_any_protocol_service(proto, service_objects)
+ svc_refs.append(svc_ref)
+ return fwo_base.sort_and_join(svc_refs)
+ # Fallback if protocol group not found
+ FWOLogger.warning(f"Protocol group '{protocol_group_name}' not found. Defaulting to tcp/udp/icmp any.")
+ svc_refs = []
+ for proto in ("tcp", "udp", "icmp"):
+ svc_refs.append(create_any_protocol_service(proto, service_objects))
+ return fwo_base.sort_and_join(svc_refs)
+
+
+def resolve_service_reference_for_rule(
+ entry: AccessListEntry, protocol_groups: list[AsaProtocolGroup], service_objects: dict[str, ServiceObject]
+) -> str:
+ """
+ Resolve service reference for a rule entry.
+
+ Args:
+ entry: Access list entry
+ protocol_groups: List of protocol groups for resolving protocol-group references
+ service_objects: Dictionary of service objects to update if needed
+
+ Returns:
+ Service reference string
+
+ """
+ if entry.protocol.kind == "protocol-group":
+ # Protocol group - resolve to list of protocols
+ return create_service_for_protocol_group_entry(entry.protocol.value, protocol_groups, service_objects)
+ # Handle other protocol types using existing function
+ return create_service_for_acl_entry(entry, service_objects)
+
+
+def resolve_network_reference_for_rule(endpoint: EndpointKind, network_objects: dict[str, NetworkObject]) -> str:
+ """
+ Resolve network reference for a rule endpoint.
+
+ Args:
+ endpoint: Access list entry endpoint (src or dst)
+ network_objects: Dictionary of network objects to update if needed
+
+ Returns:
+ Network reference string
+
+ """
+ # Create network object if needed and get reference
+ network_obj = get_network_rule_endpoint(endpoint, network_objects)
+
+ # Return reference - convert subnet mask to CIDR if present
+ if hasattr(endpoint, "mask") and endpoint.mask is not None:
+ return str(IPNetwork(f"{endpoint.value}/{endpoint.mask}"))
+ return network_obj.obj_uid
+
+
+def create_rule_from_acl_entry(
+ access_list_name: str,
+ entry: AccessListEntry,
+ protocol_groups: list[AsaProtocolGroup],
+ network_objects: dict[str, NetworkObject],
+ service_objects: dict[str, ServiceObject],
+) -> RuleNormalized:
+ """
+ Create a normalized rule from an ACL entry.
+
+ Args:
+ access_list_name: Name of the access list
+ idx: Rule index (1-based)
+ entry: Access list entry to convert
+ protocol_groups: List of protocol groups for resolving references
+ network_objects: Dictionary of network objects to update if needed
+ service_objects: Dictionary of service objects to update if needed
+
+ Returns:
+ Normalized rule object
+
+ """
+ # Generate unique rule UID by hashing entry dict
+ rule_uid = fwo_base.generate_hash_from_dict(entry.model_dump())
+
+ # Resolve service reference
+ svc_ref = resolve_service_reference_for_rule(entry, protocol_groups, service_objects)
+
+ # Resolve source and destination references
+ src_ref = resolve_network_reference_for_rule(entry.src, network_objects)
+ dst_ref = resolve_network_reference_for_rule(entry.dst, network_objects)
+
+ # Create normalized rule
+ return RuleNormalized(
+ rule_num=0,
+ rule_num_numeric=0, # will be set later
+ rule_disabled=entry.inactive,
+ rule_src_neg=False,
+ rule_src=src_ref,
+ rule_src_refs=src_ref,
+ rule_dst_neg=False,
+ rule_dst=dst_ref,
+ rule_dst_refs=dst_ref,
+ rule_svc_neg=False,
+ rule_svc=svc_ref,
+ rule_svc_refs=svc_ref,
+ rule_action=RuleAction.ACCEPT if entry.action == "permit" else RuleAction.DROP,
+ rule_track=RuleTrack.NONE,
+ rule_installon=None, # gateway_uid, TODO: commented out for now to avoid duplication issues
+ rule_time=None,
+ rule_name=access_list_name,
+ rule_uid=rule_uid,
+ rule_custom_fields=None,
+ rule_implied=False,
+ rule_type=RuleType.ACCESS,
+ last_change_admin=None,
+ parent_rule_uid=None,
+ last_hit=None,
+ rule_comment=entry.description,
+ rule_src_zone=None,
+ rule_dst_zone=None,
+ rule_head_text=None,
+ )
+
+
+def build_rulebases_from_access_lists(
+ access_lists: list[AccessList],
+ mgm_uid: str,
+ protocol_groups: list[AsaProtocolGroup],
+ network_objects: dict[str, NetworkObject],
+ service_objects: dict[str, ServiceObject],
+) -> list[Rulebase]:
+ """
+ Build rulebases from ASA access lists.
+
+ Each access list becomes a separate rulebase containing normalized rules.
+ Rules are created from ACL entries with proper service, source, and destination references.
+
+ Args:
+ access_lists: List of parsed ASA access lists
+ mgm_uid: Management UID for the device
+ protocol_groups: List of protocol groups for resolving protocol-group references
+ network_objects: Dictionary of network objects to update if needed
+ service_objects: Dictionary of service objects to update if needed
+
+ Returns:
+ List of normalized rulebases
+
+ """
+ rulebases: list[Rulebase] = []
+
+ for access_list in access_lists:
+ rules: dict[str, RuleNormalized] = {}
+
+ for entry in access_list.entries:
+ rule = create_rule_from_acl_entry(
+ access_list.name, entry, protocol_groups, network_objects, service_objects
+ )
+ if rule.rule_uid is None:
+ FWOLogger.error(f"Failed to create rule UID for ACL entry: {entry}")
+ raise ValueError("Rule UID generation failed.")
+ rules[rule.rule_uid] = rule
+
+ # Create rulebase for this access list
+ rulebase = Rulebase(uid=access_list.name, name=access_list.name, mgm_uid=mgm_uid, is_global=False, rules=rules)
+ rulebases.append(rulebase)
+
+ return rulebases
diff --git a/roles/importer/files/importer/fw_modules/ciscoasa9/asa_service.py b/roles/importer/files/importer/fw_modules/ciscoasa9/asa_service.py
new file mode 100644
index 0000000000..ee3fff906b
--- /dev/null
+++ b/roles/importer/files/importer/fw_modules/ciscoasa9/asa_service.py
@@ -0,0 +1,481 @@
+"""
+ASA Service Object Management
+
+This module handles the creation and normalization of service objects from ASA configurations.
+It manages both explicit service objects/groups and implicit service objects created from
+inline ACL definitions.
+"""
+
+import fwo_base
+import fwo_const
+from fw_modules.ciscoasa9.asa_maps import name_to_port, protocol_map
+from fw_modules.ciscoasa9.asa_models import AccessListEntry, AsaServiceObject, AsaServiceObjectGroup
+from fwo_log import FWOLogger
+from models.serviceobject import ServiceObject
+
+
+def create_service_object(
+ name: str, port: int, port_end: int, protocol: str, comment: str | None = None
+) -> ServiceObject:
+ """
+ Create a normalized service object.
+
+ Args:
+ name: Service object name/UID
+ port: Start port number
+ port_end: End port number
+ protocol: Protocol name (tcp, udp, icmp, etc.)
+ comment: Optional description
+
+ Returns:
+ Normalized ServiceObject instance
+
+ """
+ return ServiceObject(
+ svc_uid=name,
+ svc_name=name,
+ svc_port=port,
+ svc_port_end=port_end,
+ svc_color=fwo_const.DEFAULT_COLOR,
+ svc_typ="simple",
+ ip_proto=protocol_map.get(protocol, 0),
+ svc_comment=comment,
+ )
+
+
+def create_protocol_service_object(name: str, protocol: str, comment: str | None = None) -> ServiceObject:
+ """
+ Create a service object for a protocol without specific ports.
+
+ Args:
+ name: Service object name/UID
+ protocol: Protocol name
+ comment: Optional description
+
+ Returns:
+ Normalized ServiceObject instance
+
+ """
+ return ServiceObject(
+ svc_uid=name,
+ svc_name=name,
+ svc_color=fwo_const.DEFAULT_COLOR,
+ svc_typ="simple",
+ ip_proto=protocol_map.get(protocol, 0),
+ svc_comment=comment,
+ )
+
+
+def create_service_group_object(name: str, member_refs: list[str], comment: str | None = None) -> ServiceObject:
+ """
+ Create a service group object.
+
+ Args:
+ name: Group name/UID
+ member_refs: List of member service object references
+ comment: Optional description
+
+ Returns:
+ Normalized ServiceObject group instance
+
+ """
+ return ServiceObject(
+ svc_uid=name,
+ svc_name=name,
+ svc_typ="group",
+ svc_member_names=fwo_base.sort_and_join(member_refs),
+ svc_member_refs=fwo_base.sort_and_join(member_refs),
+ svc_color=fwo_const.DEFAULT_COLOR,
+ svc_comment=comment,
+ )
+
+
+def normalize_service_objects(service_objects: list[AsaServiceObject]) -> dict[str, ServiceObject]:
+ """
+ Normalize individual service objects from ASA configuration.
+
+ Args:
+ service_objects: List of parsed ASA service objects
+
+ Returns:
+ Dictionary of normalized service objects keyed by svc_uid
+
+ """
+ normalized: dict[str, ServiceObject] = {}
+
+ for svc in service_objects:
+ if svc.dst_port_eq:
+ # Service with specific port (eq)
+ port = svc.dst_port_eq
+ if not port.isdigit():
+ port = name_to_port[port]["port"]
+
+ obj = create_service_object(svc.name, int(port), int(port), svc.protocol, svc.description)
+ normalized[svc.name] = obj
+
+ elif svc.dst_port_range:
+ # Service with port range
+ start, end = svc.dst_port_range
+ if not start.isdigit():
+ start = name_to_port[start]["port"]
+ if not end.isdigit():
+ end = name_to_port[end]["port"]
+ obj = create_service_object(svc.name, int(start), int(end), svc.protocol, svc.description)
+ normalized[svc.name] = obj
+
+ else:
+ # Protocol-only service (no specific ports)
+ obj = create_protocol_service_object(svc.name, svc.protocol, svc.description)
+ normalized[svc.name] = obj
+
+ return normalized
+
+
+def create_protocol_any_service_objects() -> dict[str, ServiceObject]:
+ """
+ Create default 'any' service objects for common protocols.
+
+ Returns:
+ Dictionary of protocol-any service objects
+
+ """
+ service_objects: dict[str, ServiceObject] = {}
+
+ for proto in ("tcp", "udp", "icmp", "ip"):
+ obj_name = f"any-{proto}"
+ obj = ServiceObject(
+ svc_uid=obj_name,
+ svc_name=obj_name,
+ svc_port=0,
+ svc_port_end=65535,
+ svc_color=fwo_const.DEFAULT_COLOR,
+ svc_typ="simple",
+ ip_proto=protocol_map.get(proto, 0),
+ svc_comment=f"any {proto}",
+ )
+ service_objects[obj_name] = obj
+
+ return service_objects
+
+
+def create_service_for_port(port: str, proto: str, service_objects: dict[str, ServiceObject]) -> str:
+ """
+ Create a service object for a single port and protocol if it doesn't exist.
+
+ Args:
+ port: Port number or name
+ proto: Protocol name
+ service_objects: Dictionary to update with new service object
+
+ Returns:
+ Service object name/UID
+
+ """
+ if proto == "icmp":
+ obj = create_protocol_service_object(f"icmp-{port}", "icmp", None)
+ service_objects[obj.svc_uid] = obj
+ return obj.svc_uid
+ obj_name = f"{port}-{proto}"
+ if obj_name not in service_objects:
+ description = None
+ if not port.isdigit():
+ description = name_to_port[port]["description"]
+ port = name_to_port[port]["port"]
+ obj = create_service_object(obj_name, int(port), int(port), proto, description)
+ service_objects[obj_name] = obj
+ return obj_name
+
+
+def create_service_for_port_range(
+ port_range: tuple[str, str], proto: str, service_objects: dict[str, ServiceObject]
+) -> str:
+ """
+ Create a service object for a port range and protocol if it doesn't exist.
+
+ Args:
+ port_range: Tuple of (start_port, end_port)
+ proto: Protocol name
+ service_objects: Dictionary to update with new service object
+
+ Returns:
+ Service object name/UID
+
+ """
+ obj_name = (
+ f"{port_range[0]}-{port_range[1]}-{proto}" if port_range[0] != port_range[1] else f"{port_range[0]}-{proto}"
+ )
+ if obj_name not in service_objects:
+ start, end = port_range
+ description = None
+ if not start.isdigit():
+ description = f"{start}: {name_to_port[start]['description']}"
+ start = name_to_port[start]["port"]
+ if not end.isdigit():
+ if not description:
+ description = f"{end}: {name_to_port[end]['description']}"
+ else:
+ description += f"; {end}: {name_to_port[end]['description']}"
+ end = name_to_port[end]["port"]
+ obj = create_service_object(obj_name, int(start), int(end), proto, description)
+ service_objects[obj_name] = obj
+ return obj_name
+
+
+def create_any_protocol_service(proto: str, service_objects: dict[str, ServiceObject]) -> str:
+ """
+ Create an 'any' service object for a protocol if it doesn't exist.
+
+ Args:
+ proto: Protocol name
+ service_objects: Dictionary to update with new service object
+
+ Returns:
+ Service object name/UID
+
+ """
+ obj_name = f"any-{proto}"
+ if obj_name not in service_objects:
+ port_range = (0, 65535) if proto in ("tcp", "udp") else (None, None)
+ obj = ServiceObject(
+ svc_uid=obj_name,
+ svc_name=obj_name,
+ svc_port=port_range[0],
+ svc_port_end=port_range[1],
+ svc_color=fwo_const.DEFAULT_COLOR,
+ svc_typ="simple",
+ ip_proto=protocol_map.get(proto, 0),
+ svc_comment=f"any {proto}",
+ )
+ service_objects[obj_name] = obj
+ return obj_name
+
+
+def create_service_for_protocol_entry_with_single_protocol(
+ entry: AccessListEntry, service_objects: dict[str, ServiceObject]
+) -> str:
+ """
+ Create service reference for a protocol entry with set protocol.
+
+ Args:
+ entry: Access list entry with protocol
+ service_objects: Dictionary to update with new service objects
+ Returns:
+ Service reference string (single object or delimited list)
+
+ """
+ if entry.dst_port.kind == "eq":
+ # Single port (e.g., 'eq 443' or 'eq https')
+ return create_service_for_port(entry.dst_port.value, entry.protocol.value, service_objects)
+
+ if entry.dst_port.kind == "range":
+ # Port range (e.g., 'range 1024 65535')
+ ports = entry.dst_port.value.split() # expecting "start end"
+ return create_service_for_port_range((ports[0], ports[1]), entry.protocol.value, service_objects)
+
+ if entry.dst_port.kind == "any":
+ # Any port for the protocol
+ return create_any_protocol_service(entry.protocol.value, service_objects)
+
+ if entry.dst_port.kind in ("service", "service-group"):
+ # Reference to existing service object/group
+ return entry.dst_port.value
+ # Default to any port for the protocol
+ return create_any_protocol_service(entry.protocol.value, service_objects)
+
+
+def create_service_for_protocol_entry(entry: AccessListEntry, service_objects: dict[str, ServiceObject]) -> str:
+ """
+ Create service reference for a protocol group entry.
+
+ Args:
+ entry: Access list entry with protocol group
+ service_objects: Dictionary to update with new service objects
+ Returns:
+ Service reference string (single object or delimited list)
+
+ """
+ if entry.protocol.value in ("tcp", "udp", "icmp"):
+ return create_service_for_protocol_entry_with_single_protocol(entry, service_objects)
+
+ if entry.protocol.value == "ip":
+ svc_refs = [create_any_protocol_service(proto, service_objects) for proto in protocol_map]
+
+ reference_string = fwo_base.sort_and_join(svc_refs)
+ # create a service group for all protocols
+ service_objects["ANY"] = ServiceObject(
+ svc_uid="ANY",
+ svc_name="ANY",
+ svc_color=fwo_const.DEFAULT_COLOR,
+ svc_typ="group",
+ svc_member_names=reference_string,
+ svc_member_refs=reference_string,
+ )
+ return "ANY"
+ # Unknown protocol, default to any for the protocol
+ return create_any_protocol_service(entry.protocol.value, service_objects)
+
+
+def create_service_for_acl_entry(entry: AccessListEntry, service_objects: dict[str, ServiceObject]) -> str:
+ """
+ Create service object(s) for an ACL entry and return the service reference.
+
+ Args:
+ entry: Access list entry with protocol and port information
+ service_objects: Dictionary to update with new service objects
+
+ Returns:
+ Service reference string (single object or delimited list)
+
+ """
+ if entry.protocol.kind == "protocol":
+ return create_service_for_protocol_entry(entry, service_objects)
+
+ if entry.protocol.kind in ("service-group", "service"):
+ # Reference to service object or group
+ return entry.protocol.value
+
+ if entry.protocol.kind == "protocol-group":
+ # Protocol group - will be resolved by caller
+ return entry.protocol.value
+
+ # Default to all common protocols
+ svc_refs = [create_any_protocol_service(proto, service_objects) for proto in ("tcp", "udp", "icmp")]
+ return fwo_base.sort_and_join(svc_refs)
+
+
+def process_mixed_protocol_eq_ports(
+ group: AsaServiceObjectGroup, service_objects: dict[str, ServiceObject]
+) -> list[str]:
+ """Process equal ports for mixed protocol groups."""
+ obj_names: list[str] = []
+ for protos, eq_ports in group.ports_eq.items():
+ for proto in protos.split("-"): # handles "tcp-udp"
+ for port in eq_ports:
+ obj_name = create_service_for_port(port, proto, service_objects)
+ obj_names.append(obj_name)
+ return obj_names
+
+
+def process_mixed_protocol_range_ports(
+ group: AsaServiceObjectGroup, service_objects: dict[str, ServiceObject]
+) -> list[str]:
+ """Process port ranges for mixed protocol groups."""
+ obj_names: list[str] = []
+ for proto, ranges in group.ports_range.items():
+ for pr in ranges:
+ obj_name = create_service_for_port_range(pr, proto, service_objects)
+ obj_names.append(obj_name)
+ return obj_names
+
+
+def process_fully_enabled_protocols(
+ group: AsaServiceObjectGroup, service_objects: dict[str, ServiceObject]
+) -> list[str]:
+ """Process protocols that allow all ports."""
+ obj_names: list[str] = []
+ for proto in group.protocols:
+ obj_name = create_any_protocol_service(proto, service_objects)
+ obj_names.append(obj_name)
+ return obj_names
+
+
+def process_mixed_protocol_group(group: AsaServiceObjectGroup, service_objects: dict[str, ServiceObject]) -> list[str]:
+ """Process a mixed protocol service group."""
+ obj_names: list[str] = []
+
+ # Process ports_eq (single port values)
+ obj_names.extend(process_mixed_protocol_eq_ports(group, service_objects))
+
+ # Process ports_range (port ranges)
+ obj_names.extend(process_mixed_protocol_range_ports(group, service_objects))
+
+ # Process any-protocol references
+ obj_names.extend(process_fully_enabled_protocols(group, service_objects))
+
+ # Process nested references
+ obj_names.extend(group.nested_refs)
+
+ return obj_names
+
+
+def process_single_protocol_eq_ports(
+ protocol: str, ports: list[str], service_objects: dict[str, ServiceObject]
+) -> list[str]:
+ """Process equal ports for single protocol groups."""
+ obj_names: list[str] = []
+ for port in ports:
+ obj_name = create_service_for_port(port, protocol, service_objects)
+ obj_names.append(obj_name)
+ return obj_names
+
+
+def process_single_protocol_range_ports(
+ protocol: str, ranges: list[tuple[str, str]], service_objects: dict[str, ServiceObject]
+) -> list[str]:
+ """Process port ranges for single protocol groups."""
+ obj_names: list[str] = []
+ for obj_range in ranges:
+ obj_name = create_service_for_port_range(obj_range, protocol, service_objects)
+ obj_names.append(obj_name)
+ return obj_names
+
+
+def process_single_protocol_group(group: AsaServiceObjectGroup, service_objects: dict[str, ServiceObject]) -> list[str]:
+ """Process a single-protocol service group."""
+ obj_names: list[str] = []
+
+ if not group.proto_mode:
+ raise ValueError(f"Service object group {group.name} missing proto_mode")
+
+ for protocol in group.proto_mode.split("-"): # handles "tcp-udp"
+ if protocol not in protocol_map:
+ raise ValueError(f"Unknown protocol in service object group: {protocol}")
+
+ # Process single port values
+ obj_names.extend(
+ process_single_protocol_eq_ports(protocol, group.ports_eq.get(group.proto_mode, []), service_objects)
+ )
+
+ # Process port ranges
+ obj_names.extend(
+ process_single_protocol_range_ports(protocol, group.ports_range.get(group.proto_mode, []), service_objects)
+ )
+
+ # Process nested references
+ obj_names.extend(group.nested_refs)
+
+ return obj_names
+
+
+def normalize_service_object_groups(
+ service_groups: list[AsaServiceObjectGroup], service_objects: dict[str, ServiceObject]
+) -> dict[str, ServiceObject]:
+ """
+ Normalize service object groups from ASA configuration.
+
+ Args:
+ service_groups: List of parsed ASA service object groups
+ service_objects: Existing service objects dictionary to update
+
+ Returns:
+ Updated service objects dictionary including groups
+
+ """
+ # Process each service group
+ for group in service_groups:
+ if group.proto_mode:
+ obj_names = process_single_protocol_group(group, service_objects)
+ else:
+ obj_names = process_mixed_protocol_group(group, service_objects)
+
+ # look for duplicates and remove them
+ unique_obj_names = list(set(obj_names))
+ if len(unique_obj_names) < len(obj_names):
+ duplicates = [x for x in obj_names if obj_names.count(x) > 1]
+ FWOLogger.debug(f"Removed duplicate service object references found in group {group.name}: {duplicates}")
+
+ # Create the group object
+ group_obj = create_service_group_object(group.name, unique_obj_names, group.description)
+ service_objects[group.name] = group_obj
+
+ return service_objects
diff --git a/roles/importer/files/importer/fw_modules/ciscoasa9/fwcommon.py b/roles/importer/files/importer/fw_modules/ciscoasa9/fwcommon.py
new file mode 100644
index 0000000000..a72e97017a
--- /dev/null
+++ b/roles/importer/files/importer/fw_modules/ciscoasa9/fwcommon.py
@@ -0,0 +1,338 @@
+#!/usr/bin/env python3
+"""
+ASA Configuration Import Module
+
+This module handles the main configuration import workflow for Cisco ASA devices.
+It provides functions to connect to devices, retrieve configurations, and
+orchestrate the normalization process.
+"""
+
+import time
+from pathlib import Path
+from typing import Any
+
+from fw_modules.ciscoasa9.asa_normalize import normalize_config
+from fw_modules.ciscoasa9.asa_parser import parse_asa_config
+from fwo_base import write_native_config_to_file
+from fwo_exceptions import FwoImporterError
+from fwo_log import FWOLogger
+from model_controllers.fwconfigmanagerlist_controller import FwConfigManagerListController
+from model_controllers.import_state_controller import ImportStateController
+from model_controllers.management_controller import ManagementController
+from models.fw_common import FwCommon
+from scrapli.driver import GenericDriver
+
+
+class CiscoAsa9Common(FwCommon):
+ def get_config(
+ self, config_in: FwConfigManagerListController, import_state: ImportStateController
+ ) -> tuple[int, FwConfigManagerListController]:
+ return get_config(config_in, import_state)
+
+
+def _connect_to_device(mgm_details: ManagementController) -> GenericDriver:
+ """
+ Establish SSH connection to the device.
+
+ Args:
+ mgm_details: ManagementController object with connection details.
+
+ Returns:
+ Connected GenericDriver instance.
+
+ """
+ device: dict[str, Any] = {
+ "host": mgm_details.hostname,
+ "port": mgm_details.port,
+ "auth_username": mgm_details.import_user,
+ "auth_password": mgm_details.secret,
+ "auth_strict_key": False,
+ "transport_options": {"open_cmd": ["-o", "KexAlgorithms=+diffie-hellman-group14-sha1"]},
+ }
+ conn = GenericDriver(**device)
+ conn.open()
+ return conn
+
+
+def _prepare_virtual_asa(conn: GenericDriver) -> None:
+ """
+ Connect to ASA module on virtual device.
+
+ Args:
+ conn: Active connection to the device.
+
+ """
+ conn.send_command("connect module 1 console\n")
+ time.sleep(2)
+ conn.send_command("\n")
+ time.sleep(2)
+
+
+def _get_current_prompt(conn: GenericDriver) -> str:
+ """
+ Get the current prompt from the device.
+
+ Args:
+ conn: Active connection to the device.
+
+ Returns:
+ Current prompt as string.
+
+ """
+ try:
+ return conn.get_prompt().strip()
+ except Exception:
+ FWOLogger.warning("Could not get current prompt")
+ return ""
+
+
+def _ensure_enable_mode(conn: GenericDriver, mgm_details: ManagementController) -> None:
+ """
+ Ensure device is in enabled mode.
+
+ Args:
+ conn: Active connection to the device.
+ mgm_details: ManagementController object with enable password.
+
+ Raises:
+ FwoImporterError: If unable to enter enabled mode.
+
+ """
+ current_prompt = _get_current_prompt(conn)
+ FWOLogger.debug(f"Current prompt: {current_prompt}")
+
+ if current_prompt.endswith(">"):
+ FWOLogger.debug("Device is in user mode, entering enable mode")
+ try:
+ conn.send_interactive([("enable", "Password", False), (mgm_details.cloud_client_secret, "#", True)])
+ except Exception as e:
+ FWOLogger.warning(f"Could not enter enable mode: {e}")
+ current_prompt = _get_current_prompt(conn)
+ if current_prompt == "":
+ error_msg = "Could not retrieve prompt after attempting to enter enable mode."
+ FWOLogger.error(error_msg)
+ raise FwoImporterError(error_msg) from e
+ if not current_prompt.endswith("#"):
+ raise FwoImporterError("Failed to enter enable mode.")
+
+ current_prompt = _get_current_prompt(conn)
+ if not current_prompt.endswith("#"):
+ error_msg = f"Not in enabled mode (prompt: {current_prompt})."
+ FWOLogger.error(error_msg)
+ raise FwoImporterError(error_msg)
+
+ FWOLogger.debug("Device is in enabled mode")
+
+
+def _get_running_config(conn: GenericDriver) -> str:
+ """
+ Retrieve running configuration from device.
+
+ Args:
+ conn: Active connection to the device.
+
+ Returns:
+ Running configuration as string.
+
+ """
+ try:
+ conn.send_command("terminal pager 0")
+ except Exception as e:
+ FWOLogger.warning(f"Could not disable paging: {e}")
+
+ response = conn.send_interactive([("show running", ": end", False)], timeout_ops=600)
+ return response.result.strip()
+
+
+def _safe_close_connection(conn: GenericDriver | None) -> None:
+ """
+ Safely close connection with proper cleanup.
+
+ Args:
+ conn: Connection to close (can be None).
+
+ """
+ if conn is None:
+ return
+
+ if not conn.isalive():
+ FWOLogger.debug("Connection already closed")
+ return
+
+ try:
+ conn.send_command("exit")
+ except Exception as e:
+ FWOLogger.warning(f"Could not exit session cleanly: {e}")
+
+ try:
+ conn.close()
+ except Exception as e:
+ FWOLogger.warning(f"Error closing connection: {e}")
+
+
+def _handle_connection_error(e: Exception, mgm_details: ManagementController, attempt: int, max_retries: int) -> str:
+ """
+ Build detailed error message for connection failures.
+
+ Args:
+ e: The exception that occurred.
+ mgm_details: Management details for context.
+ attempt: Current attempt number.
+ max_retries: Maximum retry attempts.
+
+ Returns:
+ Formatted error message.
+
+ """
+ error_msg = f"Error connecting to device {mgm_details.hostname} (attempt {attempt + 1}/{max_retries}): {e}"
+
+ error_str = str(e).lower()
+ if "password" in error_str or "enable" in error_str:
+ error_msg += "\nPossible causes: incorrect password, or device already in use by another import session"
+ elif "prompt" in error_str or "timeout" in error_str:
+ error_msg += (
+ "\nPossible causes: concurrent access from multiple import processes, or device not responding as expected"
+ )
+
+ return error_msg
+
+
+def _log_retry_attempt(attempt: int, max_retries: int) -> None:
+ """
+ Log retry attempt with exponential backoff.
+
+ Args:
+ attempt: Current attempt number (0-indexed).
+ max_retries: Maximum number of retries.
+
+ """
+ if attempt > 0:
+ backoff_time = 2 ** (attempt + 1)
+ FWOLogger.info(f"Retry attempt {attempt + 1}/{max_retries} after {backoff_time} seconds backoff")
+ time.sleep(backoff_time)
+ else:
+ FWOLogger.debug(f"Connection attempt {attempt + 1}/{max_retries}")
+
+
+def _retrieve_config_from_device(conn: GenericDriver, mgm_details: ManagementController, is_virtual_asa: bool) -> str:
+ """
+ Retrieve configuration from connected device.
+
+ Args:
+ conn: Active connection to the device.
+ mgm_details: ManagementController object with connection details.
+ is_virtual_asa: Whether this is a virtual ASA device.
+
+ Returns:
+ Running configuration as string.
+
+ """
+ if is_virtual_asa:
+ _prepare_virtual_asa(conn)
+
+ _ensure_enable_mode(conn, mgm_details)
+ return _get_running_config(conn)
+
+
+def _attempt_connection(mgm_details: ManagementController, is_virtual_asa: bool, attempt: int, max_retries: int) -> str:
+ """
+ Attempt a single connection to retrieve configuration.
+
+ Args:
+ mgm_details: ManagementController object with connection details.
+ is_virtual_asa: Whether this is a virtual ASA device.
+ attempt: Current attempt number.
+ max_retries: Maximum retry attempts.
+
+ Returns:
+ Running configuration as string.
+
+ Raises:
+ FwoImporterError: If connection fails and should not retry.
+
+ """
+ conn = None
+
+ try:
+ conn = _connect_to_device(mgm_details)
+ config = _retrieve_config_from_device(conn, mgm_details, is_virtual_asa)
+ _safe_close_connection(conn)
+
+ FWOLogger.debug(f"Successfully connected after {attempt + 1} attempt(s)")
+
+ return config
+
+ except Exception as e:
+ _safe_close_connection(conn)
+ error_msg = _handle_connection_error(e, mgm_details, attempt, max_retries)
+
+ if attempt < max_retries - 1:
+ FWOLogger.warning(error_msg + "\nWill retry...")
+ else:
+ FWOLogger.error(error_msg + "\nMax retries exhausted.")
+ raise FwoImporterError(error_msg) from e
+
+
+def load_config_from_management(mgm_details: ManagementController, is_virtual_asa: bool, max_retries: int = 10) -> str:
+ """
+ Load ASA configuration from the management device using SSH with exponential backoff retry.
+
+ Args:
+ mgm_details: ManagementController object with connection details.
+ is_virtual_asa: Boolean indicating if the device is a virtual ASA inside of a FirePower instance.
+ max_retries: Maximum number of retry attempts (default: 10).
+
+ Returns:
+ The raw configuration as a string.
+
+ Raises:
+ FwoImporterError: After all retry attempts are exhausted.
+
+ """
+ for attempt in range(max_retries):
+ _log_retry_attempt(attempt, max_retries)
+
+ try:
+ return _attempt_connection(mgm_details, is_virtual_asa, attempt, max_retries)
+ except FwoImporterError as _:
+ if attempt >= max_retries - 1:
+ raise
+ raise FwoImporterError(f"Failed to connect to device {mgm_details.hostname} after {max_retries} attempts")
+
+
+def get_config(
+ config_in: FwConfigManagerListController, import_state: ImportStateController
+) -> tuple[int, FwConfigManagerListController]:
+ """
+ Retrieve and parse the ASA configuration.
+
+ Args:
+ config_in: Configuration input details.
+ importState: Current import state.
+
+ Returns:
+ A tuple containing the status code and the parsed configuration.
+
+ """
+ FWOLogger.debug("starting ciscoAsa9/get_config")
+
+ is_virtual_asa = import_state.state.mgm_details.device_type_name == "Cisco Asa on FirePower"
+
+ if config_in.native_config_is_empty():
+ # for debugging, use: raw_config = load_config_from_file("test_asa.conf")
+ raw_config = load_config_from_management(import_state.state.mgm_details, is_virtual_asa)
+ config2import = parse_asa_config(raw_config)
+ config_in.native_config = config2import.model_dump()
+
+ write_native_config_to_file(import_state.state, config_in.native_config)
+
+ normalize_config(config_in, import_state.state)
+
+ return 0, config_in
+
+
+def load_config_from_file(filename: str) -> str:
+ """Load ASA configuration from a file."""
+ path = Path("roles", "importer", "files", "importer", "fw_modules", "ciscoasa9", filename)
+ with open(path) as f:
+ return f.read()
diff --git a/roles/importer/files/importer/fw_modules/ciscoasa9/test_asa.conf b/roles/importer/files/importer/fw_modules/ciscoasa9/test_asa.conf
new file mode 100644
index 0000000000..f9e5c8a1d6
--- /dev/null
+++ b/roles/importer/files/importer/fw_modules/ciscoasa9/test_asa.conf
@@ -0,0 +1,178 @@
+: Saved
+
+:
+: Serial Number: JAD204201WA
+: Hardware: ASA5506, 4096 MB RAM, CPU Atom C2000 series 1250 MHz, 1 CPU (4 cores)
+:
+ASA Version 9.16(4)
+!
+hostname ciscoasa
+enable password ***** pbkdf2
+service-module 1 keepalive-timeout 4
+service-module 1 keepalive-counter 6
+service-module sfr keepalive-timeout 4
+service-module sfr keepalive-counter 6
+
+! ===== DEMO: Names =====
+names
+name 11.22.33.44 DEMO_OLD_OBJ
+name 11.22.33.44 DEMO_OLD_OBJ2 description abc
+name 203.0.113.10 DEMO-WEB
+!
+object network DEMO_OBJ_HOST_WEB
+ host 203.0.113.10
+ description Public webserver
+!
+object network DEMO_OBJ_HOST_WEB_NAT
+ nat (inside,outside) static 203.0.113.10
+!
+object network DEMO_OBJ_USER_NET
+ subnet 192.0.2.0 255.255.255.0
+ description Inside users (demo)
+!
+object network DEMO_OBJ_FQDN
+ fqdn v4 www.example.com
+ description FQDN object (demo)
+!
+object network DEMO_OBJ_IP_RANGE
+ range 192.0.2.1 192.0.2.150
+!
+object service DEMO_SVC_TCP_8443
+ service tcp destination eq 8443
+ description Alt HTTPS (demo)
+!
+object service DEMO_SVC_UDP_RANGE
+ service udp destination range 2000 2010
+ description UDP port range (demo)
+!
+object service DEMO_UDP_NAME_RANGE
+ service udp destination range netbios-ns netbios-dgm
+ description UDP port range via names (demo)
+!
+object service echo-request
+ service icmp echo 0
+!
+object service echo-reply
+ service icmp echo-reply 0
+!
+object service icmp-fragmentation
+ service icmp unreachable 4
+!
+object service time-exceeded
+ service icmp time-exceeded 0
+!
+object service GRE
+ service gre
+ description GRE Tunnel Protokoll
+!
+object-group network DEMO_OG_NET_BASE
+ description Base network objects (demo)
+ network-object object DEMO_OBJ_USER_NET
+!
+object-group network DEMO_OG_NET_DMZ
+ network-object object DEMO_OBJ_HOST_WEB
+ network-object host 192.0.2.22
+!
+object-group network DEMO_OG_NET_ALL
+ description All internal/DMZ nets (demo)
+ group-object DEMO_OG_NET_BASE
+ group-object DEMO_OG_NET_DMZ
+ network-object object DEMO_OBJ_FQDN
+!
+object-group network DEMO_OG_IPV6
+ network-object 2001:db8:abcd::/40
+ network-object 2001::/12
+ network-object host 2001:db8:abcd::1
+ network-object 192.0.2.0 255.255.255.0
+ network-object host 192.0.2.10
+ network-object object DEMO_OBJ_IP_RANGE
+!
+object-group service DEMO_OG_SVC_MIXED
+ description Mixed service refs (demo)
+ service-object object DEMO_SVC_TCP_8443
+ service-object tcp destination eq https
+ service-object udp destination eq domain
+ service-object udp destination range 2000 2005
+ service-object udp destination range netbios-ns netbios-dgm
+!
+object-group service DEMO_OG_SVC_PORTS tcp-udp
+ description Common app ports (demo)
+ port-object eq 135
+ port-object range 138 139
+ port-object eq ssh
+ port-object eq sqlnet
+!
+object-group service DEMO_OG_SVC_TCP
+ description Common TCP ports (demo)
+ service-object tcp-udp destination eq https
+!
+object-group service DEMO_OG_SVC_ICMP
+ description ICMP and a service object (demo)
+ service-object icmp
+ service-object object DEMO_SVC_TCP_8443
+!
+object-group icmp-type DEMO_OG_SVC_ICMP
+ icmp-object echo
+ icmp-object echo-reply
+!
+object-group protocol DEMO_OG_PROTO_TCP
+ protocol-object tcp
+!
+object-group protocol DEMO_OG_PROTO_TCP_UDP
+ protocol-object tcp
+ protocol-object udp
+!
+object network DEMO_OBJ_EXT_A
+ host 198.51.100.100
+ description External peer A (demo)
+!
+object network DEMO_OBJ_EXT_B
+ host 198.51.100.101
+ description External peer B (demo)
+!
+object service DEMO_SVC_TCP_22hdsfkjskfjhsdkf tcp
+ service destination eq 22
+ description SSH (demo)
+!
+object service DEMO_SVC_TCP_22sadahdsfkjskfjhsdkf tcp-udp
+ service destination eq 22
+ description SSH (demo)
+!
+access-list DEMO_ACL extended permit ip host 192.0.2.10 host 203.0.113.10
+access-list DEMO_ACL extended permit ip object DEMO_OBJ_USER_NET object DEMO_OBJ_HOST_WEB
+access-list DEMO_ACL extended permit ip object-group DEMO_OG_NET_ALL any4
+access-list DEMO_ACL extended permit ip object-group DEMO_OG_NET_BASE object-group DEMO_OG_NET_DMZ
+
+access-list DEMO_ACL extended permit object DEMO_SVC_TCP_8443 object DEMO_OBJ_USER_NET object DEMO_OBJ_HOST_WEB
+access-list DEMO_ACL extended permit object-group DEMO_OG_PROTO_TCP any object DEMO_OBJ_HOST_WEB eq 8443 inactive
+access-list DEMO_ACL extended permit object-group DEMO_OG_PROTO_TCP object-group DEMO_OG_NET_BASE object-group DEMO_OG_NET_DMZ object-group DEMO_OG_SVC_PORTS
+access-list DEMO_ACL extended permit object-group DEMO_OG_PROTO_TCP object-group DEMO_OG_NET_BASE object-group DEMO_OG_NET_DMZ object DEMO_SVC_TCP_8443
+access-list DEMO_ACL extended permit object-group DEMO_OG_SVC_PORTS object-group DEMO_OG_NET_BASE object DEMO_OBJ_HOST_WEB
+
+access-list DEMO_ACL extended permit icmp any object-group DEMO_OG_NET_DMZ echo-reply
+access-list DEMO_ACL extended permit icmp object-group DEMO_OG_NET_BASE object-group DEMO_OG_NET_DMZ
+access-list DEMO_ACL extended permit icmp object DEMO_OBJ_USER_NET any4
+
+access-list DEMO_ACL extended permit tcp object-group DEMO_OG_NET_BASE object-group DEMO_OG_NET_DMZ eq ssh
+access-list DEMO_ACL extended permit udp object DEMO_OBJ_USER_NET object DEMO_OBJ_HOST_WEB eq bootps
+access-list DEMO_ACL extended permit udp object DEMO_OBJ_USER_NET object-group DEMO_OG_NET_DMZ
+access-list DEMO_ACL extended permit tcp any object DEMO_OBJ_HOST_WEB eq https inactive
+access-list DEMO_ACL extended permit tcp any object DEMO_OBJ_HOST_WEB eq 8443 inactive
+access-list DEMO_ACL extended permit tcp object-group DEMO_OG_NET_BASE object-group DEMO_OG_NET_DMZ eq 2345
+access-list DEMO_ACL extended permit tcp object-group DEMO_OG_NET_BASE object-group DEMO_OG_NET_DMZ range 2000 2005
+access-list DEMO_ACL extended permit tcp object-group DEMO_OG_NET_BASE object-group DEMO_OG_NET_DMZ object-group DEMO_OG_SVC_MIXED
+access-list DEMO_ACL extended permit tcp object-group DEMO_OG_NET_BASE any eq https
+access-list DEMO_ACL extended deny tcp object DEMO_OBJ_EXT_A object-group DEMO_OG_NET_DMZ eq https
+
+access-list acl1 extended permit ip host 1.2.3.4 host 2.3.4.5
+access-list acl1 extended permit ip host 1.2.3.4 host 2.3.4.6
+
+access-list acl2 extended permit tcp 10.0.0.0 255.255.255.128 object DEMO_OBJ_USER_NET eq 8834
+access-list acl2 extended permit icmp 10.0.0.0 255.255.255.240 10.0.120.64 255.255.255.224
+
+access-list acl3 extended permit udp object DEMO_OBJ_HOST_WEB 10.0.120.64 255.255.255.224 range 135 netbios-ssn
+
+!
+access-group DEMO_ACL in interface inside_2
+
+: end
\ No newline at end of file
diff --git a/roles/importer/files/importer/dummyroutermanagement1/__init__.py b/roles/importer/files/importer/fw_modules/ciscofirepowerdomain7ff/__init__.py
similarity index 100%
rename from roles/importer/files/importer/dummyroutermanagement1/__init__.py
rename to roles/importer/files/importer/fw_modules/ciscofirepowerdomain7ff/__init__.py
diff --git a/roles/importer/files/importer/fw_modules/ciscofirepowerdomain7ff/fwcommon.py b/roles/importer/files/importer/fw_modules/ciscofirepowerdomain7ff/fwcommon.py
new file mode 100644
index 0000000000..e652fc97b6
--- /dev/null
+++ b/roles/importer/files/importer/fw_modules/ciscofirepowerdomain7ff/fwcommon.py
@@ -0,0 +1,10 @@
+from model_controllers.fwconfigmanagerlist_controller import FwConfigManagerListController
+from model_controllers.import_state_controller import ImportStateController
+from models.fw_common import FwCommon
+
+
+class CiscoFirepowerDomain7ffCommon(FwCommon):
+ def get_config(
+ self, config_in: FwConfigManagerListController, import_state: ImportStateController
+ ) -> tuple[int, FwConfigManagerListController]:
+ raise NotImplementedError("Cisco Firepower Domain 7ff is not supported yet in the new python importer.")
diff --git a/roles/importer/files/importer/fortiadom5ff/__init__.py b/roles/importer/files/importer/fw_modules/fortiadom5ff/__init__.py
similarity index 100%
rename from roles/importer/files/importer/fortiadom5ff/__init__.py
rename to roles/importer/files/importer/fw_modules/fortiadom5ff/__init__.py
diff --git a/roles/importer/files/importer/checkpointR8x/discovery_logging.conf b/roles/importer/files/importer/fw_modules/fortiadom5ff/discovery_logging.conf
similarity index 100%
rename from roles/importer/files/importer/checkpointR8x/discovery_logging.conf
rename to roles/importer/files/importer/fw_modules/fortiadom5ff/discovery_logging.conf
diff --git a/roles/importer/files/importer/fw_modules/fortiadom5ff/fmgr_base.py b/roles/importer/files/importer/fw_modules/fortiadom5ff/fmgr_base.py
new file mode 100644
index 0000000000..abc3b1cd1a
--- /dev/null
+++ b/roles/importer/files/importer/fw_modules/fortiadom5ff/fmgr_base.py
@@ -0,0 +1,59 @@
+from typing import Any
+
+from fwo_log import FWOLogger
+from services.service_provider import ServiceProvider
+
+
+# TODO: unused functions - remove?
+def set_alerts_for_missing_objects(
+ objects_not_found: list[str], import_id: int, rule_uid: str | None, object_type: str | None, mgm_id: int
+):
+ for obj in objects_not_found:
+ if obj in {"all", "Original"}:
+ continue
+
+ service_provider = ServiceProvider()
+ global_state = service_provider.get_global_state()
+
+ api_call = global_state.import_state.api_call
+
+ api_call.create_data_issue(obj_name=obj, severity=1, rule_uid=rule_uid, mgm_id=mgm_id, object_type=object_type)
+
+ desc = "found a broken network object reference '" + obj + "' "
+ if object_type is not None:
+ desc += "(type=" + object_type + ") "
+ desc += "in rule with UID '" + str(rule_uid) + "'"
+ api_call.set_alert(
+ import_id=import_id,
+ title="object reference error",
+ mgm_id=mgm_id,
+ severity=1,
+ description=desc,
+ source="import",
+ alert_code=16,
+ )
+
+
+def lookup_obj_in_tables(
+ el: str, object_tables: list[list[dict[str, Any]]], name_key: str, uid_key: str, ref_list: list[str]
+) -> bool:
+ break_flag = False
+ found = False
+
+ for tab in object_tables:
+ if break_flag:
+ found = True
+ break
+ for obj in tab:
+ if obj[name_key] == el:
+ if uid_key in obj:
+ ref_list.append(obj[uid_key])
+ # in case of internet-service-object we find no uid field, but custom q_origin_key_
+ elif "q_origin_key" in obj:
+ ref_list.append("q_origin_key_" + str(obj["q_origin_key"]))
+ else:
+ FWOLogger.error("found object without expected uid")
+ break_flag = True
+ found = True
+ break
+ return found
diff --git a/roles/importer/files/importer/fw_modules/fortiadom5ff/fmgr_consts.py b/roles/importer/files/importer/fw_modules/fortiadom5ff/fmgr_consts.py
new file mode 100644
index 0000000000..421e8e0d79
--- /dev/null
+++ b/roles/importer/files/importer/fw_modules/fortiadom5ff/fmgr_consts.py
@@ -0,0 +1,23 @@
+nw_obj_types = [
+ "firewall/address",
+ "firewall/address6",
+ "firewall/addrgrp",
+ "firewall/addrgrp6",
+ "firewall/ippool",
+ "firewall/vip",
+ "system/external-resource",
+ "firewall/wildcard-fqdn/custom",
+ "firewall/wildcard-fqdn/group",
+]
+
+svc_obj_types = [
+ "application/list",
+ "application/group",
+ "application/categories",
+ "application/custom",
+ "firewall/service/custom",
+ "firewall/service/group",
+]
+
+nat_types = ["central/dnat", "central/dnat6", "firewall/central-snat-map"]
+user_obj_types = ["user/local", "user/group"]
diff --git a/roles/importer/files/importer/fw_modules/fortiadom5ff/fmgr_getter.py b/roles/importer/files/importer/fw_modules/fortiadom5ff/fmgr_getter.py
new file mode 100644
index 0000000000..1dd6186e55
--- /dev/null
+++ b/roles/importer/files/importer/fw_modules/fortiadom5ff/fmgr_getter.py
@@ -0,0 +1,207 @@
+# library for API get functions
+import json
+from typing import Any
+
+import fwo_globals
+import requests
+from fwo_exceptions import (
+ FwApiCallFailedError,
+ FwLoginFailedError,
+ FwLogoutFailedError,
+ FwoUnknownDeviceForManagerError,
+)
+from fwo_log import FWOLogger
+from models.management import Management
+
+
+def api_call(url: str, command: str, json_payload: dict[str, Any], sid: str, method: str = "") -> dict[str, Any]:
+ request_headers = {"Content-Type": "application/json"}
+ if sid != "":
+ json_payload.update({"session": sid})
+ if command != "":
+ for p in json_payload["params"]:
+ p.update({"url": command})
+ if method == "":
+ method = "get"
+ json_payload.update({"method": method})
+
+ r = requests.post(url, data=json.dumps(json_payload), headers=request_headers, verify=fwo_globals.verify_certs)
+ result_json = r.json()
+ if "result" not in result_json or len(result_json["result"]) == 0:
+ if "pass" in json.dumps(json_payload):
+ raise FwApiCallFailedError(f"error while sending api_call containing credential information to url {url!s}")
+ result_data = r.json()["result"][0]["status"] if "status" in r.json()["result"][0] else r.json()["result"][0]
+ exception_text = (
+ f"error while sending api_call to url {url!s} with payload {json.dumps(json_payload, indent=2)}"
+ )
+ exception_text += (
+ f" and headers: {json.dumps(request_headers, indent=2)}, result={json.dumps(result_data, indent=2)}"
+ )
+ raise FwApiCallFailedError(exception_text)
+ if "pass" in json.dumps(json_payload):
+ FWOLogger.debug("api_call containing credential information to url " + str(url) + " - not logging query", 3)
+ else:
+ FWOLogger.debug(
+ "api_call to url "
+ + str(url)
+ + " with payload "
+ + json.dumps(json_payload, indent=2)
+ + " and headers: "
+ + json.dumps(request_headers, indent=2),
+ 3,
+ )
+
+ return result_json
+
+
+def login(user: str, password: str, base_url: str) -> str | None:
+ payload: dict[str, Any] = {
+ "id": 1,
+ "params": [
+ {
+ "data": [
+ {
+ "user": user,
+ "passwd": password,
+ }
+ ]
+ }
+ ],
+ }
+ try:
+ response = api_call(base_url, "sys/login/user", payload, "", method="exec")
+ except Exception:
+ raise FwLoginFailedError("FortiManager login ERROR: url=" + base_url) from None
+ if "session" not in response: # leaving out payload as it contains pwd
+ raise FwLoginFailedError("FortiManager login ERROR (no sid): url=" + base_url) from None
+ return response["session"]
+
+
+def logout(v_url: str, sid: str, method: str = "exec"):
+ payload: dict[str, Any] = {"params": [{}]}
+
+ response = api_call(v_url, "sys/logout", payload, sid, method=method)
+ if (
+ "result" in response
+ and "status" in response["result"][0]
+ and "code" in response["result"][0]["status"]
+ and response["result"][0]["status"]["code"] == 0
+ ):
+ FWOLogger.debug("successfully logged out")
+ else:
+ raise FwLogoutFailedError(
+ "fmgr_getter ERROR: did not get status code 0 when logging out, "
+ "api call: url: " + str(v_url) + ", + payload: " + str(payload)
+ )
+
+
+def update_config_with_fortinet_api_call(
+ config_json: list[dict[str, Any]],
+ sid: str,
+ api_base_url: str,
+ api_path: str,
+ result_name: str,
+ payload: dict[str, Any] | None = None,
+ options: list[Any] | None = None,
+ limit: int = 150,
+ method: str = "get",
+):
+ if options is None:
+ options = []
+ if payload is None:
+ payload = {}
+ offset = 0
+ limit = int(limit)
+ returned_new_objects = True
+ full_result: list[Any] = []
+ while returned_new_objects:
+ range_ = [offset, limit]
+ if payload == {}:
+ payload = {"params": [{"range": range_}]}
+ elif "params" in payload and len(payload["params"]) > 0:
+ payload["params"][0].update({"range": range_})
+
+ # adding options
+ if len(options) > 0:
+ payload["params"][0].update({"option": options})
+
+ result = fortinet_api_call(sid, api_base_url, api_path, payload=payload, method=method)
+ full_result.extend(result)
+ offset += limit
+ if len(result) < limit:
+ returned_new_objects = False
+
+ full_result = parse_special_fortinet_api_results(result_name, full_result)
+
+ config_json.append({"type": result_name, "data": full_result})
+
+
+def parse_special_fortinet_api_results(result_name: str, full_result: list[Any]) -> list[Any]:
+ if result_name == "nw_obj_global_firewall/internet-service-basic":
+ if len(full_result) > 0 and "response" in full_result[0] and "results" in full_result[0]["response"]:
+ full_result = full_result[0]["response"]["results"]
+ else:
+ FWOLogger.warning(f"did not get expected results for {result_name} - setting to empty list")
+ full_result = []
+ return full_result
+
+
+def fortinet_api_call(
+ sid: str, api_base_url: str, api_path: str, payload: dict[str, Any] | None = None, method: str = "get"
+) -> list[Any]:
+ if payload is None:
+ payload = {}
+ if payload == {}:
+ payload = {"params": [{}]}
+ api_result = api_call(api_base_url, api_path, payload, sid, method=method)
+ plain_result: dict[str, Any] = api_result["result"][0]
+ if "data" in plain_result:
+ result = plain_result["data"]
+ if isinstance(
+ result, dict
+ ): # code implicitly expects result to be a list, but some fmgr data results are dicts
+ result: list[Any] = [result]
+ else:
+ result = []
+ return result
+
+
+def get_devices_from_manager(adom_mgm_details: Management, sid: str, fm_api_url: str) -> dict[str, Any]:
+ device_vdom_dict: dict[str, dict[str, str]] = {}
+
+ device_results = fortinet_api_call(sid, fm_api_url, "/dvmdb/adom/" + adom_mgm_details.domain_name + "/device")
+ for mgm_details_device in adom_mgm_details.devices:
+ if not mgm_details_device["importDisabled"]:
+ found_fmgr_device = False
+ for fmgr_device in device_results:
+ found_fmgr_device = parse_device_and_vdom(
+ fmgr_device, mgm_details_device, device_vdom_dict, found_fmgr_device
+ )
+ if not found_fmgr_device:
+ raise FwoUnknownDeviceForManagerError(
+ "Could not find " + mgm_details_device["name"] + " in Fortimanager Config"
+ ) from None
+
+ return device_vdom_dict
+
+
+def parse_device_and_vdom(
+ fmgr_device: dict[str, Any],
+ mgm_details_device: dict[str, Any],
+ device_vdom_dict: dict[str, dict[str, str]],
+ found_fmgr_device: bool,
+) -> bool:
+ if "vdom" in fmgr_device:
+ for fmgr_vdom in fmgr_device["vdom"]:
+ if mgm_details_device["name"] == fmgr_device["name"] + "_" + fmgr_vdom["name"]:
+ found_fmgr_device = True
+ if fmgr_device["name"] in device_vdom_dict:
+ device_vdom_dict[fmgr_device["name"]].update({fmgr_vdom["name"]: ""})
+ else:
+ device_vdom_dict.update({fmgr_device["name"]: {fmgr_vdom["name"]: ""}})
+ return found_fmgr_device
+
+
+def get_policy_packages_from_manager(sid: str, fm_api_url: str, adom: str = "") -> list[Any]:
+ url = "/pm/pkg/global" if adom == "" else "/pm/pkg/adom/" + adom
+ return fortinet_api_call(sid, fm_api_url, url)
diff --git a/roles/importer/files/importer/fw_modules/fortiadom5ff/fmgr_gw_networking.py b/roles/importer/files/importer/fw_modules/fortiadom5ff/fmgr_gw_networking.py
new file mode 100644
index 0000000000..cd46dfe146
--- /dev/null
+++ b/roles/importer/files/importer/fw_modules/fortiadom5ff/fmgr_gw_networking.py
@@ -0,0 +1,371 @@
+import traceback
+from functools import cmp_to_key
+from typing import Any
+
+import fmgr_getter
+from fwo_log import FWOLogger
+from model_controllers.interface_controller import Interface
+from model_controllers.route_controller import Route, get_route_destination
+from netaddr import IPAddress, IPNetwork
+
+# Constants
+SUBFETCH_HIDDEN = "subfetch hidden"
+INTERFACES_PER_DEVICE = "interfaces_per_device/"
+
+
+def process_ipv4_routing(
+ native_config: dict[str, Any], normalized_config: dict[str, Any], dev_id: int, full_vdom_name: str
+) -> None:
+ """Process IPv4 routing table for a single device/vdom."""
+ routing_key = "routing-table-ipv4/" + full_vdom_name
+
+ if routing_key not in native_config:
+ FWOLogger.warning("could not find routing data " + routing_key)
+ FWOLogger.warning("native configs contains the following keys " + str(native_config.keys()))
+ return
+
+ for route in native_config[routing_key]:
+ norm_route = Route(
+ dev_id,
+ route["gateway"],
+ route["ip_mask"],
+ interface=route["interface"],
+ metric=route["metric"],
+ distance=route["distance"],
+ )
+ normalized_config["routing"].append(norm_route)
+
+
+def process_ipv6_routing(
+ native_config: dict[str, Any], normalized_config: dict[str, Any], dev_id: int, full_vdom_name: str
+) -> None:
+ """Process IPv6 routing table for a single device/vdom."""
+ routing_key = "routing-table-ipv6/" + full_vdom_name
+
+ if routing_key not in native_config:
+ FWOLogger.warning("could not find routing data " + routing_key)
+ if FWOLogger.is_debug_level(6):
+ FWOLogger.warning("native configs contains the following keys " + str(native_config.keys()))
+ return
+
+ for route in native_config[routing_key]:
+ norm_route = Route(
+ dev_id,
+ route["gateway"],
+ route["ip_mask"],
+ metric=route["metric"],
+ distance=route["distance"],
+ interface=route["interface"],
+ ip_version=6,
+ )
+ normalized_config["routing"].append(norm_route)
+
+
+def process_device_interfaces(
+ native_config: dict[str, Any], normalized_config: dict[str, Any], dev_id: int, full_vdom_name: str
+) -> None:
+ """Process interfaces for a single device/vdom."""
+ interfaces_key = INTERFACES_PER_DEVICE + full_vdom_name
+
+ if interfaces_key not in native_config:
+ return
+
+ for interface in native_config[interfaces_key]:
+ # Process IPv6 interface
+ if "ipv6" in interface and "ip6-address" in interface["ipv6"] and interface["ipv6"]["ip6-address"] != "::/0":
+ ipv6, netmask_bits = interface["ipv6"]["ip6-address"].split("/")
+ norm_if_v6 = Interface(dev_id, interface["name"], IPAddress(ipv6), netmask_bits, ip_version=6)
+ normalized_config["interfaces"].append(norm_if_v6)
+
+ # Process IPv4 interface
+ if "ip" in interface and interface["ip"] != ["0.0.0.0", "0.0.0.0"]:
+ ipv4 = IPAddress(interface["ip"][0])
+ netmask_bits = IPAddress(interface["ip"][1]).netmask_bits()
+ norm_if_v4 = Interface(dev_id, interface["name"], ipv4, netmask_bits, ip_version=4)
+ normalized_config["interfaces"].append(norm_if_v4)
+
+
+def normalize_network_data(
+ native_config: dict[str, Any], normalized_config: dict[str, Any], mgm_details: dict[str, Any]
+) -> None:
+ """Normalize network data by processing routing tables and interfaces for all devices."""
+ normalized_config.update({"routing": [], "interfaces": []})
+
+ for dev_id, _, _, full_vdom_name in get_all_dev_names(mgm_details["devices"]):
+ # Process routing tables
+ process_ipv4_routing(native_config, normalized_config, dev_id, full_vdom_name)
+ process_ipv6_routing(native_config, normalized_config, dev_id, full_vdom_name)
+
+ # Process interfaces
+ process_device_interfaces(native_config, normalized_config, dev_id, full_vdom_name)
+
+ # Sort routing table by destination
+ normalized_config["routing"].sort(key=get_route_destination, reverse=True)
+
+
+def get_matching_route(destination_ip: IPAddress, routing_table: list[dict[str, Any]]) -> dict[str, Any] | None:
+ def route_matches(ip: IPAddress, destination: str) -> bool:
+ ip_n = IPNetwork(ip).cidr
+ dest_n = IPNetwork(destination).cidr
+ return ip_n in dest_n or dest_n in ip_n
+
+ if len(routing_table) == 0:
+ FWOLogger.error("src nat behind interface: encountered empty routing table")
+ return None
+
+ for route in routing_table:
+ if route_matches(destination_ip, route["destination"]):
+ return route
+
+ FWOLogger.warning("src nat behind interface: found no matching route in routing table - no default route?!")
+ return None
+
+
+def get_ip_of_interface(interface: str, interface_list: list[dict[str, Any]] | None = None) -> str | None:
+ if interface_list is None:
+ interface_list = []
+ interface_details = next((sub for sub in interface_list if sub["name"] == interface), None)
+
+ if interface_details is not None and "ipv4" in interface_details:
+ return interface_details["ipv4"]
+ return None
+
+
+def sort_reverse(ar_in: list[dict[str, Any]], key: str) -> list[dict[str, Any]]:
+ def comp(left: dict[str, Any], right: dict[str, Any]) -> int:
+ l_submask = int(left[key].split("/")[1])
+ r_submask = int(right[key].split("/")[1])
+ return l_submask - r_submask
+
+ return sorted(ar_in, key=cmp_to_key(comp), reverse=True)
+
+
+# strip off last part of a string separated by separator
+def strip_off_last_part(string_in: str, separator: str = "_") -> str:
+ string_out = string_in
+ if separator in string_in: # strip off final _xxx part
+ str_ar = string_in.split(separator)
+ str_ar.pop()
+ string_out = separator.join(str_ar)
+ return string_out
+
+
+def get_last_part(string_in: str, separator: str = "_") -> str:
+ string_out = ""
+ if separator in string_in: # strip off _vdom_name
+ str_ar = string_in.split(separator)
+ string_out = str_ar.pop()
+ return string_out
+
+
+def get_plain_device_names_without_vdoms(devices: list[dict[str, Any]]) -> list[str]:
+ device_array: list[str] = []
+ for dev in devices:
+ dev_name = strip_off_last_part(dev["name"])
+ if dev_name not in device_array:
+ device_array.append(dev_name)
+ return device_array
+
+
+# only getting one vdom as currently assuming routing to be
+# the same for all vdoms on a device
+def get_device_names_plus_one_vdom(devices: list[dict[str, Any]]) -> list[list[str]]:
+ device_array: list[str] = []
+ device_array_with_vdom: list[list[str]] = []
+ for dev in devices:
+ dev_name = strip_off_last_part(dev["name"])
+ vdom_name = get_last_part(dev["name"])
+ if dev_name not in device_array:
+ device_array.append(dev_name)
+ device_array_with_vdom.append([dev_name, vdom_name])
+ return device_array_with_vdom
+
+
+# getting devices and their vdom names
+def get_device_plus_full_vdom_names(devices: list[dict[str, Any]]) -> list[list[str]]:
+ device_array_with_vdom: list[list[str]] = []
+ for dev in devices:
+ dev_name = strip_off_last_part(dev["name"])
+ vdom_name = dev["name"]
+ device_array_with_vdom.append([dev_name, vdom_name])
+ return device_array_with_vdom
+
+
+# getting devices and their vdom names
+def get_all_dev_names(devices: list[dict[str, Any]]) -> list[list[Any]]:
+ device_array_with_vdom: list[list[Any]] = []
+ for dev in devices:
+ dev_id = dev["id"]
+ dev_name = strip_off_last_part(dev["name"])
+ plain_vdom_name = get_last_part(dev["name"])
+ full_vdom_name = dev["name"]
+ device_array_with_vdom.append([dev_id, dev_name, plain_vdom_name, full_vdom_name])
+ return device_array_with_vdom
+
+
+# get network information (currently only used for source nat)
+def create_interfaces_payload(plain_vdom_name: str) -> dict[str, Any]:
+ """Create payload for fetching interface information."""
+ return {
+ "id": 1,
+ "params": [
+ {
+ "fields": ["name", "ip"],
+ "filter": ["vdom", "==", plain_vdom_name],
+ "sub fetch": {
+ "client-options": {SUBFETCH_HIDDEN: 1},
+ "dhcp-snooping-server-list": {SUBFETCH_HIDDEN: 1},
+ "egress-queues": {SUBFETCH_HIDDEN: 1},
+ "ipv6": {
+ "fields": ["ip6-address"],
+ "sub fetch": {
+ "dhcp6-iapd-list": {SUBFETCH_HIDDEN: 1},
+ "ip6-delegated-prefix-list": {SUBFETCH_HIDDEN: 1},
+ "ip6-extra-addr": {SUBFETCH_HIDDEN: 1},
+ "ip6-prefix-list": {SUBFETCH_HIDDEN: 1},
+ "vrrp6": {SUBFETCH_HIDDEN: 1},
+ },
+ },
+ "l2tp-client-settings": {SUBFETCH_HIDDEN: 1},
+ "secondaryip": {SUBFETCH_HIDDEN: 1},
+ "tagging": {SUBFETCH_HIDDEN: 1},
+ "vrrp": {SUBFETCH_HIDDEN: 1},
+ "wifi-networks": {SUBFETCH_HIDDEN: 1},
+ },
+ }
+ ],
+ }
+
+
+def fetch_device_interfaces(
+ sid: str,
+ fm_api_url: str,
+ native_config: dict[str, Any],
+ plain_dev_name: str,
+ plain_vdom_name: str,
+ full_vdom_name: str,
+ limit: int,
+) -> None:
+ """Fetch interface information for a single device."""
+ try:
+ all_interfaces_payload = create_interfaces_payload(plain_vdom_name)
+ # The API call expects a list but we need to work with a dict,
+ # so we work around this by using the pattern from the original code
+ temp_result: list[dict[str, Any]] = []
+ fmgr_getter.update_config_with_fortinet_api_call(
+ temp_result,
+ sid,
+ fm_api_url,
+ "/pm/config/device/" + plain_dev_name + "/global/system/interface",
+ INTERFACES_PER_DEVICE + full_vdom_name,
+ payload=all_interfaces_payload,
+ limit=limit,
+ method="get",
+ )
+ # Extract the data from the result and store in native_config
+ for item in temp_result:
+ if item["type"] == INTERFACES_PER_DEVICE + full_vdom_name:
+ native_config[INTERFACES_PER_DEVICE + full_vdom_name] = item["data"]
+ except Exception:
+ FWOLogger.warning(
+ "error while getting interfaces of device "
+ + plain_vdom_name
+ + ", vdom="
+ + plain_vdom_name
+ + ", ignoring, traceback: "
+ + str(traceback.format_exc())
+ )
+
+
+def fetch_routing_table(
+ sid: str,
+ fm_api_url: str,
+ native_config: dict[str, Any],
+ adom_name: str,
+ plain_dev_name: str,
+ plain_vdom_name: str,
+ full_vdom_name: str,
+ ip_version: str,
+ limit: int,
+) -> None:
+ """Fetch routing table for a specific IP version and device."""
+ payload: dict[str, Any] = {
+ "params": [
+ {
+ "data": {
+ "target": ["adom/" + adom_name + "/device/" + plain_dev_name],
+ "action": "get",
+ "resource": "/api/v2/monitor/router/" + ip_version + "/select?&vdom=" + plain_vdom_name,
+ }
+ }
+ ]
+ }
+
+ try:
+ routing_helper: list[dict[str, Any]] = []
+ routing_table: list[Any] = []
+ fmgr_getter.update_config_with_fortinet_api_call(
+ routing_helper,
+ sid,
+ fm_api_url,
+ "/sys/proxy/json",
+ "routing-table-" + ip_version + "/" + full_vdom_name,
+ payload=payload,
+ limit=limit,
+ method="exec",
+ )
+
+ # Extract routing table data from the result
+ routing_key = "routing-table-" + ip_version + "/" + full_vdom_name
+ for item in routing_helper:
+ if item["type"] == routing_key:
+ routing_data = item["data"]
+ if len(routing_data) > 0 and "response" in routing_data[0] and "results" in routing_data[0]["response"]:
+ routing_table = routing_data[0]["response"]["results"]
+ else:
+ FWOLogger.warning(
+ "got empty " + ip_version + " routing table from device " + full_vdom_name + ", ignoring"
+ )
+ routing_table = []
+ break
+ except Exception:
+ FWOLogger.warning("could not get routing table for device " + full_vdom_name + ", ignoring")
+ routing_table = []
+
+ # Store the routing table
+ native_config.update({"routing-table-" + ip_version + "/" + full_vdom_name: routing_table})
+
+
+def get_interfaces_and_routing(
+ sid: str, fm_api_url: str, native_config: dict[str, Any], adom_name: str, devices: list[dict[str, Any]], limit: int
+) -> None:
+ """Get network information (interfaces and routing) for all devices."""
+ device_array = get_all_dev_names(devices)
+
+ for _, plain_dev_name, plain_vdom_name, full_vdom_name in device_array:
+ FWOLogger.info("dev_name: " + plain_dev_name + ", full vdom_name: " + full_vdom_name)
+
+ # Fetch device interfaces
+ fetch_device_interfaces(sid, fm_api_url, native_config, plain_dev_name, plain_vdom_name, full_vdom_name, limit)
+
+ # Fetch routing information for both IP versions
+ for ip_version in ["ipv4", "ipv6"]:
+ fetch_routing_table(
+ sid,
+ fm_api_url,
+ native_config,
+ adom_name,
+ plain_dev_name,
+ plain_vdom_name,
+ full_vdom_name,
+ ip_version,
+ limit,
+ )
+
+
+def get_device_from_package(package_name: str, mgm_details: dict[str, Any]) -> str | None:
+ for dev in mgm_details["devices"]:
+ if dev["local_rulebase_name"] == package_name:
+ return dev["id"]
+ FWOLogger.debug('get_device_from_package - could not find device for package "' + package_name + '"')
+ return None
diff --git a/roles/importer/files/importer/fw_modules/fortiadom5ff/fmgr_network.py b/roles/importer/files/importer/fw_modules/fortiadom5ff/fmgr_network.py
new file mode 100644
index 0000000000..b1e88aa7ca
--- /dev/null
+++ b/roles/importer/files/importer/fw_modules/fortiadom5ff/fmgr_network.py
@@ -0,0 +1,311 @@
+import ipaddress
+from typing import Any
+
+from fw_modules.fortiadom5ff.fmgr_zone import find_zones_in_normalized_config
+from fwo_base import sort_and_join_refs
+from fwo_const import ANY_IP_END, ANY_IP_START, LIST_DELIMITER, NAT_POSTFIX
+from fwo_exceptions import FwoImporterErrorInconsistenciesError
+from fwo_log import FWOLogger
+
+
+def normalize_network_objects(
+ native_config: dict[str, Any],
+ normalized_config_adom: dict[str, Any],
+ normalized_config_global: dict[str, Any],
+ nw_obj_types: list[str],
+) -> None:
+ nw_objects: list[dict[str, Any]] = []
+
+ if "objects" not in native_config:
+ return # no objects to normalize
+ # objects dicts have type as toplevel key due to rewrite_native_config_obj_type_as_key
+ for current_obj_type in native_config["objects"]:
+ if not (current_obj_type in nw_obj_types and "data" in native_config["objects"][current_obj_type]):
+ continue
+ for obj_orig in native_config["objects"][current_obj_type]["data"]:
+ normalize_network_object(
+ obj_orig,
+ nw_objects,
+ normalized_config_adom,
+ normalized_config_global,
+ native_config["objects"],
+ current_obj_type,
+ )
+
+ if native_config.get("is-super-manager", False):
+ # finally add "Original" network object for natting (only in global domain)
+ original_obj_name = "Original"
+ original_obj_uid = "Original"
+ nw_objects.append(
+ create_network_object(
+ name=original_obj_name,
+ obj_type="network",
+ ip=ANY_IP_START,
+ ip_end=ANY_IP_END,
+ uid=original_obj_uid,
+ zone="global",
+ color="black",
+ comment='"original" network object created by FWO importer for NAT purposes',
+ )
+ )
+
+ normalized_config_adom.update({"network_objects": nw_objects})
+
+
+def get_obj_member_refs_list(
+ obj_orig: dict[str, Any], native_config_objects: dict[str, Any], current_obj_type: str
+) -> list[str]:
+ obj_member_refs_list: list[str] = []
+ for member_name in obj_orig["member"]:
+ for obj_type in native_config_objects:
+ if exclude_object_types_in_member_ref_search(obj_type, current_obj_type):
+ continue
+ for potential_member in native_config_objects[obj_type]["data"]:
+ if potential_member["name"] == member_name:
+ obj_member_refs_list.append(potential_member.get("uuid", potential_member["name"])) # noqa: PERF401
+ if len(obj_member_refs_list) != len(obj_orig["member"]):
+ raise FwoImporterErrorInconsistenciesError(
+ f"Member inconsistent for object {obj_orig['name']}, found members={obj_orig['member']!s} and member_refs={obj_member_refs_list!s}"
+ )
+ return obj_member_refs_list
+
+
+def exclude_object_types_in_member_ref_search(obj_type: str, current_obj_type: str) -> bool:
+ # TODO: expand for all kinds of missmatches in group and member
+ skip_member_ref_loop = False
+ if current_obj_type.endswith("firewall/addrgrp") and obj_type.endswith("firewall/ippool"):
+ skip_member_ref_loop = True
+ return skip_member_ref_loop
+
+
+def normalize_network_object(
+ obj_orig: dict[str, Any],
+ nw_objects: list[dict[str, Any]],
+ normalized_config_adom: dict[str, Any],
+ normalized_config_global: dict[str, Any],
+ native_config_objects: dict[str, Any],
+ current_obj_type: str,
+) -> None:
+ obj: dict[str, Any] = {}
+ obj.update({"obj_name": obj_orig["name"]})
+ if "subnet" in obj_orig: # ipv4 object
+ _parse_subnet(obj, obj_orig)
+ elif "ip6" in obj_orig: # ipv6 object
+ normalize_network_object_ipv6(obj_orig, obj)
+ elif "member" in obj_orig: # addrgrp4, TODO for addrgrp6 change obj_typ to 'group_v6' and adjust obj_member_refs
+ member_name_list: list[str] = obj_orig["member"]
+ member_ref_list: list[str] = get_obj_member_refs_list(obj_orig, native_config_objects, current_obj_type)
+ sorted_member_refs, sorted_member_names = sort_and_join_refs(
+ list(zip(member_ref_list, member_name_list, strict=False))
+ )
+ obj.update({"obj_typ": "group"})
+ obj.update({"obj_member_names": sorted_member_names})
+ obj.update({"obj_member_refs": sorted_member_refs})
+ elif "startip" in obj_orig: # ippool object
+ obj.update({"obj_typ": "ip_range"})
+ obj.update({"obj_ip": obj_orig["startip"]})
+ obj.update({"obj_ip_end": obj_orig["endip"]})
+ elif "start-ip" in obj_orig: # standard ip range object
+ obj.update({"obj_typ": "ip_range"})
+ obj.update({"obj_ip": obj_orig["start-ip"]})
+ obj.update({"obj_ip_end": obj_orig["end-ip"]})
+ elif "extip" in obj_orig: # vip object, simplifying to a single ip
+ normalize_vip_object(obj_orig, obj, nw_objects)
+ elif "wildcard-fqdn" in obj_orig or "fqdn" in obj_orig: # domain or wildcard-domain
+ obj.update({"obj_typ": "domain"})
+ obj.update({"obj_ip": ANY_IP_START})
+ obj.update({"obj_ip_end": ANY_IP_END})
+ elif "q_origin_key" in obj_orig:
+ obj.update({"obj_typ": "dynamic_net_obj"})
+ obj.update({"obj_ip": ANY_IP_START})
+ obj.update({"obj_ip_end": ANY_IP_END})
+ else: # unknown types
+ obj.update({"obj_typ": "network"})
+ obj.update({"obj_ip": ANY_IP_START})
+ obj.update({"obj_ip_end": ANY_IP_END})
+
+ # if obj_ip_end is not define, set it to obj_ip (assuming host)
+ if obj.get("obj_ip_end") is None and obj.get("obj_typ") == "host":
+ obj["obj_ip_end"] = obj.get("obj_ip")
+
+ obj.update({"obj_comment": obj_orig.get("comment")})
+ # TODO: deal with all other colors (will be currently ignored)
+ # we would need a list of fortinet color codes, maybe:
+ # https://community.fortinet.com/t5/Support-Forum/Object-color-codes-for-CLI/td-p/249479
+ obj.update({"obj_color": "black"})
+
+ obj.update(
+ {"obj_uid": obj_orig.get("uuid", obj_orig["name"])}
+ ) # using name as fallback, but this should not happen
+
+ associated_interfaces = find_zones_in_normalized_config(
+ obj_orig.get("associated-interface", []), normalized_config_adom, normalized_config_global
+ )
+ obj.update({"obj_zone": LIST_DELIMITER.join(associated_interfaces)})
+
+ nw_objects.append(obj)
+
+
+def _parse_subnet(obj: dict[str, Any], obj_orig: dict[str, Any]) -> None:
+ ipa = ipaddress.ip_network(str(obj_orig["subnet"][0]) + "/" + str(obj_orig["subnet"][1]))
+ if ipa.num_addresses > 1:
+ obj.update({"obj_typ": "network"})
+ else:
+ obj.update({"obj_typ": "host"})
+ obj.update({"obj_ip": str(ipa.network_address)})
+ obj.update({"obj_ip_end": str(ipa.broadcast_address)})
+
+
+def normalize_network_object_ipv6(obj_orig: dict[str, Any], obj: dict[str, Any]) -> None:
+ ipa = ipaddress.ip_network(obj_orig["ip6"])
+ if ipa.num_addresses > 1:
+ obj.update({"obj_typ": "network"})
+ else:
+ obj.update({"obj_typ": "host"})
+ obj.update({"obj_ip": str(ipa.network_address)})
+ obj.update({"obj_ip_end": str(ipa.broadcast_address)})
+
+
+def normalize_vip_object(obj_orig: dict[str, Any], obj: dict[str, Any], nw_objects: list[dict[str, Any]]) -> None:
+ obj_zone = "global"
+ obj.update({"obj_typ": "host"})
+ if "extip" not in obj_orig or len(obj_orig["extip"]) == 0:
+ FWOLogger.error("vip (extip): found empty extip field for " + obj_orig["name"])
+ else:
+ if len(obj_orig["extip"]) > 1:
+ FWOLogger.warning(
+ "vip (extip): found more than one extip, just using the first one for " + obj_orig["name"]
+ )
+ set_ip_in_obj(obj, obj_orig["extip"][0]) # resolving nat range if there is one
+ nat_obj: dict[str, Any] = {}
+ nat_obj.update({"obj_typ": "host"})
+ nat_obj.update({"obj_color": "black"})
+ nat_obj.update({"obj_comment": "FWO-auto-generated nat object for VIP"})
+ if (
+ "obj_ip_end" in obj
+ ): # this obj is a range - include the end ip in name and uid as well to avoid akey conflicts
+ nat_obj.update({"obj_ip_end": str(obj["obj_ip_end"])})
+
+ normalize_vip_object_nat_ip(obj_orig, obj, nat_obj)
+
+ if "obj_ip_end" not in nat_obj:
+ nat_obj.update({"obj_ip_end": str(obj["obj_nat_ip"])})
+
+ if (
+ "associated-interface" in obj_orig and len(obj_orig["associated-interface"]) > 0
+ ): # and obj_orig['associated-interface'][0] != 'any':
+ obj_zone = obj_orig["associated-interface"][0]
+ nat_obj.update({"obj_zone": obj_zone})
+ if (
+ nat_obj not in nw_objects
+ ): # rare case when a destination nat is down for two different orig ips to the same dest ip
+ nw_objects.append(nat_obj)
+
+
+def normalize_vip_object_nat_ip(obj_orig: dict[str, Any], obj: dict[str, Any], nat_obj: dict[str, Any]) -> None:
+ # now dealing with the nat ip obj (mappedip)
+ if "mappedip" not in obj_orig or len(obj_orig["mappedip"]) == 0:
+ FWOLogger.warning("vip (extip): found empty mappedip field for " + obj_orig["name"])
+ return
+
+ if len(obj_orig["mappedip"]) > 1:
+ FWOLogger.warning("vip (extip): found more than one mappedip, just using the first one for " + obj_orig["name"])
+ nat_ip = obj_orig["mappedip"][0]
+ set_ip_in_obj(nat_obj, str(nat_ip))
+ obj.update({"obj_nat_ip": str(nat_obj["obj_ip"])}) # save nat ip in vip obj
+ if (
+ "obj_ip_end" in nat_obj
+ ): # this nat obj is a range - include the end ip in name and uid as well to avoid akey conflicts
+ obj.update({"obj_nat_ip_end": str(nat_obj["obj_ip_end"])}) # save nat ip in vip obj
+ nat_obj.update({"obj_name": nat_obj["obj_ip"] + "-" + nat_obj["obj_ip_end"] + NAT_POSTFIX})
+ else:
+ obj.update({"obj_nat_ip_end": str(nat_obj["obj_ip"])}) # assuming host with obj_nat_ip_end = obj_nat_ip
+ nat_obj.update({"obj_name": nat_obj["obj_ip"] + NAT_POSTFIX})
+ nat_obj.update({"obj_uid": nat_obj["obj_name"]})
+ ###### range handling
+
+
+def set_ip_in_obj(
+ nw_obj: dict[str, Any], ip: str
+) -> None: # add start and end ip in nw_obj if it is a range, otherwise do nothing
+ if "-" in ip: # dealing with range
+ ip_start, ip_end = ip.split("-")
+ nw_obj.update({"obj_ip": str(ip_start)})
+ if ip_end != ip_start:
+ nw_obj.update({"obj_ip_end": str(ip_end)})
+ else:
+ nw_obj.update({"obj_ip": str(ip)})
+
+
+# for members of groups, the name of the member obj needs to be fetched separately (starting from API v1.?)
+def resolve_nw_uid_to_name(uid: str, nw_objects: list[dict[str, Any]]) -> str:
+ # return name of nw_objects element where obj_uid = uid
+ for obj in nw_objects:
+ if obj["obj_uid"] == uid:
+ return obj["obj_name"]
+ return 'ERROR: uid "' + uid + '" not found'
+
+
+def add_member_names_for_nw_group(idx: int, nw_objects: list[dict[str, Any]]) -> None:
+ group = nw_objects.pop(idx)
+ if group["obj_member_refs"] == "" or group["obj_member_refs"] is None:
+ group["obj_member_names"] = None
+ group["obj_member_refs"] = None
+ else:
+ member_names = ""
+ obj_member_refs = group["obj_member_refs"].split(LIST_DELIMITER)
+ for ref in obj_member_refs:
+ member_name = resolve_nw_uid_to_name(ref, nw_objects)
+ member_names += member_name + LIST_DELIMITER
+ group["obj_member_names"] = member_names[:-1]
+ nw_objects.insert(idx, group)
+
+
+def create_network_object(
+ name: str, obj_type: str, ip: str, ip_end: str | None, uid: str, color: str, comment: str | None, zone: str | None
+) -> dict[str, Any]:
+ return {
+ "obj_name": name,
+ "obj_typ": obj_type,
+ "obj_ip": ip,
+ "obj_ip_end": ip_end,
+ "obj_uid": uid,
+ "obj_color": color,
+ "obj_comment": comment,
+ "obj_zone": zone,
+ }
+
+
+def get_nw_obj(nat_obj_name: str, nwobjects: list[dict[str, Any]]) -> dict[str, Any] | None:
+ for obj in nwobjects:
+ if "obj_name" in obj and obj["obj_name"] == nat_obj_name:
+ return obj
+ return None
+
+
+# this removes all obj_nat_ip entries from all network objects
+# these were used during import but might cause issues if imported into db
+def remove_nat_ip_entries(config2import: dict[str, Any]) -> None:
+ for obj in config2import["network_objects"]:
+ if "obj_nat_ip" in obj:
+ obj.pop("obj_nat_ip")
+
+
+def get_first_ip_of_destination(obj_ref: str, config2import: dict[str, Any]) -> str | None:
+ if LIST_DELIMITER in obj_ref:
+ obj_ref = obj_ref.split(LIST_DELIMITER)[0]
+ # if destination does not contain exactly one ip, raise a warning
+ FWOLogger.info(
+ "src nat behind interface: more than one NAT IP - just using the first one for routing decision for obj_ref "
+ + obj_ref
+ )
+
+ for obj in config2import["network_objects"]:
+ if "obj_uid" in obj and obj["obj_uid"] == obj_ref:
+ if "obj_type" in obj and obj["obj_type"] == "group":
+ if "obj_member_refs" in obj and LIST_DELIMITER in obj["obj_member_refs"]:
+ return get_first_ip_of_destination(obj["obj_member_refs"].split(LIST_DELIMITER)[0], config2import)
+ elif "obj_ip" in obj:
+ return obj["obj_ip"]
+ FWOLogger.warning("src nat behind interface: found no IP info for destination object " + obj_ref)
+ return None
diff --git a/roles/importer/files/importer/fw_modules/fortiadom5ff/fmgr_rule.py b/roles/importer/files/importer/fw_modules/fortiadom5ff/fmgr_rule.py
new file mode 100644
index 0000000000..73fe4b3fe2
--- /dev/null
+++ b/roles/importer/files/importer/fw_modules/fortiadom5ff/fmgr_rule.py
@@ -0,0 +1,780 @@
+import copy
+import ipaddress
+from time import localtime, strftime
+from typing import Any
+
+from fw_modules.fortiadom5ff import fmgr_getter
+from fw_modules.fortiadom5ff.fmgr_consts import nat_types
+from fw_modules.fortiadom5ff.fmgr_zone import find_zones_in_normalized_config
+from fwo_const import LIST_DELIMITER
+from fwo_exceptions import FwoDeviceWithoutLocalPackageError, FwoImporterErrorInconsistenciesError
+from fwo_log import FWOLogger
+from models.rule import RuleAction, RuleNormalized, RuleTrack, RuleType
+from models.rulebase import Rulebase
+
+NETWORK_OBJECT = "network_object"
+STRING_PKG = "/pkg/"
+STRING_PM_CONFIG_GLOBAL_PKG = "/pm/config/global/pkg/"
+STRING_PM_CONFIG_ADOM = "/pm/config/adom/"
+rule_access_scope_v4 = ["rules_global_header_v4", "rules_adom_v4", "rules_global_footer_v4"]
+rule_access_scope_v6 = ["rules_global_header_v6", "rules_adom_v6", "rules_global_footer_v6"]
+rule_access_scope = rule_access_scope_v6 + rule_access_scope_v4
+rule_nat_scope = ["rules_global_nat", "rules_adom_nat"]
+rule_scope = rule_access_scope + rule_nat_scope
+
+
+def normalize_rulebases(
+ mgm_uid: str,
+ native_config: dict[str, Any],
+ native_config_global: dict[str, Any],
+ normalized_config_adom: dict[str, Any],
+ normalized_config_global: dict[str, Any],
+ is_global_loop_iteration: bool,
+) -> None:
+ normalized_config_adom["policies"] = []
+ fetched_rulebase_uids: list[str] = []
+ if normalized_config_global != {}:
+ fetched_rulebase_uids = [
+ normalized_rulebase_global.uid
+ for normalized_rulebase_global in normalized_config_global.get("policies", [])
+ ]
+ for gateway in native_config["gateways"]:
+ normalize_rulebases_for_each_link_destination(
+ gateway,
+ mgm_uid,
+ fetched_rulebase_uids,
+ native_config,
+ native_config_global,
+ is_global_loop_iteration,
+ normalized_config_adom,
+ normalized_config_global,
+ )
+
+
+def normalize_rulebases_for_each_link_destination(
+ gateway: dict[str, Any],
+ mgm_uid: str,
+ fetched_rulebase_uids: list[str],
+ native_config: dict[str, Any],
+ native_config_global: dict[str, Any],
+ is_global_loop_iteration: bool,
+ normalized_config_adom: dict[str, Any],
+ normalized_config_global: dict[str, Any],
+):
+ for rulebase_link in gateway["rulebase_links"]:
+ if rulebase_link["to_rulebase_uid"] not in fetched_rulebase_uids and rulebase_link["to_rulebase_uid"] != "":
+ rulebase_to_parse = find_rulebase_to_parse(native_config["rulebases"], rulebase_link["to_rulebase_uid"])
+ # search in global rulebase
+ found_rulebase_in_global = False
+ if rulebase_to_parse == {} and not is_global_loop_iteration and native_config_global != {}:
+ rulebase_to_parse = find_rulebase_to_parse(
+ native_config_global["rulebases"], rulebase_link["to_rulebase_uid"]
+ )
+ found_rulebase_in_global = True
+ if rulebase_to_parse == {}:
+ FWOLogger.warning("found to_rulebase link without rulebase in nativeConfig: " + str(rulebase_link))
+ continue
+
+ normalized_rulebase = initialize_normalized_rulebase(rulebase_to_parse, mgm_uid)
+ parse_rulebase(
+ normalized_config_adom,
+ normalized_config_global,
+ rulebase_to_parse,
+ normalized_rulebase,
+ found_rulebase_in_global,
+ )
+ fetched_rulebase_uids.append(rulebase_link["to_rulebase_uid"])
+
+ if found_rulebase_in_global:
+ normalized_config_global["policies"].append(normalized_rulebase)
+ else:
+ normalized_config_adom["policies"].append(normalized_rulebase)
+
+ # normalizing nat rulebases is work in progress
+ # normalize_nat_rulebase(rulebase_link, native_config, normalized_config_adom, normalized_config_global) # noqa: ERA001
+
+
+def normalize_nat_rulebase(
+ rulebase_link: dict[str, Any],
+ native_config: dict[str, Any],
+ normalized_config_adom: dict[str, Any],
+ normalized_config_global: dict[str, Any],
+):
+ if not rulebase_link["is_section"]:
+ for nat_type in nat_types:
+ nat_type_string = nat_type + "_" + rulebase_link["to_rulebase_uid"]
+ nat_rulebase = get_native_nat_rulebase(native_config, nat_type_string)
+ parse_nat_rulebase(nat_rulebase, nat_type_string, normalized_config_adom, normalized_config_global)
+
+
+def get_native_nat_rulebase(native_config: dict[str, Any], nat_type_string: str) -> list[dict[str, Any]]:
+ for nat_rulebase in native_config["nat_rulebases"]:
+ if nat_type_string == nat_rulebase["type"]:
+ return nat_rulebase["data"]
+ FWOLogger.warning("no nat data for " + nat_type_string)
+ return []
+
+
+def find_rulebase_to_parse(rulebase_list: list[dict[str, Any]], rulebase_uid: str) -> dict[str, Any]:
+ for rulebase in rulebase_list:
+ if rulebase["uid"] == rulebase_uid:
+ return rulebase
+ return {}
+
+
+def initialize_normalized_rulebase(rulebase_to_parse: dict[str, Any], mgm_uid: str) -> Rulebase:
+ """
+ We use 'type' as uid/name since a rulebase may have a v4 and a v6 part
+ """
+ rulebase_name = rulebase_to_parse["type"]
+ rulebase_uid = rulebase_to_parse["type"]
+ return Rulebase(uid=rulebase_uid, name=rulebase_name, mgm_uid=mgm_uid, rules={})
+
+
+def parse_rulebase(
+ normalized_config_adom: dict[str, Any],
+ normalized_config_global: dict[str, Any],
+ rulebase_to_parse: dict[str, Any],
+ normalized_rulebase: Rulebase,
+ found_rulebase_in_global: bool,
+):
+ """Parses a native Fortinet rulebase into a normalized rulebase."""
+ for native_rule in rulebase_to_parse["data"]:
+ parse_single_rule(normalized_config_adom, normalized_config_global, native_rule, normalized_rulebase)
+ if not found_rulebase_in_global:
+ add_implicit_deny_rule(normalized_config_adom, normalized_config_global, normalized_rulebase)
+
+
+def add_implicit_deny_rule(
+ normalized_config_adom: dict[str, Any], normalized_config_global: dict[str, Any], rulebase: Rulebase
+):
+ deny_rule = {
+ "srcaddr": ["all"],
+ "srcaddr6": ["all"],
+ "dstaddr": ["all"],
+ "dstaddr6": ["all"],
+ "service": ["ALL"],
+ "srcintf": ["any"],
+ "dstintf": ["any"],
+ }
+
+ rule_src_list, rule_src_refs_list = rule_parse_addresses(
+ deny_rule, "src", normalized_config_adom, normalized_config_global, is_nat=False
+ )
+ rule_dst_list, rule_dst_refs_list = rule_parse_addresses(
+ deny_rule, "dst", normalized_config_adom, normalized_config_global, is_nat=False
+ )
+ rule_svc_list, rule_svc_refs_list = rule_parse_service(deny_rule)
+ rule_src_zones = find_zones_in_normalized_config(
+ deny_rule.get("srcintf", []), normalized_config_adom, normalized_config_global
+ )
+ rule_dst_zones = find_zones_in_normalized_config(
+ deny_rule.get("dstintf", []), normalized_config_adom, normalized_config_global
+ )
+
+ rule_normalized = RuleNormalized(
+ rule_num=0,
+ rule_num_numeric=0,
+ rule_disabled=False,
+ rule_src_neg=False,
+ rule_src=LIST_DELIMITER.join(rule_src_list),
+ rule_src_refs=LIST_DELIMITER.join(rule_src_refs_list),
+ rule_dst_neg=False,
+ rule_dst=LIST_DELIMITER.join(rule_dst_list),
+ rule_dst_refs=LIST_DELIMITER.join(rule_dst_refs_list),
+ rule_svc_neg=False,
+ rule_svc=LIST_DELIMITER.join(rule_svc_list),
+ rule_svc_refs=LIST_DELIMITER.join(rule_svc_refs_list),
+ rule_action=RuleAction.DROP,
+ rule_track=RuleTrack.NONE, # I guess this could also have different values
+ rule_installon=None,
+ rule_time=None, # Time-based rules not commonly used in basic Fortinet configs
+ rule_name="Implicit Deny",
+ rule_uid=f"{rulebase.uid}_implicit_deny",
+ rule_custom_fields=str({}),
+ rule_implied=True,
+ rule_type=RuleType.ACCESS,
+ last_change_admin=None,
+ parent_rule_uid=None,
+ last_hit=None,
+ rule_comment=None,
+ rule_src_zone=LIST_DELIMITER.join(rule_src_zones),
+ rule_dst_zone=LIST_DELIMITER.join(rule_dst_zones),
+ rule_head_text=None,
+ )
+
+ if rule_normalized.rule_uid is None:
+ raise FwoImporterErrorInconsistenciesError("rule_normalized.rule_uid is None when adding implicit deny rule")
+ rulebase.rules[rule_normalized.rule_uid] = rule_normalized
+
+
+def parse_single_rule(
+ normalized_config_adom: dict[str, Any],
+ normalized_config_global: dict[str, Any],
+ native_rule: dict[str, Any],
+ rulebase: Rulebase,
+):
+ """Parses a single native Fortinet rule into a normalized rule and adds it to the given rulebase."""
+ # Extract basic rule information
+ rule_disabled = True # Default to disabled
+ if "status" in native_rule and (native_rule["status"] == 1 or native_rule["status"] == "enable"):
+ rule_disabled = False
+
+ rule_action = rule_parse_action(native_rule)
+
+ rule_track = rule_parse_tracking_info(native_rule)
+
+ rule_src_list, rule_src_refs_list = rule_parse_addresses(
+ native_rule, "src", normalized_config_adom, normalized_config_global, is_nat=False
+ )
+ rule_dst_list, rule_dst_refs_list = rule_parse_addresses(
+ native_rule, "dst", normalized_config_adom, normalized_config_global, is_nat=False
+ )
+
+ rule_svc_list, rule_svc_refs_list = rule_parse_service(native_rule)
+
+ rule_src_zones = find_zones_in_normalized_config(
+ native_rule.get("srcintf", []), normalized_config_adom, normalized_config_global
+ )
+ rule_dst_zones = find_zones_in_normalized_config(
+ native_rule.get("dstintf", []), normalized_config_adom, normalized_config_global
+ )
+
+ rule_src_neg, rule_dst_neg, rule_svc_neg = rule_parse_negation_flags(native_rule)
+ rule_installon = rule_parse_installon(native_rule)
+
+ last_hit = rule_parse_last_hit(native_rule)
+
+ # Create the normalized rule
+ rule_normalized = RuleNormalized(
+ rule_num=0,
+ rule_num_numeric=0,
+ rule_disabled=rule_disabled,
+ rule_src_neg=rule_src_neg,
+ rule_src=LIST_DELIMITER.join(rule_src_list),
+ rule_src_refs=LIST_DELIMITER.join(rule_src_refs_list),
+ rule_dst_neg=rule_dst_neg,
+ rule_dst=LIST_DELIMITER.join(rule_dst_list),
+ rule_dst_refs=LIST_DELIMITER.join(rule_dst_refs_list),
+ rule_svc_neg=rule_svc_neg,
+ rule_svc=LIST_DELIMITER.join(rule_svc_list),
+ rule_svc_refs=LIST_DELIMITER.join(rule_svc_refs_list),
+ rule_action=rule_action,
+ rule_track=rule_track,
+ rule_installon=rule_installon,
+ rule_time=None, # Time-based rules not commonly used in basic Fortinet configs
+ rule_name=native_rule.get("name"),
+ rule_uid=native_rule.get("uuid"),
+ rule_custom_fields=str(native_rule.get("meta fields", {})),
+ rule_implied=False,
+ rule_type=RuleType.ACCESS,
+ last_change_admin=None, # native_rule.get('_last-modified-by', ''), not handled yet -> leave out to prevent mismatches
+ parent_rule_uid=None,
+ last_hit=last_hit,
+ rule_comment=native_rule.get("comments"),
+ rule_src_zone=LIST_DELIMITER.join(rule_src_zones),
+ rule_dst_zone=LIST_DELIMITER.join(rule_dst_zones),
+ rule_head_text=None,
+ )
+ if rule_normalized.rule_uid is None:
+ raise FwoImporterErrorInconsistenciesError("rule_normalized.rule_uid is None when parsing single rule")
+
+ # Add the rule to the rulebase
+ rulebase.rules[rule_normalized.rule_uid] = rule_normalized
+
+ # TODO: handle combined NAT, see handle_combined_nat_rule
+
+
+def rule_parse_action(native_rule: dict[str, Any]) -> RuleAction:
+ # Extract action - Fortinet uses 0 for deny/drop, 1 for accept
+ if native_rule.get("action", 0) == 0:
+ return RuleAction.DROP
+ return RuleAction.ACCEPT
+
+
+def rule_parse_tracking_info(native_rule: dict[str, Any]) -> RuleTrack:
+ # TODO: Implement more detailed logging level extraction (difference between 1/2/3?)
+ logtraffic = native_rule.get("logtraffic", 0)
+ if (isinstance(logtraffic, int) and logtraffic > 0) or (isinstance(logtraffic, str) and logtraffic != "disable"):
+ return RuleTrack.LOG
+ return RuleTrack.NONE
+
+
+def rule_parse_service(native_rule: dict[str, Any]) -> tuple[list[str], list[str]]:
+ """
+ Parses services to ordered (!) name list and reference list.
+ """
+ rule_svc_list: list[str] = []
+ rule_svc_refs_list: list[str] = []
+ for svc in sorted(native_rule.get("service", [])):
+ rule_svc_list.append(svc)
+ rule_svc_refs_list.append(svc)
+ if rule_svc_list == [] and "internet-service-name" in native_rule and len(native_rule["internet-service-name"]) > 0:
+ rule_svc_list.append("ALL")
+ rule_svc_refs_list.append("ALL")
+ if (
+ rule_svc_list == []
+ and "internet-service-src-name" in native_rule
+ and len(native_rule["internet-service-src-name"]) > 0
+ ):
+ rule_svc_list.append("ALL")
+ rule_svc_refs_list.append("ALL")
+
+ return rule_svc_list, rule_svc_refs_list
+
+
+def rule_parse_addresses(
+ native_rule: dict[str, Any],
+ target: str,
+ normalized_config_adom: dict[str, Any],
+ normalized_config_global: dict[str, Any],
+ is_nat: bool,
+) -> tuple[list[str], list[str]]:
+ """
+ Parses addresses to ordered (!) name list and reference list for source or destination addresses.
+ """
+ if target not in ["src", "dst"]:
+ raise FwoImporterErrorInconsistenciesError(f"target '{target}' must either be src or dst.")
+ addr_list: list[str] = []
+ addr_ref_list: list[str] = []
+ if not is_nat:
+ build_addr_list(
+ native_rule, target, normalized_config_adom, normalized_config_global, addr_list, addr_ref_list, is_v4=True
+ )
+ build_addr_list(
+ native_rule, target, normalized_config_adom, normalized_config_global, addr_list, addr_ref_list, is_v4=False
+ )
+ else:
+ build_nat_addr_list(
+ native_rule, target, normalized_config_adom, normalized_config_global, addr_list, addr_ref_list
+ )
+ return addr_list, addr_ref_list
+
+
+def build_addr_list(
+ native_rule: dict[str, Any],
+ target: str,
+ normalized_config_adom: dict[str, Any],
+ normalized_config_global: dict[str, Any],
+ addr_list: list[str],
+ addr_ref_list: list[str],
+ is_v4: bool,
+) -> None:
+ """
+ Builds ordered (!) address list and address reference list for source or destination addresses.
+ """
+ if is_v4 and target == "src":
+ for addr in sorted(native_rule.get("srcaddr", [])) + sorted(native_rule.get("internet-service-src-name", [])):
+ addr_list.append(addr)
+ addr_ref_list.append(find_addr_ref(addr, is_v4, normalized_config_adom, normalized_config_global))
+ elif not is_v4 and target == "src":
+ for addr in sorted(native_rule.get("srcaddr6", [])):
+ addr_list.append(addr)
+ addr_ref_list.append(find_addr_ref(addr, is_v4, normalized_config_adom, normalized_config_global))
+ elif is_v4 and target == "dst":
+ for addr in sorted(native_rule.get("dstaddr", [])) + sorted(native_rule.get("internet-service-name", [])):
+ addr_list.append(addr)
+ addr_ref_list.append(find_addr_ref(addr, is_v4, normalized_config_adom, normalized_config_global))
+ else:
+ for addr in sorted(native_rule.get("dstaddr6", [])):
+ addr_list.append(addr)
+ addr_ref_list.append(find_addr_ref(addr, is_v4, normalized_config_adom, normalized_config_global))
+
+
+def build_nat_addr_list(
+ native_rule: dict[str, Any],
+ target: str,
+ normalized_config_adom: dict[str, Any],
+ normalized_config_global: dict[str, Any],
+ addr_list: list[str],
+ addr_ref_list: list[str],
+) -> None:
+ # so far only ip v4 expected
+ if target == "src":
+ for addr in sorted(native_rule.get("orig-addr", [])):
+ addr_list.append(addr)
+ addr_ref_list.append(
+ find_addr_ref(
+ addr,
+ is_v4=True,
+ normalized_config_adom=normalized_config_adom,
+ normalized_config_global=normalized_config_global,
+ )
+ )
+ if target == "dst":
+ for addr in sorted(native_rule.get("dst-addr", [])):
+ addr_list.append(addr)
+ addr_ref_list.append(
+ find_addr_ref(
+ addr,
+ is_v4=True,
+ normalized_config_adom=normalized_config_adom,
+ normalized_config_global=normalized_config_global,
+ )
+ )
+
+
+def find_addr_ref(
+ addr: str, is_v4: bool, normalized_config_adom: dict[str, Any], normalized_config_global: dict[str, Any]
+) -> str:
+ for nw_obj in normalized_config_adom["network_objects"] + normalized_config_global.get("network_objects", []):
+ if addr == nw_obj["obj_name"] and ((is_v4 and ip_type(nw_obj) == 4) or (not is_v4 and ip_type(nw_obj) == 6)): # noqa: PLR2004
+ return nw_obj["obj_uid"]
+ raise FwoImporterErrorInconsistenciesError(f"No ref found for '{addr}'.")
+
+
+def ip_type(nw_obj: dict[str, Any]) -> int:
+ # default to v4
+ first_ip = nw_obj.get("obj_ip", "0.0.0.0/32")
+ if first_ip == "":
+ first_ip = "0.0.0.0/32"
+ net = ipaddress.ip_network(str(first_ip))
+ return net.version
+
+
+def rule_parse_negation_flags(native_rule: dict[str, Any]) -> tuple[bool, bool, bool]:
+ # if customer decides to mix internet-service and "normal" addr obj in src/dst and mix negates this will prob. not work correctly
+ if "srcaddr-negate" in native_rule:
+ rule_src_neg = native_rule["srcaddr-negate"] == 1 or native_rule["srcaddr-negate"] == "disable"
+ elif "internet-service-src-negate" in native_rule:
+ rule_src_neg = (
+ native_rule["internet-service-src-negate"] == 1 or native_rule["internet-service-src-negate"] == "disable"
+ )
+ else:
+ rule_src_neg = False
+ rule_dst_neg = "dstaddr-negate" in native_rule and (
+ native_rule["dstaddr-negate"] == 1 or native_rule["dstaddr-negate"] == "disable"
+ ) # TODO: last part does not make sense?
+ rule_svc_neg = "service-negate" in native_rule and (
+ native_rule["service-negate"] == 1 or native_rule["service-negate"] == "disable"
+ )
+ return rule_src_neg, rule_dst_neg, rule_svc_neg
+
+
+def rule_parse_installon(native_rule: dict[str, Any]) -> str | None:
+ rule_installon = None
+ if native_rule.get("scope_member"):
+ rule_installon = LIST_DELIMITER.join(
+ sorted({vdom["name"] + "_" + vdom["vdom"] for vdom in native_rule["scope_member"]})
+ )
+ return rule_installon
+
+
+def rule_parse_last_hit(native_rule: dict[str, Any]) -> str | None:
+ last_hit = native_rule.get("_last_hit")
+ if last_hit is not None:
+ last_hit = strftime("%Y-%m-%d %H:%M:%S", localtime(last_hit))
+ return last_hit
+
+
+def get_access_policy(
+ sid: str,
+ fm_api_url: str,
+ native_config_adom: dict[str, Any],
+ native_config_global: dict[str, Any],
+ adom_device_vdom_policy_package_structure: dict[str, Any],
+ adom_name: str,
+ mgm_details_device: dict[str, Any],
+ device_config: dict[str, Any],
+ limit: int,
+):
+ previous_rulebase = None
+ link_list: list[Any] = []
+ local_pkg_name, global_pkg_name = find_packages(
+ adom_device_vdom_policy_package_structure, adom_name, mgm_details_device
+ )
+ options = ["extra info", "scope member", "get meta"]
+
+ previous_rulebase = get_and_link_global_rulebase(
+ "header", previous_rulebase, global_pkg_name, native_config_global, sid, fm_api_url, options, limit, link_list
+ )
+
+ previous_rulebase = get_and_link_local_rulebase(
+ "rules_adom",
+ previous_rulebase,
+ adom_name,
+ local_pkg_name,
+ native_config_adom,
+ sid,
+ fm_api_url,
+ options,
+ limit,
+ link_list,
+ )
+
+ previous_rulebase = get_and_link_global_rulebase(
+ "footer", previous_rulebase, global_pkg_name, native_config_global, sid, fm_api_url, options, limit, link_list
+ )
+
+ device_config["rulebase_links"].extend(link_list)
+
+
+def get_and_link_global_rulebase(
+ header_or_footer: str,
+ previous_rulebase: str | None,
+ global_pkg_name: str,
+ native_config_global: dict[str, Any],
+ sid: str,
+ fm_api_url: str,
+ options: list[str],
+ limit: int,
+ link_list: list[Any],
+) -> Any:
+ rulebase_type_prefix = "rules_global_" + header_or_footer
+ if global_pkg_name != "":
+ if not is_rulebase_already_fetched(
+ native_config_global["rulebases"], rulebase_type_prefix + "_v4_" + global_pkg_name
+ ):
+ fmgr_getter.update_config_with_fortinet_api_call(
+ native_config_global["rulebases"],
+ sid,
+ fm_api_url,
+ STRING_PM_CONFIG_GLOBAL_PKG + global_pkg_name + "/global/" + header_or_footer + "/policy",
+ rulebase_type_prefix + "_v4_" + global_pkg_name,
+ options=options,
+ limit=limit,
+ )
+ if not is_rulebase_already_fetched(
+ native_config_global["rulebases"], rulebase_type_prefix + "_v6_" + global_pkg_name
+ ):
+ # delete_v: hier auch options=options?
+ fmgr_getter.update_config_with_fortinet_api_call(
+ native_config_global["rulebases"],
+ sid,
+ fm_api_url,
+ STRING_PM_CONFIG_GLOBAL_PKG + global_pkg_name + "/global/" + header_or_footer + "/policy6",
+ rulebase_type_prefix + "_v6_" + global_pkg_name,
+ limit=limit,
+ )
+ previous_rulebase = link_rulebase(
+ link_list,
+ native_config_global["rulebases"],
+ global_pkg_name,
+ rulebase_type_prefix,
+ previous_rulebase,
+ is_global=True,
+ )
+ return previous_rulebase
+
+
+def get_and_link_local_rulebase(
+ rulebase_type_prefix: str,
+ previous_rulebase: str | None,
+ adom_name: str,
+ local_pkg_name: str,
+ native_config_adom: dict[str, Any],
+ sid: str,
+ fm_api_url: str,
+ options: list[str],
+ limit: int,
+ link_list: list[Any],
+) -> Any:
+ if not is_rulebase_already_fetched(native_config_adom["rulebases"], rulebase_type_prefix + "_v4_" + local_pkg_name):
+ fmgr_getter.update_config_with_fortinet_api_call(
+ native_config_adom["rulebases"],
+ sid,
+ fm_api_url,
+ STRING_PM_CONFIG_ADOM + adom_name + STRING_PKG + local_pkg_name + "/firewall/policy",
+ rulebase_type_prefix + "_v4_" + local_pkg_name,
+ options=options,
+ limit=limit,
+ )
+ if not is_rulebase_already_fetched(native_config_adom["rulebases"], rulebase_type_prefix + "_v6_" + local_pkg_name):
+ fmgr_getter.update_config_with_fortinet_api_call(
+ native_config_adom["rulebases"],
+ sid,
+ fm_api_url,
+ STRING_PM_CONFIG_ADOM + adom_name + STRING_PKG + local_pkg_name + "/firewall/policy6",
+ rulebase_type_prefix + "_v6_" + local_pkg_name,
+ limit=limit,
+ )
+ return link_rulebase(
+ link_list,
+ native_config_adom["rulebases"],
+ local_pkg_name,
+ rulebase_type_prefix,
+ previous_rulebase,
+ is_global=False,
+ )
+
+
+def find_packages(
+ adom_device_vdom_policy_package_structure: dict[str, Any], adom_name: str, mgm_details_device: dict[str, Any]
+) -> tuple[str, str]:
+ for device in adom_device_vdom_policy_package_structure[adom_name]:
+ for vdom in adom_device_vdom_policy_package_structure[adom_name][device]:
+ if mgm_details_device["name"] == device + "_" + vdom:
+ device_dict = adom_device_vdom_policy_package_structure[adom_name][device]
+ if (
+ "local" in device_dict[vdom]
+ and "global" in adom_device_vdom_policy_package_structure[adom_name][device][vdom]
+ ):
+ return device_dict[vdom]["local"], adom_device_vdom_policy_package_structure[adom_name][device][
+ vdom
+ ]["global"]
+ return "", ""
+ raise FwoDeviceWithoutLocalPackageError(
+ "Could not find local package for " + mgm_details_device["name"] + " in Fortimanager Config"
+ ) from None
+
+
+def is_rulebase_already_fetched(rulebases: list[dict[str, Any]], typ: str) -> bool:
+ return any(rulebase["type"] == typ for rulebase in rulebases)
+
+
+def link_rulebase(
+ link_list: list[Any],
+ rulebases: list[dict[str, Any]],
+ pkg_name: str,
+ rulebase_type_prefix: str,
+ previous_rulebase: str | None,
+ is_global: bool,
+) -> str | None:
+ for version in ["v4", "v6"]:
+ full_pkg_name = rulebase_type_prefix + "_" + version + "_" + pkg_name
+ has_data = has_rulebase_data(rulebases, full_pkg_name, is_global, version, pkg_name)
+ if has_data:
+ link_list.append(build_link(previous_rulebase, full_pkg_name, is_global))
+ previous_rulebase = full_pkg_name
+
+ return previous_rulebase
+
+
+def build_link(previous_rulebase: str | None, full_pkg_name: str, is_global: bool) -> dict[str, Any]:
+ if previous_rulebase is None:
+ is_initial = True
+ previous_rulebase = None
+ else:
+ is_initial = False
+ return {
+ "from_rulebase_uid": previous_rulebase,
+ "from_rule_uid": None,
+ "to_rulebase_uid": full_pkg_name,
+ "type": "ordered",
+ "is_global": is_global,
+ "is_initial": is_initial,
+ "is_section": False,
+ }
+
+
+def has_rulebase_data(
+ rulebases: list[dict[str, Any]], full_pkg_name: str, is_global: bool, version: str, pkg_name: str
+) -> bool:
+ """Adds name and uid to rulebase and removes empty global rulebases"""
+ has_data = False
+ is_v4 = version == "v4"
+ for rulebase in rulebases:
+ if rulebase["type"] == full_pkg_name:
+ rulebase.update(
+ {
+ "name": full_pkg_name,
+ "uid": full_pkg_name,
+ "is_global": is_global,
+ "is_v4": is_v4,
+ "package": pkg_name,
+ }
+ )
+ if len(rulebase["data"]) > 0:
+ has_data = True
+ elif is_global:
+ rulebases.remove(rulebase)
+ return has_data
+
+
+def get_nat_policy(
+ sid: str,
+ fm_api_url: str,
+ native_config: dict[str, Any],
+ adom_device_vdom_policy_package_structure: dict[str, Any],
+ adom_name: str,
+ mgm_details_device: dict[str, Any],
+ limit: int,
+):
+ local_pkg_name, global_pkg_name = find_packages(
+ adom_device_vdom_policy_package_structure, adom_name, mgm_details_device
+ )
+ if adom_name == "":
+ for nat_type in nat_types:
+ fmgr_getter.update_config_with_fortinet_api_call(
+ native_config["nat_rulebases"],
+ sid,
+ fm_api_url,
+ STRING_PM_CONFIG_GLOBAL_PKG + global_pkg_name + "/" + nat_type,
+ nat_type + "_global_" + global_pkg_name,
+ limit=limit,
+ )
+ else:
+ for nat_type in nat_types:
+ fmgr_getter.update_config_with_fortinet_api_call(
+ native_config["nat_rulebases"],
+ sid,
+ fm_api_url,
+ STRING_PM_CONFIG_ADOM + adom_name + STRING_PKG + local_pkg_name + "/" + nat_type,
+ nat_type + "_adom_" + adom_name + "_" + local_pkg_name,
+ limit=limit,
+ )
+
+
+# delete_v: ab hier kann sehr viel weg, ich lasses vorerst zB für die nat
+# pure nat rules
+
+
+def parse_nat_rulebase(
+ _nat_rulebase: list[dict[str, Any]],
+ _nat_type_string: str,
+ _normalized_config_adom: dict[str, Any],
+ _normalized_config_global: dict[str, Any],
+) -> None:
+ # this function is not called until it is ready check git commit for reference
+ return
+
+
+def create_xlate_rule(rule: dict[str, Any]) -> dict[str, Any]:
+ xlate_rule = copy.deepcopy(rule)
+ rule["rule_type"] = "combined"
+ xlate_rule["rule_type"] = "xlate"
+ xlate_rule["rule_comment"] = None
+ xlate_rule["rule_disabled"] = False
+ xlate_rule["rule_src"] = "Original"
+ xlate_rule["rule_src_refs"] = "Original"
+ xlate_rule["rule_dst"] = "Original"
+ xlate_rule["rule_dst_refs"] = "Original"
+ xlate_rule["rule_svc"] = "Original"
+ xlate_rule["rule_svc_refs"] = "Original"
+ return xlate_rule
+
+
+def handle_combined_nat_rule(
+ rule: dict[str, Any], rule_orig: dict[str, Any], config2import: dict[str, Any], nat_rule_number: int, dev_id: int
+) -> dict[str, Any] | None:
+ # TODO: see fOS_rule for reference implementation
+ raise NotImplementedError("handle_combined_nat_rule is not implemented yet")
+
+
+def extract_nat_objects(nwobj_list: list[str], all_nwobjects: list[dict[str, str]]) -> list[dict[str, str]]:
+ nat_obj_list: list[dict[str, str]] = []
+ for obj in nwobj_list:
+ for obj2 in all_nwobjects:
+ if obj2["obj_name"] == obj:
+ if "obj_nat_ip" in obj2:
+ nat_obj_list.append(obj2)
+ break
+ return nat_obj_list
+
+
+def add_users_to_rule(rule_orig: dict[str, Any], rule: dict[str, Any]) -> None:
+ if "groups" in rule_orig:
+ add_users(rule_orig["groups"], rule)
+ if "users" in rule_orig:
+ add_users(rule_orig["users"], rule)
+
+
+def add_users(users: list[str], rule: dict[str, Any]) -> None:
+ for user in users:
+ rule_src_with_users = [user + "@" + src for src in rule["rule_src"].split(LIST_DELIMITER)]
+
+ rule["rule_src"] = LIST_DELIMITER.join(rule_src_with_users)
+
+ # here user ref is the user name itself
+ rule_src_refs_with_users = [user + "@" + src for src in rule["rule_src_refs"].split(LIST_DELIMITER)]
+ rule["rule_src_refs"] = LIST_DELIMITER.join(rule_src_refs_with_users)
diff --git a/roles/importer/files/importer/fw_modules/fortiadom5ff/fmgr_service.py b/roles/importer/files/importer/fw_modules/fortiadom5ff/fmgr_service.py
new file mode 100644
index 0000000000..959a8bed51
--- /dev/null
+++ b/roles/importer/files/importer/fw_modules/fortiadom5ff/fmgr_service.py
@@ -0,0 +1,274 @@
+import re
+from typing import Any
+
+from fwo_base import sort_and_join
+from fwo_const import LIST_DELIMITER
+
+
+def normalize_service_objects(
+ native_config: dict[str, Any], normalized_config_adom: dict[str, Any], svc_obj_types: list[str]
+) -> None:
+ svc_objects: list[dict[str, Any]] = []
+
+ if "objects" not in native_config:
+ return # no objects to normalize
+ for current_obj_type in native_config["objects"]:
+ if not (current_obj_type in svc_obj_types and "data" in native_config["objects"][current_obj_type]):
+ continue
+ for obj_orig in native_config["objects"][current_obj_type]["data"]:
+ normalize_service_object(obj_orig, svc_objects)
+
+ if native_config.get("is-super-manager", False):
+ # finally add "Original" service object for natting (global domain only)
+ original_obj_name = "Original"
+ svc_objects.append(
+ create_svc_object(
+ name=original_obj_name,
+ proto=0,
+ color="foreground",
+ port=None,
+ comment='"original" service object created by FWO importer for NAT purposes',
+ )
+ )
+
+ normalized_config_adom.update({"service_objects": svc_objects})
+
+
+def normalize_service_object(obj_orig: dict[str, Any], svc_objects: list[dict[str, Any]]) -> None:
+ member_names = ""
+ if "member" in obj_orig:
+ svc_type = "group"
+ for member in obj_orig["member"]:
+ member_names += member + LIST_DELIMITER
+ member_names = member_names[:-1]
+ else:
+ svc_type = "simple"
+
+ name = None
+ if "name" in obj_orig:
+ name = str(obj_orig["name"])
+
+ if name is None:
+ raise ValueError("Service object without name encountered")
+
+ color = "foreground" # TODO: color mapping. what is color: 0? (nativeconfig entwickler_fortimanager_stand_2025-07-27, service object 'gALL')
+
+ session_timeout = None # TODO: find the right timer
+
+ if "protocol" in obj_orig:
+ handle_svc_protocol(obj_orig, svc_objects, svc_type, name, color, session_timeout)
+ elif svc_type == "group":
+ add_object(svc_objects, svc_type, name, color, 0, None, member_names, session_timeout)
+ else:
+ add_object(svc_objects, svc_type, name, color, 0, None, None, session_timeout)
+
+
+def handle_svc_protocol(
+ obj_orig: dict[str, Any],
+ svc_objects: list[dict[str, Any]],
+ svc_type: str,
+ name: str,
+ color: str,
+ session_timeout: Any,
+) -> None:
+ proto = 0
+ range_names = ""
+ added_svc_obj = 0
+
+ # forti uses strange protocol numbers, so we need to map them
+
+ match obj_orig["protocol"]:
+ case 1:
+ add_object(svc_objects, svc_type, name, color, 1, None, None, session_timeout)
+ added_svc_obj += 1
+ case 2:
+ if "protocol-number" in obj_orig:
+ proto = obj_orig["protocol-number"]
+ add_object(svc_objects, svc_type, name, color, proto, None, None, session_timeout)
+ added_svc_obj += 1
+ case 5 | 11 | 15: # magic numbers from FortiNet: 5 = TCP/UDP, 11 = TCP/UDP/SCTP, 15 = TCP/UDP/SCTP/ICMP
+ parse_standard_protocols_with_ports(
+ obj_orig, svc_objects, svc_type, name, color, session_timeout, range_names, added_svc_obj
+ )
+ case 6:
+ add_object(svc_objects, svc_type, name, color, 58, None, None, session_timeout)
+ case _:
+ pass # not doing anything for other protocols, e.g. GRE, ESP, ...
+
+
+def parse_standard_protocols_with_ports(
+ obj_orig: dict[str, Any],
+ svc_objects: list[dict[str, Any]],
+ svc_type: str,
+ name: str,
+ color: str,
+ session_timeout: Any,
+ range_names: str,
+ added_svc_obj: int,
+) -> None:
+ split = check_split(obj_orig)
+ if "tcp-portrange" in obj_orig and len(obj_orig["tcp-portrange"]) > 0:
+ tcpname = name
+ if split:
+ tcpname += "_tcp"
+ range_names += tcpname + LIST_DELIMITER
+ add_object(svc_objects, svc_type, tcpname, color, 6, obj_orig["tcp-portrange"], None, session_timeout)
+ added_svc_obj += 1
+ if "udp-portrange" in obj_orig and len(obj_orig["udp-portrange"]) > 0:
+ udpname = name
+ if split:
+ udpname += "_udp"
+ range_names += udpname + LIST_DELIMITER
+ add_object(svc_objects, svc_type, udpname, color, 17, obj_orig["udp-portrange"], None, session_timeout)
+ added_svc_obj += 1
+ if "sctp-portrange" in obj_orig and len(obj_orig["sctp-portrange"]) > 0:
+ sctpname = name
+ if split:
+ sctpname += "_sctp"
+ range_names += sctpname + LIST_DELIMITER
+ add_object(svc_objects, svc_type, sctpname, color, 132, obj_orig["sctp-portrange"], None, session_timeout)
+ added_svc_obj += 1
+ if split:
+ range_names = range_names[:-1]
+ add_object(svc_objects, "group", name, color, 0, None, range_names, session_timeout)
+ added_svc_obj += 1
+ if added_svc_obj == 0: # assuming RPC service which here has no properties at all
+ add_object(svc_objects, "rpc", name, color, 0, None, None, None)
+ added_svc_obj += 1
+
+
+def check_split(obj_orig: dict[str, Any]) -> bool:
+ count = 0
+ if "tcp-portrange" in obj_orig and len(obj_orig["tcp-portrange"]) > 0:
+ count += 1
+ if "udp-portrange" in obj_orig and len(obj_orig["udp-portrange"]) > 0:
+ count += 1
+ if "sctp-portrange" in obj_orig and len(obj_orig["sctp-portrange"]) > 0:
+ count += 1
+ return count > 1
+
+
+def extract_ports(port_ranges: list[str] | None) -> "tuple[list[Any], list[Any]]":
+ ports: list[Any] = []
+ port_ends: list[Any] = []
+ if port_ranges is not None and len(port_ranges) > 0:
+ for port_range in port_ranges:
+ # remove src-ports
+ port = port_range.split(":")[0]
+ port_end = port
+
+ # open ranges (not found so far in data)
+ pattern = re.compile(r"^\>(\d+)$")
+ match = pattern.match(port)
+ if match:
+ port = str(int(match.group()[1:]) + 1)
+ port_end = str(65535)
+ pattern = re.compile(r"^\<(\d+)$")
+ match = pattern.match(port)
+ if match:
+ port = str(1)
+ port_end = str(int(match.group()[1:]) - 1)
+
+ # split ranges
+ pattern = re.compile(r"^(\d+)\-(\d+)$")
+ match = pattern.match(port)
+ if match:
+ port, port_end = match.group().split("-")
+ ports.append(port)
+ port_ends.append(port_end)
+ return ports, port_ends
+
+
+def create_svc_object(name: str, proto: int, color: str, port: Any, comment: str) -> "dict[str, Any]":
+ return {
+ "svc_name": name,
+ "svc_typ": "simple",
+ "svc_port": port,
+ "ip_proto": proto,
+ "svc_color": color,
+ "svc_uid": name, # services have no uid in fortimanager
+ "svc_comment": comment,
+ }
+
+
+def add_object(
+ svc_objects: list[dict[str, Any]],
+ typ: str,
+ name: str,
+ color: str,
+ proto: int,
+ port_ranges: list[str] | None,
+ member_names: str | None,
+ session_timeout: Any,
+) -> None:
+ if member_names:
+ member_names = sort_and_join(
+ member_names.split(LIST_DELIMITER)
+ ) # TODO: sorting should be done earlier on actual list
+ if port_ranges is None:
+ svc_objects.extend(
+ [
+ {
+ "svc_typ": typ,
+ "svc_name": name,
+ "svc_color": color,
+ "svc_uid": name, # ?
+ "svc_comment": None, # ?
+ "ip_proto": proto,
+ "svc_port": None,
+ "svc_port_end": None,
+ "svc_member_refs": member_names, # ?
+ "svc_member_names": member_names,
+ "svc_timeout": session_timeout,
+ "rpc_nr": None, # ?
+ }
+ ]
+ )
+ else:
+ range_names = ""
+ ports, port_ends = extract_ports(port_ranges)
+ split = len(ports) > 1
+ for index, port in enumerate(ports):
+ port_end = port_ends[index]
+ full_name = name
+ if split:
+ full_name += "_" + str(port)
+ range_names += full_name + LIST_DELIMITER
+ svc_objects.extend(
+ [
+ {
+ "svc_typ": typ,
+ "svc_name": full_name,
+ "svc_color": color,
+ "svc_uid": full_name, # ?
+ "svc_comment": None, # ?
+ "ip_proto": proto,
+ "svc_port": port,
+ "svc_port_end": port_end,
+ "svc_member_refs": member_names, # ?
+ "svc_member_names": member_names,
+ "svc_timeout": session_timeout,
+ "rpc_nr": None, # ?
+ }
+ ]
+ )
+ if split:
+ range_names = range_names[:-1]
+ svc_objects.extend(
+ [
+ {
+ "svc_typ": "group",
+ "svc_name": name,
+ "svc_color": color,
+ "svc_uid": name, # ?
+ "svc_comment": None, # ?
+ "ip_proto": proto,
+ "svc_port": None,
+ "svc_port_end": None,
+ "svc_member_refs": range_names, # ?
+ "svc_member_names": range_names,
+ "svc_timeout": session_timeout,
+ "rpc_nr": None, # ?
+ }
+ ]
+ )
diff --git a/roles/importer/files/importer/fw_modules/fortiadom5ff/fmgr_user.py b/roles/importer/files/importer/fw_modules/fortiadom5ff/fmgr_user.py
new file mode 100644
index 0000000000..59380c4791
--- /dev/null
+++ b/roles/importer/files/importer/fw_modules/fortiadom5ff/fmgr_user.py
@@ -0,0 +1,51 @@
+from typing import Any
+
+from fwo_const import LIST_DELIMITER
+
+
+# TODO: unused function
+def normalize_users(
+ full_config: dict[str, list[dict[str, Any]]], config2import: dict[str, list[dict[str, Any]]], user_scope: list[str]
+) -> None:
+ users: list[dict[str, Any]] = []
+ for scope in user_scope:
+ for user_orig in full_config[scope]:
+ user_normalized = _parse_user(user_orig)
+ users.append(user_normalized)
+
+ config2import.update({"user_objects": users})
+
+
+def _parse_user(user_orig: dict[str, Any]) -> dict[str, Any]:
+ name = None
+ svc_type = "simple"
+ color = None
+ member_names = None
+ comment = None
+ user: dict[str, Any] = {}
+ if "member" in user_orig:
+ svc_type = "group"
+ member_names = ""
+ for member in user_orig["member"]:
+ member_names += member + LIST_DELIMITER
+ member_names = member_names[:-1]
+ if "name" in user_orig:
+ name = str(user_orig["name"])
+ if "comment" in user_orig:
+ comment = str(user_orig["comment"])
+ if "color" in user_orig and str(user_orig["color"]) != "0":
+ color = str(user_orig["color"])
+
+ user.update(
+ {
+ "user_typ": svc_type,
+ "user_name": name,
+ "user_color": color,
+ "user_uid": name,
+ "user_comment": comment,
+ "user_member_refs": member_names,
+ "user_member_names": member_names,
+ }
+ )
+
+ return user
diff --git a/roles/importer/files/importer/fw_modules/fortiadom5ff/fmgr_zone.py b/roles/importer/files/importer/fw_modules/fortiadom5ff/fmgr_zone.py
new file mode 100644
index 0000000000..f5c761e88b
--- /dev/null
+++ b/roles/importer/files/importer/fw_modules/fortiadom5ff/fmgr_zone.py
@@ -0,0 +1,83 @@
+from typing import Any
+
+from fw_modules.fortiadom5ff import fmgr_getter
+from fwo_exceptions import FwoNormalizedConfigParseError
+
+
+def get_zones(sid: str, fm_api_url: str, native_config: dict[str, Any], adom_name: str, limit: int):
+ if adom_name == "":
+ fmgr_getter.update_config_with_fortinet_api_call(
+ native_config["zones"],
+ sid,
+ fm_api_url,
+ "/pm/config/global/obj/dynamic/interface",
+ "interface_global",
+ limit=limit,
+ )
+ else:
+ fmgr_getter.update_config_with_fortinet_api_call(
+ native_config["zones"],
+ sid,
+ fm_api_url,
+ "/pm/config/adom/" + adom_name + "/obj/dynamic/interface",
+ "interface_" + adom_name,
+ limit=limit,
+ )
+
+
+def normalize_zones(
+ native_config: dict[str, Any], normalized_config_adom: dict[str, Any], is_global_loop_iteration: bool
+):
+ zones: list[dict[str, Any]] = []
+ fetched_zones: list[str] = []
+ if is_global_loop_iteration: # can not find the following zones in api return
+ statically_add_missing_global_zones(fetched_zones)
+ for zone_type in native_config["zones"]:
+ for mapping in zone_type.get("data", []):
+ if "defmap-intf" in mapping and mapping["defmap-intf"] not in fetched_zones:
+ fetched_zones.append(mapping["defmap-intf"])
+ if mapping["dynamic_mapping"] is not None:
+ fetch_dynamic_mapping(mapping, fetched_zones)
+ if mapping["platform_mapping"] is not None:
+ fetch_platform_mapping(mapping, fetched_zones)
+
+ zones = [{"zone_name": zone} for zone in fetched_zones]
+ normalized_config_adom.update({"zone_objects": zones})
+
+
+def statically_add_missing_global_zones(fetched_zones: list[str]) -> None:
+ fetched_zones.extend(["any", "sslvpn_tun_intf", "virtual-wan-link"])
+ # double check, if these zones cannot be parsed from api results
+
+
+def fetch_dynamic_mapping(mapping: dict[str, Any], fetched_zones: list[str]) -> None:
+ for dyn_mapping in mapping["dynamic_mapping"]:
+ if "name" in dyn_mapping and dyn_mapping["name"] not in fetched_zones:
+ fetched_zones.append(dyn_mapping["name"])
+ if "local-intf" in dyn_mapping:
+ for local_interface in dyn_mapping["local-intf"]:
+ if local_interface not in fetched_zones:
+ fetched_zones.append(local_interface)
+
+
+def fetch_platform_mapping(mapping: dict[str, Any], fetched_zones: list[str]) -> None:
+ for dyn_mapping in mapping["platform_mapping"]:
+ if "intf-zone" in dyn_mapping and dyn_mapping["intf-zone"] not in fetched_zones:
+ fetched_zones.append(dyn_mapping["intf-zone"])
+
+
+def find_zones_in_normalized_config(
+ native_zone_list: list[str], normalized_config_adom: dict[str, Any], normalized_config_global: dict[str, Any]
+) -> list[str]:
+ """Verifies that input zones exist in normalized config"""
+ zone_out_list: list[str] = []
+ for nativ_zone in native_zone_list:
+ was_zone_found = False
+ for normalized_zone in normalized_config_adom["zone_objects"] + normalized_config_global["zone_objects"]:
+ if nativ_zone == normalized_zone["zone_name"]:
+ zone_out_list.append(normalized_zone["zone_name"])
+ was_zone_found = True
+ break
+ if not was_zone_found:
+ raise FwoNormalizedConfigParseError("Could not find zone " + nativ_zone + " in normalized config.")
+ return sorted(zone_out_list)
diff --git a/roles/importer/files/importer/fw_modules/fortiadom5ff/fwcommon.py b/roles/importer/files/importer/fw_modules/fortiadom5ff/fwcommon.py
new file mode 100644
index 0000000000..cc3fbe1c59
--- /dev/null
+++ b/roles/importer/files/importer/fw_modules/fortiadom5ff/fwcommon.py
@@ -0,0 +1,506 @@
+from copy import deepcopy
+from typing import Any
+
+import fwo_const
+from fw_modules.fortiadom5ff import fmgr_getter
+from fw_modules.fortiadom5ff.fmgr_consts import nw_obj_types, svc_obj_types, user_obj_types
+from fw_modules.fortiadom5ff.fmgr_network import normalize_network_objects
+from fw_modules.fortiadom5ff.fmgr_rule import get_access_policy, get_nat_policy, normalize_rulebases
+from fw_modules.fortiadom5ff.fmgr_service import normalize_service_objects
+from fw_modules.fortiadom5ff.fmgr_zone import get_zones, normalize_zones
+from fwo_base import ConfigAction, write_native_config_to_file
+from fwo_exceptions import FwLoginFailedError, FwLogoutFailedError, ImportInterruptionError
+from fwo_log import FWOLogger
+from model_controllers.fwconfigmanagerlist_controller import FwConfigManagerListController
+from model_controllers.import_state_controller import ImportStateController
+from models.fw_common import FwCommon
+from models.fwconfig_normalized import FwConfigNormalized
+from models.fwconfigmanager import FwConfigManager
+from models.import_state import ImportState
+from models.management import Management
+from utils.conversion_utils import convert_list_to_dict
+
+
+class FortiAdom5ffCommon(FwCommon):
+ def get_config(
+ self, config_in: FwConfigManagerListController, import_state: ImportStateController
+ ) -> tuple[int, FwConfigManagerListController]:
+ return get_config(config_in, import_state)
+
+
+def get_config(
+ config_in: FwConfigManagerListController, import_state: ImportStateController
+) -> tuple[int, FwConfigManagerListController]:
+ if config_in.has_empty_config(): # no native config was passed in, so getting it from FW-Manager
+ config_in.native_config.update({"domains": []}) # type: ignore #TYPING: What is this? None or not None this is the question # noqa: PGH003
+ parsing_config_only = False
+ else:
+ parsing_config_only = True
+
+ if not parsing_config_only: # no native config was passed in, so getting it from FortiManager
+ sid = get_sid(import_state.state)
+ limit = import_state.state.fwo_config.api_fetch_size
+ fm_api_url = import_state.state.mgm_details.build_fw_api_string()
+ native_config_global = initialize_native_config_domain(import_state.state.mgm_details)
+ config_in.native_config["domains"].append(native_config_global) # type: ignore #TYPING: None or not None this is the question # noqa: PGH003
+ adom_list = build_adom_list(import_state.state)
+ adom_device_vdom_structure = build_adom_device_vdom_structure(adom_list, sid, fm_api_url)
+ # delete_v: das geht schief für unschöne adoms
+ arbitrary_vdom_for_updateable_objects = get_arbitrary_vdom(adom_device_vdom_structure)
+ adom_device_vdom_policy_package_structure = add_policy_package_to_vdoms(
+ adom_device_vdom_structure, sid, fm_api_url
+ )
+
+ # get global
+ get_objects(
+ sid,
+ fm_api_url,
+ native_config_global,
+ native_config_global,
+ "",
+ limit,
+ nw_obj_types,
+ svc_obj_types,
+ "global",
+ arbitrary_vdom_for_updateable_objects,
+ )
+ get_zones(sid, fm_api_url, native_config_global, "", limit)
+
+ for adom in adom_list:
+ adom_name = adom.domain_name
+ native_config_adom = initialize_native_config_domain(adom)
+ config_in.native_config["domains"].append(native_config_adom) # type: ignore #TYPING: None or not None this is the question # noqa: PGH003
+
+ adom_scope = "adom/" + adom_name
+ get_objects(
+ sid,
+ fm_api_url,
+ native_config_adom,
+ native_config_global,
+ adom_name,
+ limit,
+ nw_obj_types,
+ svc_obj_types,
+ adom_scope,
+ arbitrary_vdom_for_updateable_objects,
+ )
+ # currently reading zone from objects/rules for backward compat with FortiManager 6.x
+ get_zones(sid, fm_api_url, native_config_adom, adom_name, limit)
+
+ # TODO: bring interfaces and routing in new domain native config format
+ # getInterfacesAndRouting(
+ # sid, fm_api_url, nativeConfig, adom_name, adom.Devices, limit)
+
+ for mgm_details_device in adom.devices:
+ device_config = initialize_device_config(mgm_details_device)
+ native_config_adom["gateways"].append(device_config)
+ get_access_policy(
+ sid,
+ fm_api_url,
+ native_config_adom,
+ native_config_global,
+ adom_device_vdom_policy_package_structure,
+ adom_name,
+ mgm_details_device,
+ device_config,
+ limit,
+ )
+ get_nat_policy(
+ sid,
+ fm_api_url,
+ native_config_adom,
+ adom_device_vdom_policy_package_structure,
+ adom_name,
+ mgm_details_device,
+ limit,
+ )
+
+ try: # logout of fortimanager API
+ fmgr_getter.logout(fm_api_url, sid)
+ except Exception:
+ raise FwLogoutFailedError("logout exception probably due to timeout - irrelevant, so ignoring it")
+
+ write_native_config_to_file(import_state.state, config_in.native_config)
+
+ if not config_in.native_config:
+ raise ImportError("native config missing")
+
+ normalized_managers = normalize_config(config_in.native_config)
+ FWOLogger.info("completed getting config")
+ return 0, normalized_managers
+
+
+def initialize_native_config_domain(mgm_details: Management) -> dict[str, Any]:
+ return {
+ "domain_name": mgm_details.domain_name,
+ "domain_uid": mgm_details.domain_uid,
+ "is-super-manager": mgm_details.is_super_manager,
+ "management_name": mgm_details.name,
+ "management_uid": mgm_details.uid,
+ "objects": [],
+ "rulebases": [],
+ "nat_rulebases": [],
+ "zones": [],
+ "gateways": [],
+ }
+
+
+def get_arbitrary_vdom(adom_device_vdom_structure: dict[str, dict[str, dict[str, Any]]]) -> dict[str, str] | None:
+ for adom in adom_device_vdom_structure: # noqa: PLC0206
+ for device in adom_device_vdom_structure[adom]:
+ for vdom in adom_device_vdom_structure[adom][device]:
+ return {"adom": adom, "device": device, "vdom": vdom}
+ return None
+
+
+def normalize_config(native_config: dict[str, Any]) -> FwConfigManagerListController:
+ manager_list = FwConfigManagerListController()
+
+ if "domains" not in native_config:
+ raise ImportInterruptionError("No domains found in native config. Cannot normalize config.")
+
+ rewrite_native_config_obj_type_as_key(native_config) # for easier accessability of objects in normalization process
+
+ native_config_global: dict[str, Any] = {}
+ normalized_config_global = {}
+
+ for native_conf in native_config["domains"]:
+ normalized_config_adom = deepcopy(fwo_const.EMPTY_NORMALIZED_FW_CONFIG_JSON_DICT)
+ is_global_loop_iteration = False
+
+ if native_conf["is-super-manager"]:
+ native_config_global = native_conf
+ normalized_config_global = normalized_config_adom
+ is_global_loop_iteration = True
+
+ normalize_single_manager_config(
+ native_conf,
+ native_config_global,
+ normalized_config_adom,
+ normalized_config_global,
+ is_global_loop_iteration,
+ )
+
+ normalized_config = FwConfigNormalized(
+ action=ConfigAction.INSERT,
+ network_objects=convert_list_to_dict(normalized_config_adom.get("network_objects", []), "obj_uid"),
+ service_objects=convert_list_to_dict(normalized_config_adom.get("service_objects", []), "svc_uid"),
+ zone_objects=convert_list_to_dict(normalized_config_adom.get("zone_objects", []), "zone_name"),
+ rulebases=normalized_config_adom.get("policies", []),
+ gateways=normalized_config_adom.get("gateways", []),
+ )
+
+ # TODO: identify the correct manager
+
+ manager = FwConfigManager(
+ manager_uid=native_conf.get("management_uid", ""),
+ manager_name=native_conf.get("management_name", ""),
+ is_super_manager=native_conf.get("is-super-manager", False),
+ domain_name=native_conf.get("domain_name", ""),
+ domain_uid=native_conf.get("domain_uid", ""),
+ sub_manager_ids=[],
+ configs=[normalized_config],
+ )
+
+ manager_list.add_manager(manager)
+
+ return manager_list
+
+
+def rewrite_native_config_obj_type_as_key(native_config: dict[str, Any]):
+ # rewrite native config objects to have the object type as key
+ # this is needed for the normalization process
+
+ for domain in native_config["domains"]:
+ if "objects" not in domain:
+ continue
+ obj_dict: dict[str, Any] = {}
+ for obj_chunk in domain["objects"]:
+ if "type" not in obj_chunk:
+ continue
+ obj_type = obj_chunk["type"]
+ obj_dict.update({obj_type: obj_chunk})
+ domain["objects"] = obj_dict
+
+
+def normalize_single_manager_config(
+ native_config: "dict[str, Any]",
+ native_config_global: "dict[str, Any]",
+ normalized_config_adom: dict[str, Any],
+ normalized_config_global: dict[str, Any],
+ is_global_loop_iteration: bool,
+):
+ current_nw_obj_types = deepcopy(nw_obj_types)
+ current_svc_obj_types = deepcopy(svc_obj_types)
+ if native_config["is-super-manager"]:
+ current_nw_obj_types = ["nw_obj_global_" + t for t in current_nw_obj_types]
+ current_nw_obj_types.append("nw_obj_global_firewall/internet-service-basic")
+ current_svc_obj_types = ["svc_obj_global_" + t for t in current_svc_obj_types]
+ else:
+ current_nw_obj_types = [f"nw_obj_adom/{native_config.get('domain_name', '')}_{t}" for t in current_nw_obj_types]
+ current_svc_obj_types = [
+ f"svc_obj_adom/{native_config.get('domain_name', '')}_{t}" for t in current_svc_obj_types
+ ]
+
+ normalize_zones(native_config, normalized_config_adom, is_global_loop_iteration)
+ FWOLogger.info("completed normalizing zones for manager: " + native_config.get("domain_name", ""))
+ normalize_network_objects(native_config, normalized_config_adom, normalized_config_global, current_nw_obj_types)
+ FWOLogger.info("completed normalizing network objects for manager: " + native_config.get("domain_name", ""))
+ normalize_service_objects(native_config, normalized_config_adom, current_svc_obj_types)
+ FWOLogger.info("completed normalizing service objects for manager: " + native_config.get("domain_name", ""))
+ mgm_uid = native_config["management_uid"]
+ normalize_rulebases(
+ mgm_uid,
+ native_config,
+ native_config_global,
+ normalized_config_adom,
+ normalized_config_global,
+ is_global_loop_iteration,
+ )
+ FWOLogger.info("completed normalizing rulebases for manager: " + native_config.get("domain_name", ""))
+
+ normalize_gateways(native_config, normalized_config_adom)
+
+
+def build_adom_list(import_state: ImportState) -> list[Management]:
+ adom_list: list[Management] = []
+ if import_state.mgm_details.is_super_manager:
+ adom_list = [deepcopy(sub_manager) for sub_manager in import_state.mgm_details.sub_managers]
+ return adom_list
+
+
+def build_adom_device_vdom_structure(
+ adom_list: list[Management], sid: str, fm_api_url: str
+) -> dict[str, dict[str, dict[str, Any]]]:
+ adom_device_vdom_structure: dict[str, dict[str, dict[str, Any]]] = {}
+ for adom in adom_list:
+ adom_device_vdom_structure.update({adom.domain_name: {}})
+ if len(adom.devices) > 0:
+ device_vdom_dict = fmgr_getter.get_devices_from_manager(adom, sid, fm_api_url)
+ adom_device_vdom_structure[adom.domain_name].update(device_vdom_dict)
+ return adom_device_vdom_structure
+
+
+def add_policy_package_to_vdoms(
+ adom_device_vdom_structure: dict[str, dict[str, dict[str, str]]], sid: str, fm_api_url: str
+) -> dict[str, dict[str, dict[str, Any]]]:
+ adom_device_vdom_policy_package_structure = deepcopy(adom_device_vdom_structure)
+ for adom in adom_device_vdom_policy_package_structure:
+ policy_packages_result = fmgr_getter.fortinet_api_call(sid, fm_api_url, "/pm/pkg/adom/" + adom)
+ for policy_package in policy_packages_result:
+ if "scope member" in policy_package:
+ parse_policy_package(policy_package, adom_device_vdom_policy_package_structure, adom)
+ add_global_policy_package_to_vdom(adom_device_vdom_policy_package_structure, sid, fm_api_url, adom)
+ return adom_device_vdom_policy_package_structure
+
+
+def parse_policy_package(
+ policy_package: dict[str, Any],
+ adom_device_vdom_policy_package_structure: dict[str, dict[str, dict[str, Any]]],
+ adom: str,
+):
+ for scope_member in policy_package["scope member"]:
+ for device in adom_device_vdom_policy_package_structure[adom]:
+ if device == scope_member["name"]:
+ for vdom in adom_device_vdom_policy_package_structure[adom][device]:
+ if vdom == scope_member["vdom"]:
+ adom_device_vdom_policy_package_structure[adom][device].update(
+ {vdom: {"local": policy_package["name"], "global": ""}}
+ )
+
+
+def add_global_policy_package_to_vdom(
+ adom_device_vdom_policy_package_structure: dict[str, dict[str, dict[str, Any]]],
+ sid: str,
+ fm_api_url: str,
+ adom: str,
+):
+ global_assignment_result = fmgr_getter.fortinet_api_call(
+ sid, fm_api_url, "/pm/config/adom/" + adom + "/_adom/options"
+ )
+ for global_assignment in global_assignment_result:
+ if global_assignment["assign_excluded"] == 0 and global_assignment["specify_assign_pkg_list"] == 0:
+ assign_case_all(adom_device_vdom_policy_package_structure, adom, global_assignment)
+ elif global_assignment["assign_excluded"] == 0 and global_assignment["specify_assign_pkg_list"] == 1:
+ assign_case_include(adom_device_vdom_policy_package_structure, adom, global_assignment)
+ elif global_assignment["assign_excluded"] == 1 and global_assignment["specify_assign_pkg_list"] == 1:
+ assign_case_exclude(adom_device_vdom_policy_package_structure, adom, global_assignment)
+ else:
+ raise ImportInterruptionError("Broken global assign format.")
+
+
+def assign_case_all(
+ adom_device_vdom_policy_package_structure: dict[str, dict[str, dict[str, Any]]],
+ adom: str,
+ global_assignment: dict[str, Any],
+):
+ for device in adom_device_vdom_policy_package_structure[adom]:
+ for vdom in adom_device_vdom_policy_package_structure[adom][device]:
+ adom_device_vdom_policy_package_structure[adom][device][vdom]["global"] = global_assignment["assign_name"]
+
+
+def assign_case_include(
+ adom_device_vdom_policy_package_structure: dict[str, dict[str, dict[str, Any]]],
+ adom: str,
+ global_assignment: dict[str, Any],
+):
+ for device in adom_device_vdom_policy_package_structure[adom]:
+ for vdom in adom_device_vdom_policy_package_structure[adom][device]:
+ match_assign_and_vdom_policy_package(
+ global_assignment, adom_device_vdom_policy_package_structure[adom][device][vdom], is_include=True
+ )
+
+
+def assign_case_exclude(
+ adom_device_vdom_policy_package_structure: dict[str, dict[str, dict[str, Any]]],
+ adom: str,
+ global_assignment: dict[str, Any],
+):
+ for device in adom_device_vdom_policy_package_structure[adom]:
+ for vdom in adom_device_vdom_policy_package_structure[adom][device]:
+ match_assign_and_vdom_policy_package(
+ global_assignment, adom_device_vdom_policy_package_structure[adom][device][vdom], is_include=False
+ )
+
+
+def match_assign_and_vdom_policy_package(
+ global_assignment: dict[str, Any], vdom_structure: dict[str, Any], is_include: bool
+):
+ for package in global_assignment["pkg list"]:
+ if is_include:
+ if package["name"] == vdom_structure["local"]:
+ vdom_structure["global"] = global_assignment["assign_name"]
+ elif package["name"] != vdom_structure["local"]:
+ vdom_structure["global"] = global_assignment["assign_name"]
+
+
+def initialize_device_config(mgm_details_device: dict[str, Any]) -> dict[str, Any]:
+ device_config: dict[str, Any] = {
+ "name": mgm_details_device["name"],
+ "uid": mgm_details_device["uid"],
+ "rulebase_links": [],
+ }
+ return device_config
+
+
+def get_sid(import_state: ImportState):
+ fm_api_url = "https://" + import_state.mgm_details.hostname + ":" + str(import_state.mgm_details.port) + "/jsonrpc"
+ sid = fmgr_getter.login(import_state.mgm_details.import_user, import_state.mgm_details.secret, fm_api_url)
+ if sid is None:
+ raise FwLoginFailedError("did not succeed in logging in to FortiManager API, no sid returned")
+ return sid
+
+
+def get_objects(
+ sid: str,
+ fm_api_url: str,
+ native_config_domain: dict[str, Any],
+ native_config_global: dict[str, Any],
+ adom_name: str,
+ limit: int,
+ nw_obj_types: list[str],
+ svc_obj_types: list[str],
+ adom_scope: str,
+ arbitrary_vdom_for_updateable_objects: dict[str, Any] | None,
+):
+ # get those objects that exist globally and on adom level
+ api_base_path = f"/pm/config/{adom_scope}/obj/"
+
+ # get network objects:
+ for object_type in nw_obj_types:
+ fmgr_getter.update_config_with_fortinet_api_call(
+ native_config_domain["objects"],
+ sid,
+ fm_api_url,
+ api_base_path + object_type,
+ "nw_obj_" + adom_scope + "_" + object_type,
+ limit=limit,
+ )
+
+ # get service objects:
+ # service/custom is an undocumented API call!
+ for object_type in svc_obj_types:
+ fmgr_getter.update_config_with_fortinet_api_call(
+ native_config_domain["objects"],
+ sid,
+ fm_api_url,
+ api_base_path + object_type,
+ "svc_obj_" + adom_scope + "_" + object_type,
+ limit=limit,
+ )
+
+ # user: /pm/config/global/obj/user/local, /pm/config/global/obj/user/group
+ # get user objects:
+ for object_type in user_obj_types:
+ fmgr_getter.update_config_with_fortinet_api_call(
+ native_config_domain["objects"],
+ sid,
+ fm_api_url,
+ api_base_path + object_type,
+ "user_obj_" + adom_scope + "_" + object_type,
+ limit=limit,
+ )
+
+ # get one arbitrary device and vdom to get dynamic objects
+ # they are equal across all adoms, vdoms, devices
+ if arbitrary_vdom_for_updateable_objects is None:
+ FWOLogger.error("arbitrary_vdom_for_updateable_objects is None, cannot get dynamic objects")
+ return
+ if arbitrary_vdom_for_updateable_objects["adom"] == adom_name:
+ # get dynamic objects
+ payload: dict[str, Any] = {
+ "params": [
+ {
+ "data": {
+ "action": "get",
+ "resource": "/api/v2/monitor/firewall/internet-service-basic?vdom="
+ + arbitrary_vdom_for_updateable_objects["vdom"],
+ "target": ["adom/" + adom_name + "/device/" + arbitrary_vdom_for_updateable_objects["device"]],
+ }
+ }
+ ]
+ }
+ fmgr_getter.update_config_with_fortinet_api_call(
+ native_config_global["objects"],
+ sid,
+ fm_api_url,
+ "sys/proxy/json",
+ "nw_obj_global_firewall/internet-service-basic",
+ limit=limit,
+ payload=payload,
+ method="exec",
+ )
+
+
+def normalize_gateways(native_config: dict[str, Any], normalized_config_adom: dict[str, Any]):
+ for gateway in native_config["gateways"]:
+ normalized_gateway = {}
+ normalized_gateway["Uid"] = gateway["uid"]
+ normalized_gateway["Name"] = gateway["name"]
+ normalized_gateway["Interfaces"] = normalize_interfaces()
+ normalized_gateway["Routing"] = normalize_routing()
+ normalized_gateway["RulebaseLinks"] = normalize_links(gateway["rulebase_links"])
+ normalized_config_adom["gateways"].append(normalized_gateway)
+
+
+def normalize_interfaces() -> list[Any]:
+ # TODO: Implement interface normalization
+ return []
+
+
+def normalize_routing() -> list[Any]:
+ # TODO: Implement routing normalization
+ return []
+
+
+def normalize_links(rulebase_links: list[dict[str, Any]]) -> list[dict[str, Any]]:
+ for link in rulebase_links:
+ link["link_type"] = link.pop("type")
+
+ # Remove from_rulebase_uid and from_rule_uid if link_type is initial
+ if link["link_type"] == "initial":
+ if link["from_rulebase_uid"] is not None:
+ link["from_rulebase_uid"] = None
+ if link["from_rule_uid"] is not None:
+ link["from_rule_uid"] = None
+ return rulebase_links
diff --git a/roles/importer/files/importer/fortiosmanagementREST/__init__.py b/roles/importer/files/importer/fw_modules/fortiosmanagementREST/__init__.py
similarity index 100%
rename from roles/importer/files/importer/fortiosmanagementREST/__init__.py
rename to roles/importer/files/importer/fw_modules/fortiosmanagementREST/__init__.py
diff --git a/roles/importer/files/importer/fw_modules/fortiosmanagementREST/fwcommon.py b/roles/importer/files/importer/fw_modules/fortiosmanagementREST/fwcommon.py
new file mode 100644
index 0000000000..2886ff5834
--- /dev/null
+++ b/roles/importer/files/importer/fw_modules/fortiosmanagementREST/fwcommon.py
@@ -0,0 +1,10 @@
+from model_controllers.fwconfigmanagerlist_controller import FwConfigManagerListController
+from model_controllers.import_state_controller import ImportStateController
+from models.fw_common import FwCommon
+
+
+class FortiosManagementRESTCommon(FwCommon):
+ def get_config(
+ self, config_in: FwConfigManagerListController, import_state: ImportStateController
+ ) -> tuple[int, FwConfigManagerListController]:
+ raise NotImplementedError("Fortios Management REST is not supported yet in the new python importer.")
diff --git a/roles/importer/files/importer/nsx4ff/__init__.py b/roles/importer/files/importer/fw_modules/nsx4ff/__init__.py
similarity index 100%
rename from roles/importer/files/importer/nsx4ff/__init__.py
rename to roles/importer/files/importer/fw_modules/nsx4ff/__init__.py
diff --git a/roles/importer/files/importer/fw_modules/nsx4ff/fwcommon.py b/roles/importer/files/importer/fw_modules/nsx4ff/fwcommon.py
new file mode 100644
index 0000000000..4b94bcd03d
--- /dev/null
+++ b/roles/importer/files/importer/fw_modules/nsx4ff/fwcommon.py
@@ -0,0 +1,10 @@
+from model_controllers.fwconfigmanagerlist_controller import FwConfigManagerListController
+from model_controllers.import_state_controller import ImportStateController
+from models.fw_common import FwCommon
+
+
+class Nsx4ffCommon(FwCommon):
+ def get_config(
+ self, config_in: FwConfigManagerListController, import_state: ImportStateController
+ ) -> tuple[int, FwConfigManagerListController]:
+ raise NotImplementedError("NSX 4ff is not supported yet in the new python importer.")
diff --git a/roles/importer/files/importer/paloaltomanagement2023ff/__init__.py b/roles/importer/files/importer/fw_modules/paloaltomanagement2023ff/__init__.py
similarity index 100%
rename from roles/importer/files/importer/paloaltomanagement2023ff/__init__.py
rename to roles/importer/files/importer/fw_modules/paloaltomanagement2023ff/__init__.py
diff --git a/roles/importer/files/importer/fw_modules/paloaltomanagement2023ff/fwcommon.py b/roles/importer/files/importer/fw_modules/paloaltomanagement2023ff/fwcommon.py
new file mode 100644
index 0000000000..5313a7b4b2
--- /dev/null
+++ b/roles/importer/files/importer/fw_modules/paloaltomanagement2023ff/fwcommon.py
@@ -0,0 +1,10 @@
+from model_controllers.fwconfigmanagerlist_controller import FwConfigManagerListController
+from model_controllers.import_state_controller import ImportStateController
+from models.fw_common import FwCommon
+
+
+class PaloAltoManagement2023ffCommon(FwCommon):
+ def get_config(
+ self, config_in: FwConfigManagerListController, import_state: ImportStateController
+ ) -> tuple[int, FwConfigManagerListController]:
+ raise NotImplementedError("Palo Alto Management 2023 ff is not supported yet in the new python importer.")
diff --git a/roles/importer/files/importer/fwconfig_base.py b/roles/importer/files/importer/fwconfig_base.py
new file mode 100644
index 0000000000..4185956ab2
--- /dev/null
+++ b/roles/importer/files/importer/fwconfig_base.py
@@ -0,0 +1,17 @@
+import json
+
+from fwo_enums import ConfFormat, ConfigAction
+
+
+class FwoEncoder(json.JSONEncoder):
+ def default(self, o: object) -> object:
+ if isinstance(o, (ConfigAction, ConfFormat)):
+ return o.name
+
+ return json.JSONEncoder.default(self, o)
+
+
+def replace_none_with_empty(s: str | None) -> str: # TYPING: make a utils file and move there
+ if s is None or s == "":
+ return ""
+ return str(s)
diff --git a/roles/importer/files/importer/fwo_alert.py b/roles/importer/files/importer/fwo_alert.py
deleted file mode 100644
index b9086ee054..0000000000
--- a/roles/importer/files/importer/fwo_alert.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import fwo_api
-import json
-import fwo_const
-import fwo_log
-
-def getFwoAlerter():
- logger = fwo_log.getFwoLogger()
- try:
- with open(fwo_const.fwo_config_filename, "r") as fwo_config:
- fwo_config = json.loads(fwo_config.read())
- user_management_api_base_url = fwo_config['middleware_uri']
- fwo_api_base_url = fwo_config['api_uri']
- except:
- logger.error("getFwoAlerter - error while reading FWO config file")
- raise
-
- try:
- with open(fwo_const.base_dir + '/etc/secrets/importer_pwd', 'r') as file:
- importer_pwd = file.read().replace('\n', '')
- except:
- logger.error("getFwoAlerter - error while reading importer pwd file")
- raise
-
- jwt = fwo_api.login(fwo_const.importer_user_name, importer_pwd, user_management_api_base_url)
-
- return { "fwo_api_base_url": fwo_api_base_url, "jwt": jwt }
-
-# fwo_api.create_data_issue(fwo_api_base_url, jwt, import_id=import_id, obj_name=obj['obj_name'], severity=1, rule_uid=rule_uid, mgm_id=mgm_id, object_type=obj['obj_typ'])
diff --git a/roles/importer/files/importer/fwo_api.py b/roles/importer/files/importer/fwo_api.py
index 0b74713e2d..cde4245619 100644
--- a/roles/importer/files/importer/fwo_api.py
+++ b/roles/importer/files/importer/fwo_api.py
@@ -1,692 +1,475 @@
-# library for FWORCH API calls
-import re
-import traceback
-import requests.packages
-import requests
import json
-import datetime
+import string
import time
+import traceback
+from collections.abc import MutableMapping
+from pprint import pformat
+from typing import Any
-from fwo_const import fwo_config_filename
-from fwo_log import getFwoLogger
import fwo_globals
-import fwo_const
-from fwo_const import fwo_api_http_import_timeout
-from fwo_exception import FwoApiServiceUnavailable, FwoApiTimeout, FwoApiLoginFailed, SecretDecryptionFailed
-from fwo_base import writeAlertToLogFile
-from fwo_encrypt import decrypt
-
-def showApiCallInfo(url, query, headers, type='debug'):
- max_query_size_to_display = 1000
- query_string = json.dumps(query, indent=2)
- header_string = json.dumps(headers, indent=2)
- query_size = len(query_string)
-
- if type=='error':
- result = "error while sending api_call to url "
- else:
- result = "successful FWO API call to url "
- result += str(url) + " with payload \n"
- if query_size < max_query_size_to_display:
- result += query_string
- else:
- result += str(query)[:round(max_query_size_to_display/2)] + "\n ... [snip] ... \n" + \
- query_string[query_size-round(max_query_size_to_display/2):] + " (total query size=" + str(query_size) + " bytes)"
- result += "\n and headers: \n" + header_string
- return result
-
-
-# standard FWO API call
-def call(url, jwt, query, query_variables="", role="reporter", show_progress=False, method=''):
- request_headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + jwt, 'x-hasura-role': role }
- full_query = {"query": query, "variables": query_variables}
- logger = getFwoLogger()
-
- with requests.Session() as session:
- if fwo_globals.verify_certs is None: # only for first FWO API call (getting info on cert verification)
- session.verify = False
- else:
- session.verify = fwo_globals.verify_certs
- session.headers = request_headers
+import requests
+from fwo_const import FWO_API_HTTP_IMPORT_TIMEOUT
+from fwo_exceptions import FwoApiLoginFailedError, FwoApiServiceUnavailableError, FwoApiTimeoutError, FwoImporterError
+from fwo_log import FWOLogger
+from query_analyzer import QueryAnalyzer
+from services.service_provider import ServiceProvider
+
+JSON_CONTENT_TYPE = "application/json"
+
+
+# this class is used for making calls to the FWO API (will supersede fwo_api.py)
+class FwoApi:
+ fwo_api_url: str
+ fwo_jwt: str
+ query_info: dict[str, Any]
+ query_analyzer: QueryAnalyzer
+
+ def __init__(self, api_uri: str, jwt: str):
+ self.fwo_api_url = api_uri
+ self.fwo_jwt = jwt
+ self.query_info = {}
+ self.query_analyzer = QueryAnalyzer()
+
+ def call(
+ self,
+ query: str,
+ query_variables: dict[str, list[Any] | Any] | None = None,
+ analyze_payload: bool = False,
+ ) -> dict[str, Any]:
+ """
+ The standard FWO API call.
+ """
+ if query_variables is None:
+ query_variables = {}
+ role = "importer"
+ request_headers = {
+ "Content-Type": JSON_CONTENT_TYPE,
+ "Authorization": f"Bearer {self.fwo_jwt}",
+ "x-hasura-role": role,
+ }
+ full_query: dict[str, Any] = {"query": query, "variables": query_variables}
+ return_object = {}
+
+ if analyze_payload:
+ self.query_info = self.query_analyzer.analyze_payload(query, query_variables)
try:
- r = session.post(url, data=json.dumps(full_query), timeout=int(fwo_api_http_import_timeout))
- r.raise_for_status()
- except requests.exceptions.HTTPError as http_err:
- logger.error(showApiCallInfo(url, full_query, request_headers, type='error') + ":\n" + str(traceback.format_exc()))
- print(f"HTTP error occurred: {http_err}")
- if http_err.errno == 503:
- raise FwoApiServiceUnavailable("FWO API HTTP error 503 (FWO API died?)" )
- if http_err.errno == 502:
- raise FwoApiTimeout("FWO API HTTP error 502 (might have reached timeout of " + str(int(fwo_api_http_import_timeout)/60) + " minutes)" )
+ with requests.Session() as session:
+ if fwo_globals.verify_certs is None: # only for first FWO API call (getting info on cert verification)
+ session.verify = False
+ else:
+ session.verify = fwo_globals.verify_certs
+ session.headers.update(request_headers)
+
+ if analyze_payload and self.query_info["chunking_info"]["needs_chunking"]:
+ started = time.time()
+ return_object: dict[str, Any] = self._call_chunked(session, query, query_variables)
+ elapsed_time = time.time() - started
+ affected_rows = 0
+ if "data" in return_object and "affected_rows" in return_object["data"]:
+ # If the return object contains data, we can log the affected rows.
+ affected_rows = sum(obj["affected_rows"] for obj in return_object["data"].values())
+ FWOLogger.debug(
+ f"Chunked API call ({self.query_info['query_name']}) processed in {elapsed_time:.4f} s. Affected rows: {affected_rows}."
+ )
+ self.query_info = {}
+ else:
+ return_object: dict[str, Any] = self._post_query(session, full_query)
+
+ self._try_show_api_call_info(full_query, request_headers)
+
+ return return_object
+
+ except requests.exceptions.RequestException as e:
+ self._handle_request_exception(e, full_query, request_headers)
+ except FwoImporterError as e:
+ # Handle FwoImporterError specifically, logging it and re-raising.
+ FWOLogger.error(f"FwoImporterError during API call: {e!s}")
+ raise
+ except Exception as e:
+ # Catch all other exceptions and log them.
+ FWOLogger.error(f"Unexpected error during API call: {e!s}")
+ FWOLogger.debug(pformat(self.query_info))
+ try:
+ FWOLogger.debug(pformat(return_object))
+ except NameError:
+ FWOLogger.error(f"Unexpected error during API call: {e!s}")
+ raise FwoImporterError(f"return_object not defined. Error during API call: {e!s}")
+ raise FwoImporterError(f"Unexpected error during API call: {e!s}")
+ return return_object
+
+ @staticmethod
+ def login(
+ user: str,
+ password: str | None,
+ user_management_api_base_url: str | None,
+ method: str = "api/AuthenticationToken/Get",
+ ):
+ payload: dict[str, str | None] = {"Username": user, "Password": password}
+
+ if user_management_api_base_url is None:
+ raise FwoApiLoginFailedError("fwo_api: user_management_api_base_url is None during login")
+
+ with requests.Session() as session:
+ if fwo_globals.verify_certs is None: # only for first FWO API call (getting info on cert verification)
+ session.verify = False
else:
- raise
- except Exception as err:
- print(f"Other error occurred: {err}")
-
- if int(fwo_globals.debug_level) > 4:
- logger.debug (showApiCallInfo(url, full_query, request_headers, type='debug'))
- if show_progress:
- pass
- # print('.', end='', flush=True)
- if r is not None:
- return r.json()
- else:
- return None
+ session.verify = fwo_globals.verify_certs
+ session.headers = {"Content-Type": JSON_CONTENT_TYPE}
+ try:
+ response = session.post(user_management_api_base_url + method, data=json.dumps(payload))
+ except requests.exceptions.RequestException:
+ raise FwoApiLoginFailedError(
+ "fwo_api: error during login to url: " + str(user_management_api_base_url) + " with user " + user
+ ) from None
+
+ if response.status_code == 200: # noqa: PLR2004
+ return response.text
+ error_txt = (
+ "fwo_api: ERROR: did not receive a JWT during login"
+ ", api_url: "
+ + str(user_management_api_base_url)
+ + ", ssl_verification: "
+ + str(fwo_globals.verify_certs)
+ )
+ raise FwoApiLoginFailedError(error_txt)
+
+ def call_endpoint(self, method: str, endpoint: str, params: Any = None) -> Any:
+ """
+ Generic method to call any middleware endpoint.
-def login(user, password, user_management_api_base_url, method='api/AuthenticationToken/Get'):
- payload = {"Username": user, "Password": password}
+ Args:
+ method: HTTP method (GET, POST, PUT, DELETE, PATCH)
+ endpoint: API endpoint path (e.g., "AuthenticationToken/Get", "User", "Role/User")
+ data: Request payload data
- with requests.Session() as session:
- if fwo_globals.verify_certs is None: # only for first FWO API call (getting info on cert verification)
- session.verify = False
- else:
- session.verify = fwo_globals.verify_certs
- session.headers = {'Content-Type': 'application/json'}
+ Returns:
+ Response data - could be various types based on the endpoint
- try:
- response = session.post(user_management_api_base_url + method, data=json.dumps(payload))
- except requests.exceptions.RequestException:
- raise FwoApiLoginFailed ("fwo_api: error during login to url: " + str(user_management_api_base_url) + " with user " + user) from None
+ Raises:
+ FwoApiLoginFailed: If authentication fails
+ FwoImporterError: If request fails or returns error
- if response.text is not None and response.status_code==200:
- return response.text
- else:
- error_txt = "fwo_api: ERROR: did not receive a JWT during login" + \
- ", api_url: " + str(user_management_api_base_url) + \
- ", ssl_verification: " + str(fwo_globals.verify_certs)
- raise FwoApiLoginFailed(error_txt)
-
-
-def set_api_url(base_url, testmode, api_supported, hostname):
- logger = getFwoLogger()
- url = ''
- if testmode == 'off':
- url = base_url
- else:
- if re.search(r'^\d+[\.\d+]+$', testmode) or re.search(r'^\d+$', testmode):
- if testmode in api_supported:
- url = base_url + 'v' + testmode + '/'
+ """
+ service_provider = ServiceProvider()
+ fwo_config = service_provider.get_fwo_config()
+ url = fwo_config["user_management_api_base_url"] + endpoint.lstrip("/")
+
+ with requests.Session() as session:
+ if fwo_globals.verify_certs is None:
+ session.verify = False
else:
- exception_text = "api version " + testmode + \
- " is not supported by the manager " + hostname + " - Import is canceled"
- raise Exception(exception_text)
- else:
- raise Exception("\"" + testmode + "\" - not a valid version")
- logger.debug("testmode: " + testmode + " - url: " + url)
- return url
-
-
-def get_mgm_ids(fwo_api_base_url, jwt, query_variables):
- mgm_query = """
- query getManagementIds {
- management(where:{do_not_import:{_eq:false}} order_by: {mgm_name: asc}) { id: mgm_id } } """
- return call(fwo_api_base_url, jwt, mgm_query, query_variables=query_variables, role='importer')['data']['management']
-
-
-def get_config_value(fwo_api_base_url, jwt, key='limit'):
- query_variables = {'key': key}
- config_query = "query getConf($key: String) { config(where: {config_key: {_eq: $key}}) { config_value } }"
- result = call(fwo_api_base_url, jwt, config_query, query_variables=query_variables, role='importer')
- if 'data' in result and 'config' in result['data']:
- first_result = result['data']['config'][0]
- if 'config_value' in first_result:
- return first_result['config_value']
- else:
- return None
- else:
- return None
-
-
-def get_config_values(fwo_api_base_url, jwt, keyFilter='limit'):
- query_variables = {'keyFilter': keyFilter+"%"}
- config_query = "query getConf($keyFilter: String) { config(where: {config_key: {_ilike: $keyFilter}}) { config_key config_value } }"
- result = call(fwo_api_base_url, jwt, config_query, query_variables=query_variables, role='importer')
- if 'data' in result and 'config' in result['data']:
- resultArray = result['data']['config']
- dict1 = {v['config_key']: v['config_value'] for k,v in enumerate(resultArray)}
- return dict1
- else:
- return None
-
-
-def get_mgm_details(fwo_api_base_url, jwt, query_variables, debug_level=0):
- mgm_query = """
- query getManagementDetails($mgmId: Int!) {
- management(where:{mgm_id:{_eq:$mgmId}} order_by: {mgm_name: asc}) {
- id: mgm_id
- name: mgm_name
- hostname: ssh_hostname
- port: ssh_port
- import_credential {
- id
- credential_name
- user: username
- secret
- sshPublicKey: public_key
- cloudClientId: cloud_client_id
- cloudClientSecret: cloud_client_secret
- }
- deviceType: stm_dev_typ {
- id: dev_typ_id
- name: dev_typ_name
- version: dev_typ_version
- }
- configPath: config_path
- domainUid: domain_uid
- cloudSubscriptionId: cloud_subscription_id
- cloudTenantId: cloud_tenant_id
- importDisabled: do_not_import
- forceInitialImport: force_initial_import
- importerHostname: importer_hostname
- debugLevel: debug_level
- lastConfigHash: last_import_md5_complete_config
- devices(where:{do_not_import:{_eq:false}}) {
- id: dev_id
- name: dev_name
- local_rulebase_name
- global_rulebase_name
- package_name
- }
- import_controls(where: { successful_import: {_eq: true} } order_by: {control_id: desc}, limit: 1) {
- starttime: start_time
- }
- }
- }
- """
- api_call_result = call(fwo_api_base_url, jwt, mgm_query, query_variables=query_variables, role='importer')
- if 'data' in api_call_result and 'management' in api_call_result['data'] and len(api_call_result['data']['management'])>=1:
- if not '://' in api_call_result['data']['management'][0]['hostname']:
- # only decrypt if we have a real management and are not fetching the config from an URL
- # decrypt secret read from API
+ session.verify = fwo_globals.verify_certs
+
+ session.headers = {"Authorization": f"Bearer {self.fwo_jwt}", "Content-Type": JSON_CONTENT_TYPE}
+
try:
- secret = api_call_result['data']['management'][0]['import_credential']['secret']
- decryptedSecret = decrypt(secret, readMainKey())
- except ():
- raise SecretDecryptionFailed
- api_call_result['data']['management'][0]['import_credential']['secret'] = decryptedSecret
- return api_call_result['data']['management'][0]
- else:
- raise Exception('did not succeed in getting management details from FWO API')
-
-
-def readMainKey(filePath=fwo_const.mainKeyFile):
- with open(filePath, "r") as keyfile:
- mainKey = keyfile.read().rstrip(' \n')
- return mainKey
-
-
-# this mgm field is used by mw dailycheck scheduler
-def log_import_attempt(fwo_api_base_url, jwt, mgm_id, successful=False):
- now = datetime.datetime.now().isoformat()
- query_variables = { "mgmId": mgm_id, "timeStamp": now, "success": successful }
- mgm_mutation = """
- mutation logImportAttempt($mgmId: Int!, $timeStamp: timestamp!, $success: Boolean) {
- update_management(where: {mgm_id: {_eq: $mgmId}}, _set: {last_import_attempt: $timeStamp, last_import_attempt_successful: $success } ) { affected_rows }
- }"""
- return call(fwo_api_base_url, jwt, mgm_mutation, query_variables=query_variables, role='importer')
-
-
-def lock_import(fwo_api_base_url, jwt, query_variables):
- lock_mutation = "mutation lockImport($mgmId: Int!) { insert_import_control(objects: {mgm_id: $mgmId}) { returning { control_id } } }"
- lock_result = call(fwo_api_base_url, jwt, lock_mutation, query_variables=query_variables, role='importer')
- if lock_result['data']['insert_import_control']['returning'][0]['control_id']:
- return lock_result['data']['insert_import_control']['returning'][0]['control_id']
- else:
- return -1
-
-
-def count_rule_changes_per_import(fwo_api_base_url, jwt, import_id):
- logger = getFwoLogger()
- change_count_query = """
- query count_rule_changes($importId: bigint!) {
- changelog_rule_aggregate(where: {control_id: {_eq: $importId}}) { aggregate { count } }
- }"""
- try:
- count_result = call(fwo_api_base_url, jwt, change_count_query, query_variables={'importId': import_id}, role='importer')
- rule_changes_in_import = int(count_result['data']['changelog_rule_aggregate']['aggregate']['count'])
- except:
- logger.exception("failed to count changes for import id " + str(import_id))
- rule_changes_in_import = 0
- return rule_changes_in_import
-
-
-def count_any_changes_per_import(fwo_api_base_url, jwt, import_id):
- logger = getFwoLogger()
- change_count_query = """
- query count_changes($importId: bigint!) {
- changelog_object_aggregate(where: {control_id: {_eq: $importId}}) { aggregate { count } }
- changelog_service_aggregate(where: {control_id: {_eq: $importId}}) { aggregate { count } }
- changelog_user_aggregate(where: {control_id: {_eq: $importId}}) { aggregate { count } }
- changelog_rule_aggregate(where: {control_id: {_eq: $importId}}) { aggregate { count } }
- }"""
- try:
- count_result = call(fwo_api_base_url, jwt, change_count_query, query_variables={'importId': import_id}, role='importer')
- changes_in_import = int(count_result['data']['changelog_object_aggregate']['aggregate']['count']) + \
- int(count_result['data']['changelog_service_aggregate']['aggregate']['count']) + \
- int(count_result['data']['changelog_user_aggregate']['aggregate']['count']) + \
- int(count_result['data']['changelog_rule_aggregate']['aggregate']['count'])
- except:
- logger.exception("failed to count changes for import id " + str(import_id))
- changes_in_import = 0
- return changes_in_import
-
-
-def unlock_import(import_state):
- logger = getFwoLogger()
- error_during_import_unlock = 0
- query_variables = {"stopTime": datetime.datetime.now().isoformat(), "importId": import_state.ImportId,
- "success": import_state.ErrorCount == 0, "anyChangesFound": import_state.AnyChangeCount > 0,
- "ruleChangesFound": import_state.RuleChangeCount > 0, "changeNumber": import_state.RuleChangeCount}
-
- unlock_mutation = """
- mutation unlockImport($importId: bigint!, $stopTime: timestamp!, $success: Boolean, $anyChangesFound: Boolean!, $ruleChangesFound: Boolean!, $changeNumber: Int!) {
- update_import_control(where: {control_id: {_eq: $importId}}, _set: {stop_time: $stopTime, successful_import: $success, any_changes_found: $anyChangesFound, rule_changes_found: $ruleChangesFound, security_relevant_changes_counter: $changeNumber}) {
- affected_rows
- }
- }"""
-
- try:
- unlock_result = call(import_state.FwoConfig['fwo_api_base_url'], import_state.Jwt, unlock_mutation,
- query_variables=query_variables, role='importer')
- changes_in_import_control = unlock_result['data']['update_import_control']['affected_rows']
- except:
- logger.exception("failed to unlock import for management id " + str(import_state.MgmDetails.Id))
- error_during_import_unlock = 1
- return error_during_import_unlock
-
-
-# this effectively clears the management!
-def delete_import(import_state):
- logger = getFwoLogger()
- query_variables = {"importId": import_state.ImportId}
-
- delete_import_mutation = """
- mutation deleteImport($importId: bigint!) {
- delete_import_control(where: {control_id: {_eq: $importId}}) { affected_rows }
- }"""
-
- try:
- result = call(import_state.FwoConfig['fwo_api_base_url'], import_state.Jwt, delete_import_mutation,
- query_variables=query_variables, role='importer')
- api_changes = result['data']['delete_import_control']['affected_rows']
- except:
- logger.exception(
- "fwo_api: failed to unlock import for import id " + str(import_state.ImportId))
- return 1 # signaling an error
- if api_changes == 1:
- return 0 # return code 0 is ok
- else:
- return 1
-
-
-def import_json_config(importState, configChunk):
- logger = getFwoLogger()
- import_mutation = """
- mutation import($importId: bigint!, $mgmId: Int!, $config: jsonb!, $start_import_flag: Boolean!, $debug_mode: Boolean!, $chunk_number: Int!) {
- insert_import_config(objects: {start_import_flag: $start_import_flag, import_id: $importId, mgm_id: $mgmId, chunk_number: $chunk_number, config: $config, debug_mode: $debug_mode}) {
- affected_rows
- }
+ if method.upper() == "GET":
+ response = session.get(url, json=params, timeout=int(FWO_API_HTTP_IMPORT_TIMEOUT))
+ elif method.upper() == "POST":
+ response = session.post(url, json=params, timeout=int(FWO_API_HTTP_IMPORT_TIMEOUT))
+ elif method.upper() == "PUT":
+ response = session.put(url, json=params, timeout=int(FWO_API_HTTP_IMPORT_TIMEOUT))
+ elif method.upper() == "DELETE":
+ response = session.delete(url, json=params, timeout=int(FWO_API_HTTP_IMPORT_TIMEOUT))
+ elif method.upper() == "PATCH":
+ response = session.patch(url, json=params, timeout=int(FWO_API_HTTP_IMPORT_TIMEOUT))
+ else:
+ raise FwoImporterError(f"Unsupported HTTP method: {method}")
+
+ # Check for HTTP errors
+ if response.status_code == 401: # noqa: PLR2004
+ raise FwoApiLoginFailedError(f"Authentication failed for endpoint: {endpoint}")
+ if response.status_code == 503: # noqa: PLR2004
+ raise FwoApiServiceUnavailableError("FWO Middleware API HTTP error 503 (middleware died?)")
+ if response.status_code == 502: # noqa: PLR2004
+ raise FwoApiTimeoutError("FWO Middleware API HTTP error 502 (might have reached timeout)")
+
+ response.raise_for_status()
+
+ # Try to parse JSON response
+ try:
+ return response.json()
+ except ValueError:
+ # If response is not JSON, return the text content
+ return response.text
+
+ except requests.exceptions.RequestException as e:
+ FWOLogger.error(f"Middleware API request failed: {e!s}")
+ raise FwoImporterError(f"Middleware API request failed: {e!s}")
+
+ def _handle_request_exception(
+ self, exception: requests.exceptions.RequestException, query_payload: dict[str, Any], headers: dict[str, Any]
+ ) -> None:
+ """
+ Error handling for the standard API call.
+ """
+ FWOLogger.debug(
+ self.show_import_api_call_info(self.fwo_api_url, query_payload, headers, typ="error")
+ + ":\n"
+ + str(traceback.format_exc()),
+ 2,
+ )
+ if hasattr(exception, "response") and exception.response is not None:
+ if exception.response.status_code == 503: # noqa: PLR2004
+ raise FwoApiServiceUnavailableError("FWO API HTTP error 503 (FWO API died?)")
+ if exception.response.status_code == 502: # noqa: PLR2004
+ raise FwoApiTimeoutError(
+ f"FWO API HTTP error 502 (might have reached timeout of {int(FWO_API_HTTP_IMPORT_TIMEOUT) / 60} minutes)"
+ )
+ raise exception
+
+ def _call_chunked(
+ self, session: requests.Session, query: str, query_variables: dict[str, list[Any]] | None = None
+ ) -> dict[str, Any]:
+ """
+ Splits a defined query variable into chunks and posts the queries chunk by chunk.
+ """
+ if query_variables is None:
+ query_variables = {}
+ chunk_number = 1
+ total_processed_elements = 0
+ return_object = {}
+ FWOLogger.info(f"Processing chunked API call ({self.query_info['query_name']})...")
+
+ # Separate chunkable variables.
+
+ chunkable_variables = {
+ variable: list_object
+ for variable, list_object in query_variables.items()
+ if variable in list(self.query_info["chunking_info"]["chunkable_variables"])
}
- """
- try:
- debug_mode = (fwo_globals.debug_level>0)
- configChunk.update({'debug_mode': debug_mode})
- import_result = call(importState.FwoConfig['fwo_api_base_url'], importState.Jwt, import_mutation,
- query_variables=configChunk, role='importer')
- # note: this will not detect errors in triggered stored procedure run
- if 'errors' in import_result:
- logger.exception("fwo_api:import_json_config - error while writing importable config for mgm id " +
- str(importState.MgmDetails.Id) + ": " + str(import_result['errors']))
- return 1 # error
+
+ # Loops until all elements of the the query variable have been processed.
+
+ while total_processed_elements < self.query_info["chunking_info"]["total_elements"]:
+ # Updates query variables to the current chunks data.
+
+ self.query_info["chunking_info"]["adjusted_chunk_size"] = self.query_analyzer.get_adjusted_chunk_size(
+ chunkable_variables
+ )
+
+ FWOLogger.debug(
+ f"Chunk {chunk_number}: Chunk size adjusted\n{self.query_info['chunking_info']['adjusted_chunk_size']}",
+ 9,
+ )
+
+ total_chunk_elements = self._update_query_variables_by_chunk(query_variables, chunkable_variables)
+
+ FWOLogger.debug(f"Chunk {chunk_number}: Query variables updated\n{pformat(query_variables)}", 9)
+
+ # Post query.
+
+ response = self._post_query(session, {"query": query, "variables": query_variables})
+
+ FWOLogger.debug(f"Chunk {chunk_number}: Query posted", 9)
+
+ # Gather and merge returning data.
+
+ return_object = self._handle_chunked_calls_response(return_object, response)
+
+ # Log current state of the process and increment variables.
+
+ total_processed_elements += total_chunk_elements
+ FWOLogger.debug(
+ f"Chunk {chunk_number}: {total_processed_elements}/{self.query_info['chunking_info']['total_elements']} processed elements."
+ )
+ chunk_number += 1
+
+ return return_object
+
+ def _update_query_variables_by_chunk(
+ self, query_variables: dict[str, list[Any]], chunkable_variables: dict[str, list[Any]]
+ ) -> int:
+ chunks: dict[str, Any] = {}
+ total_chunk_elements = 0
+
+ for variable, list_object in chunkable_variables.items():
+ chunks[variable] = list_object[: self.query_info["chunking_info"]["adjusted_chunk_size"]]
+ chunkable_variables[variable] = list_object[self.query_info["chunking_info"]["adjusted_chunk_size"] :]
+
+ for variable, chunk in chunks.items():
+ query_variables[variable] = chunk
+ total_chunk_elements += len(chunk)
+
+ return total_chunk_elements
+
+ def _handle_chunked_calls_response(self, return_object: dict[str, Any], response: dict[str, Any]) -> dict[str, Any]:
+ if return_object == {}:
+ self._try_write_extended_log(
+ message=f"Return object is empty, initializing with response data: {pformat(response)}"
+ )
+
+ return response
+
+ if "errors" in response:
+ error_txt = f"encountered error while handling chunked call: {response['errors']!s}"
+ FWOLogger.error(error_txt)
+ raise FwoImporterError(error_txt)
+
+ for new_return_object_type, new_return_object in response["data"].items():
+ if "data" in return_object:
+ self._handle_chunked_calls_response_with_return_data(
+ return_object, new_return_object_type, new_return_object
+ )
+ elif "affected_rows" not in new_return_object:
+ FWOLogger.warning(f"no data found: {return_object} not found in return_object['data'].")
+ elif new_return_object["affected_rows"] == 0:
+ FWOLogger.warning(f"no data found: {new_return_object} not found in return_object['data'].")
+
+ self._try_write_extended_log(
+ message=f"Returning object after handling chunked calls response: {pformat(return_object)}"
+ )
+
+ return return_object
+
+ def _handle_chunked_calls_response_with_return_data(
+ self, return_object: dict[str, Any], new_return_object_type: str, new_return_object: dict[str, Any] | list[Any]
+ ) -> None:
+ total_affected_rows = 0
+ returning_data: list[dict[str, Any]] = []
+
+ self._try_write_extended_log(
+ message=f"Handling chunked calls response for type '{new_return_object_type}' with data: {pformat(new_return_object)}"
+ )
+
+ if not isinstance(return_object["data"].get(new_return_object_type), dict):
+ return_object["data"][new_return_object_type] = {}
+ return_object["data"][new_return_object_type]["affected_rows"] = 0
+ return_object["data"][new_return_object_type]["returning"] = []
+
+ self._try_write_extended_log(
+ message=f"Initialized return_object['data']['{new_return_object_type}'] as an empty dict: {pformat(return_object['data'][new_return_object_type])}"
+ )
+
+ # If the return object is a list we need to sum the affected rows and accumuluate the returning data, else we can set the values directly.
+
+ if isinstance(new_return_object, list):
+ returning_data = [obj.get("returning", []) for obj in new_return_object if "returning" in obj]
+ total_affected_rows = sum(obj.get("affected_rows", 0) for obj in new_return_object)
else:
- changes_in_import_control = import_result['data']['insert_import_config']['affected_rows']
- except:
- logger.exception("failed to write importable config for mgm id " + str(importState.MgmDetails.Id))
- return 1 # error
-
- if changes_in_import_control==1:
- return 0
- else:
- return 1
-
-
-def update_hit_counter(importState, query_variables):
- logger = getFwoLogger()
- # currently only data for check point firewalls is collected!
-
- if 'config' in query_variables and 'rules' in query_variables['config']:
- queryVariablesLocal = {"mgmId": importState.MgmDetails.Id}
- # prerequesite: rule_uids are unique across a management
- # this is guaranteed for the newer devices
- # older devices like netscreen or FortiGate (via ssh) need to be checked
- # when hits information should be gathered here in the future
-
- found_hits = False
- last_hit_update_mutation = """
- mutation updateRuleLastHit($mgmId:Int!) {
- update_rule_metadata_many(updates: [
+ total_affected_rows = new_return_object.get("affected_rows", 0)
+ returning_data = new_return_object.get("returning", [])
+
+ return_object["data"][new_return_object_type]["affected_rows"] += total_affected_rows
+
+ if "returning" in return_object["data"][new_return_object_type] and len(returning_data) > 0:
+ self._try_write_extended_log(
+ message=f"Extending return_object['data']['{new_return_object_type}']['returning'] with new data: {pformat(returning_data)}"
+ )
+
+ return_object["data"][new_return_object_type]["returning"].extend(returning_data)
+
+ def _post_query(self, session: requests.Session, query_payload: dict[str, Any]) -> dict[str, Any]:
"""
+ Posts the given payload to the api endpoint. Returns the response as json or None if the response object is None.
+ """
+ FWOLogger.debug(
+ self.show_import_api_call_info(
+ self.fwo_api_url, query_payload, session.headers, typ="debug", show_query_info=True
+ ),
+ 9,
+ )
- for rule in query_variables['config']['rules']:
- if 'last_hit' in rule and rule['last_hit'] is not None:
- found_hits = True
- update_expr = '{{ where: {{ device: {{ mgm_id:{{_eq:$mgmId}} }} rule_uid: {{ _eq: "{rule_uid}" }} }}, _set: {{ rule_last_hit: "{last_hit}" }} }}, '.format(rule_uid=rule["rule_uid"], last_hit=rule['last_hit'])
- last_hit_update_mutation += update_expr
+ r = session.post(self.fwo_api_url, data=json.dumps(query_payload), timeout=int(FWO_API_HTTP_IMPORT_TIMEOUT))
- last_hit_update_mutation += " ]) { affected_rows } }"
+ FWOLogger.debug("API response: " + pformat(r.json(), indent=2), 10)
- if found_hits:
- try:
- update_result = call(importState.FwoConfig['fwo_api_base_url'], importState.Jwt, last_hit_update_mutation,
- query_variables=queryVariablesLocal, role='importer')
- if 'errors' in update_result:
- logger.exception("fwo_api:update_hit_counter - error while updating hit counters for mgm id " +
- str(importState.MgmDetails.Id) + ": " + str(update_result['errors']))
- update_counter = len(update_result['data']['update_rule_metadata_many'])
- except:
- logger.exception("failed to update hit counter for mgm id " + str(importState.MgmDetails.Id))
- return 1 # error
-
- return 0
+ r.raise_for_status()
+
+ return r.json()
+
+ def show_api_call_info(self, url: str, query: dict[str, Any], headers: dict[str, Any], typ: str = "debug"):
+ max_query_size_to_display = 1000
+ query_string = json.dumps(query, indent=2)
+ header_string = json.dumps(headers, indent=2)
+ query_size = len(query_string)
+
+ result = "error while sending api_call to url " if typ == "error" else "successful FWO API call to url "
+ result += str(url) + " with payload \n"
+ if query_size < max_query_size_to_display:
+ result += query_string
else:
- if len(query_variables['config']['rules'])>0:
- logger.debug("found rules without hit information for mgm_id " + str(importState.MgmDetails.Id))
- return 1
- else:
- logger.debug("no rules found for mgm_id " + str(importState.MgmDetails.Id))
- return 1
-
-
-def delete_import_object_tables(importState, query_variables):
- logger = getFwoLogger()
- delete_mutation = """
- mutation deleteImportData($importId: bigint!) {
- delete_import_object(where: {control_id: {_eq: $importId}}) {
- affected_rows
- }
- delete_import_rule(where: {control_id: {_eq: $importId}}) {
- affected_rows
- }
- delete_import_service(where: {control_id: {_eq: $importId}}) {
- affected_rows
- }
- delete_import_user(where: {control_id: {_eq: $importId}}) {
- affected_rows
- }
- }
- """
- try:
- delete_result = call(importState.FwoConfig['fwo_api_base_url'], importState.Jwt, delete_mutation,
- query_variables=query_variables, role='importer')
- changes_in_delete_import_tables = \
- int(delete_result['data']['delete_import_object']['affected_rows']) + \
- int(delete_result['data']['delete_import_rule']['affected_rows']) + \
- int(delete_result['data']['delete_import_service']['affected_rows']) + \
- int(delete_result['data']['delete_import_user']['affected_rows'])
- except:
- logger.exception("failed to delete from import_ tables")
- return -1 # indicating error
- return changes_in_delete_import_tables
-
-
-def delete_json_config_in_import_table(importState, query_variables):
- logger = getFwoLogger()
- delete_mutation = """
- mutation delete_import_config($importId: bigint!) {
- delete_import_config(where: {import_id: {_eq: $importId}}) { affected_rows }
- }
- """
- try:
- delete_result = call(importState.FwoConfig['fwo_api_base_url'], importState.Jwt, delete_mutation,
- query_variables=query_variables, role='importer')
- changes_in_delete_config = delete_result['data']['delete_import_config']['affected_rows']
- except:
- logger.exception("failed to delete config without changes")
- return -1 # indicating error
- return changes_in_delete_config
-
-
-def store_full_json_config(importState, query_variables):
- logger = getFwoLogger()
- import_mutation = """
- mutation store_full_config($importId: bigint!, $mgmId: Int!, $config: jsonb!) {
- insert_import_full_config(objects: {import_id: $importId, mgm_id: $mgmId, config: $config}) {
- affected_rows
- }
- }
- """
-
- try:
- import_result = call(importState.FwoConfig['fwo_api_base_url'], importState.Jwt, import_mutation,
- query_variables=query_variables, role='importer')
- changes_in_import_full_config = import_result['data']['insert_import_full_config']['affected_rows']
- except:
- logger.exception("failed to write full config for mgm id " + str(importState.MgmDetails.Id))
- return 2 # indicating 1 error because we are expecting exactly one change
- return changes_in_import_full_config-1
-
-
-# def delete_full_json_config(importState, query_variables):
-# logger = getFwoLogger()
-# delete_mutation = """
-# mutation delete_import_full_config($importId: bigint!) {
-# delete_import_full_config(where: {import_id: {_eq: $importId}}) {
-# affected_rows
-# }
-# }
-# """
-
-# try:
-# delete_result = call(importState.FwoConfig['fwo_api_base_url'], importState.Jwt, delete_mutation,
-# query_variables=query_variables, role='importer')
-# changes_in_delete_full_config = delete_result['data']['delete_import_full_config']['affected_rows']
-# except:
-# logger.exception("failed to delete full config ")
-# return 2 # indicating 1 error
-# return changes_in_delete_full_config-1
-
-
-def get_error_string_from_imp_control(importState, query_variables):
- error_query = "query getErrors($importId:bigint) { import_control(where:{control_id:{_eq:$importId}}) { import_errors } }"
- return call(importState.FwoConfig['fwo_api_base_url'], importState.Jwt, error_query, query_variables=query_variables, role='importer')['data']['import_control']
-
-
-def create_data_issue(fwo_api_base_url, jwt, import_id=None, obj_name=None, mgm_id=None, dev_id=None, severity=1, role='importer',
- rule_uid=None, object_type=None, description=None, source='import'):
- logger = getFwoLogger()
- if obj_name=='all' or obj_name=='Original':
- return True # ignore resolve errors for enriched objects that are not in the native config
- else:
- create_data_issue_mutation = """
- mutation createDataIssue($source: String!, $severity: Int!, $importId: bigint, $objectName: String,
- $objectType:String, $ruleUid: String, $description: String,
- $mgmId: Int, $devId: Int) {
- insert_log_data_issue(objects: {source: $source, severity: $severity, import_id: $importId,
- object_name: $objectName, rule_uid: $ruleUid,
- object_type:$objectType, description: $description, issue_dev_id: $devId, issue_mgm_id: $mgmId }) {
- affected_rows
- }
- }
+ result += (
+ str(query)[: round(max_query_size_to_display / 2)]
+ + "\n ... [snip] ... \n"
+ + query_string[query_size - round(max_query_size_to_display / 2) :]
+ + " (total query size="
+ + str(query_size)
+ + " bytes)"
+ )
+ result += "\n and headers: \n" + header_string
+ return result
+
+ def _try_show_api_call_info(self, full_query: dict[str, Any], request_headers: dict[str, Any]) -> None:
+ """
+ Tries to show the API call info if the debug level is high enough.
+ """
+ FWOLogger.debug(
+ self.show_import_api_call_info(
+ self.fwo_api_url, full_query, request_headers, typ="debug", show_query_info=True
+ ),
+ 9,
+ )
+
+ def _try_write_extended_log(self, message: str) -> None:
"""
+ Writes an extended log message if the debug level is high enough.
+ """
+ FWOLogger.debug(message, 10)
+
+ def show_import_api_call_info(
+ self,
+ api_url: str,
+ query: dict[str, Any],
+ headers: dict[str, Any] | MutableMapping[str, str | bytes],
+ typ: str = "debug",
+ show_query_info: bool = False,
+ ):
+ max_query_size_to_display = 1000
+ query_string = json.dumps(query, indent=2)
+ header_string = json.dumps(dict(headers), indent=2)
+ api_url = json.dumps(api_url, indent=2)
+ query_size = len(query_string)
+ result = "error while sending api_call to url " if typ == "error" else "successful FWO API call to url "
+ result += str(self.fwo_api_url) + " with payload \n"
+ if query_size < max_query_size_to_display:
+ result += query_string
+ else:
+ result += (
+ str(query)[: round(max_query_size_to_display / 2)]
+ + "\n ... [snip] ... \n"
+ + query_string[query_size - round(max_query_size_to_display / 2) :]
+ + " (total query size="
+ + str(query_size)
+ + " bytes)"
+ )
+ result += "\n and headers: \n" + header_string + ", api_url: " + api_url
+
+ if show_query_info and self.query_info:
+ result += "\nQuery Info: \n" + pformat(self.query_info)
+
+ return result
+
+ @classmethod
+ def get_graphql_code(cls, file_list: list[str]) -> str:
+ code = ""
+
+ for file in file_list:
+ try:
+ # read graphql code from file
+ printable_chars = set(string.printable)
+ with open(file, encoding="utf-8", errors="ignore") as f:
+ code += "".join(filter(printable_chars.__contains__, f.read())) + " "
+ except FileNotFoundError:
+ FWOLogger.error("fwo_api: file not found: " + file)
+ raise
- query_variables = {"source": source, "severity": severity }
-
- if dev_id is not None:
- query_variables.update({"devId": dev_id})
- if mgm_id is not None:
- query_variables.update({"mgmId": mgm_id})
- if obj_name is not None:
- query_variables.update({"objectName": obj_name})
- if object_type is not None:
- query_variables.update({"objectType": object_type})
- # setting import_id leads to error: 'Foreign key violation. insert or update on table "log_data_issue"
- # violates foreign key constraint "log_data_issue_import_control_control_id_fkey"
- # if import_id is not None:
- # query_variables.update({"importId": import_id})
- if rule_uid is not None:
- query_variables.update({"ruleUid": rule_uid})
- if description is not None:
- query_variables.update({"description": description})
-
- # write data issue to alert.log file as well
- # if severity>0:
- # writeAlertToLogFile(query_variables)
-
- try:
- import_result = call(fwo_api_base_url, jwt, create_data_issue_mutation, query_variables=query_variables, role=role)
- changes = import_result['data']['insert_log_data_issue']['affected_rows']
- except:
- logger.error("failed to create log_data_issue: " + json.dumps(query_variables))
- return False
- return changes==1
-
-
-def setAlert(fwo_api_base_url, jwt, import_id=None, title=None, mgm_id=None, dev_id=None, severity=1, role='importer',
- jsonData=None, description=None, source='import', user_id=None, refAlert=None, alertCode=None, mgm_details = None):
-
- logger = getFwoLogger()
-
- addAlert_mutation = """
- mutation addAlert(
- $source: String!
- $userId: Int
- $title: String
- $description: String
- $mgmId: Int
- $devId: Int
- $jsonData: json
- $refAlert: bigint
- $alertCode: Int
- )
- {
- insert_alert(
- objects: {
- source: $source
- user_id: $userId
- title: $title
- description: $description
- alert_mgm_id: $mgmId
- alert_dev_id: $devId
- json_data: $jsonData
- ref_alert_id: $refAlert
- alert_code: $alertCode
- }
- )
- {
- returning { newIdLong: alert_id }
- }
- }
- """
- getAlert_query = """
- query getAlerts($mgmId: Int!, $alertCode: Int!, $currentAlertId: bigint!) {
- alert(where: {
- alert_mgm_id: {_eq: $mgmId}, alert_code: {_eq: $alertCode}
- ack_timestamp: {_is_null: true}
- alert_id: {_neq: $currentAlertId}})
- {
- alert_id
- }
- }
- """
- ackAlert_mutation = """
- mutation ackAlert($userId: Int, $alertId: bigint, $ackTimeStamp: timestamp) {
- update_alert(where: {alert_id: {_eq: $alertId}}, _set: {ack_by: $userId, ack_timestamp: $ackTimeStamp}) {
- affected_rows
- }
- }
- """
-
- query_variables = {"source": source }
-
- if dev_id is not None:
- query_variables.update({"devId": dev_id})
- if user_id is not None:
- query_variables.update({"userId": user_id})
- if mgm_id is not None:
- query_variables.update({"mgmId": mgm_id})
- if refAlert is not None:
- query_variables.update({"refAlert": refAlert})
- if title is not None:
- query_variables.update({"title": title})
- if description is not None:
- query_variables.update({"description": description})
- if alertCode is not None:
- query_variables.update({"alertCode": alertCode})
-
- if jsonData is None:
- jsonData = {}
- if severity != None:
- jsonData.update({"severity": severity})
- if import_id != None:
- jsonData.update({"import_id": import_id})
- if mgm_details != None:
- jsonData.update({"mgm_name": mgm_details.Name})
- query_variables.update({"jsonData": json.dumps(jsonData)})
-
- # write data issue to alert.log file as well
- if severity>0:
- writeAlertToLogFile(query_variables)
-
- try:
- import_result = call(fwo_api_base_url, jwt, addAlert_mutation, query_variables=query_variables, role=role)
- newAlertId = import_result['data']['insert_alert']['returning'][0]['newIdLong']
- if alertCode is not None and mgm_id is not None:
- # Acknowledge older alert for same problem on same management
- query_variables = { "mgmId": mgm_id, "alertCode": alertCode, "currentAlertId": newAlertId }
- existingUnacknowledgedAlerts = call(fwo_api_base_url, jwt, getAlert_query, query_variables=query_variables, role=role)
- if 'data' in existingUnacknowledgedAlerts and 'alert' in existingUnacknowledgedAlerts['data']:
- for alert in existingUnacknowledgedAlerts['data']['alert']:
- if 'alert_id' in alert:
- now = datetime.datetime.now().isoformat()
- query_variables = { "userId": 0, "alertId": alert['alert_id'], "ackTimeStamp": now }
- updateResult = call(fwo_api_base_url, jwt, ackAlert_mutation, query_variables=query_variables, role=role)
- except:
- logger.error("failed to create alert entry: " + json.dumps(query_variables))
- return False
- return True
-
-
-def complete_import(importState):
- logger = getFwoLogger()
-
- success = (importState.ErrorCount==0)
- try:
- log_import_attempt(importState.FwoConfig['fwo_api_base_url'], importState.Jwt, importState.MgmDetails.Id, successful=success)
- except:
- logger.error('error while trying to log import attempt')
-
- try: # CLEANUP: delete configs of imports (without changes) (if no error occured)
- if delete_json_config_in_import_table(importState, {"importId": importState.ImportId})<0:
- importState.ErrorCount += 1
- except:
- logger.error("import_management - unspecified error cleaning up import_config: " + str(traceback.format_exc()))
-
- try: # CLEANUP: delete data of this import from import_object/rule/service/user tables
- if delete_import_object_tables(importState, {"importId": importState.ImportId})<0:
- importState.ErrorCount += 1
- except:
- logger.error("import_management - unspecified error cleaning up import_ object tables: " + str(traceback.format_exc()))
-
- try: # finalize import by unlocking it
- importState.ErrorCount += unlock_import(importState)
- except:
- logger.error("import_management - unspecified error while unlocking import: " + str(traceback.format_exc()))
-
- import_result = "import_management: import no. " + str(importState.ImportId) + \
- " for management " + importState.MgmDetails.Name + ' (id=' + str(importState.MgmDetails.Id) + ")" + \
- str(" threw errors," if importState.ErrorCount else " successful,") + \
- " total change count: " + str(importState.AnyChangeCount) + \
- ", rule change count: " + str(importState.RuleChangeCount) + \
- ", duration: " + str(int(time.time()) - importState.StartTime) + "s"
- import_result += ", ERRORS: " + importState.ErrorString if len(importState.ErrorString) > 0 else ""
-
- if importState.ErrorCount>0:
- create_data_issue(importState.FwoConfig['fwo_api_base_url'], importState.Jwt, import_id=importState.ImportId, severity=1, description=importState.ErrorString)
- setAlert(importState.FwoConfig['fwo_api_base_url'], importState.Jwt, import_id=importState.ImportId, title="import error", mgm_id=importState.MgmDetails.Id, severity=2, role='importer', \
- description=importState.ErrorString, source='import', alertCode=14, mgm_details=importState.MgmDetails)
-
- logger.info(import_result)
-
- return importState.ErrorCount
+ return code.replace("\n", " ").replace("\r", " ")
+
+ @staticmethod
+ def _read_clean_text_from_file(file_path: str) -> str:
+ printable_chars = set(string.printable)
+ with open(file_path, encoding="utf-8", errors="ignore") as f:
+ return "".join(filter(printable_chars.__contains__, f.read()))
diff --git a/roles/importer/files/importer/fwo_api_call.py b/roles/importer/files/importer/fwo_api_call.py
new file mode 100644
index 0000000000..3b9b42477c
--- /dev/null
+++ b/roles/importer/files/importer/fwo_api_call.py
@@ -0,0 +1,410 @@
+# library for all FWORCH API calls in importer module
+import datetime
+import json
+import time
+import traceback
+from typing import TYPE_CHECKING, Any
+
+import fwo_const
+from fwo_api import FwoApi
+from fwo_exceptions import FwoApiFailedLockImportError
+from fwo_log import FWOLogger
+from model_controllers.management_controller import ManagementController
+from models.fwconfig_normalized import FwConfigNormalized
+from query_analyzer import QueryAnalyzer
+
+if TYPE_CHECKING:
+ from model_controllers.import_state_controller import ImportStateController
+ from models.import_state import ImportState
+
+# NOTE: we cannot import ImportState(Controller) here due to circular refs
+
+
+class FwoApiCall(FwoApi):
+ def __init__(self, api: FwoApi):
+ self.fwo_api_url = api.fwo_api_url
+ self.fwo_jwt = api.fwo_jwt
+ self.query_info = {}
+ self.query_analyzer = QueryAnalyzer()
+
+ def get_mgm_ids(self, query_variables: dict[str, list[Any]] | None = None) -> list[int]:
+ # from 9.0 do not import sub-managers separately
+ if query_variables is None:
+ query_variables = {}
+ mgm_query = FwoApi.get_graphql_code([fwo_const.GRAPHQL_QUERY_PATH + "device/getManagementWithSubs.graphql"])
+ result = self.call(mgm_query, query_variables=query_variables)
+ if "data" in result and "management" in result["data"]:
+ return [mgm["id"] for mgm in result["data"]["management"]]
+ return []
+
+ def get_config_value(self, key: str = "limit") -> str | None:
+ query_variables: dict[str, str] = {"key": key}
+ cfg_query = FwoApi.get_graphql_code([fwo_const.GRAPHQL_QUERY_PATH + "config/getConfigValue.graphql"])
+
+ try:
+ result = self.call(cfg_query, query_variables=query_variables)
+ except Exception:
+ FWOLogger.error("fwo_api: failed to get config value for key " + key)
+ return None
+
+ if "data" in result and "config" in result["data"]:
+ first_result = result["data"]["config"][0]
+ if "config_value" in first_result:
+ return first_result["config_value"]
+ return None
+
+ def get_config_values(self, key_filter: str = "limit") -> dict[str, str] | None:
+ query_variables: dict[str, str] = {"keyFilter": key_filter + "%"}
+ config_query = FwoApi.get_graphql_code(
+ [fwo_const.GRAPHQL_QUERY_PATH + "config/getConfigValuesByKeyFilter.graphql"]
+ )
+
+ try:
+ result = self.call(config_query, query_variables=query_variables)
+ except Exception:
+ FWOLogger.error("fwo_api: failed to get config values for key filter " + key_filter)
+ return None
+
+ if "data" in result and "config" in result["data"]:
+ result_array = result["data"]["config"]
+ return {v["config_key"]: v["config_value"] for _, v in enumerate(result_array)}
+ return None
+
+ # this mgm field is used by mw dailycheck scheduler
+ def log_import_attempt(self, mgm_id: int, successful: bool):
+ now = datetime.datetime.now().isoformat()
+ query_variables: dict[str, Any] = {"mgmId": mgm_id, "timeStamp": now, "success": successful}
+ mgm_mutation = FwoApi.get_graphql_code(
+ [fwo_const.GRAPHQL_QUERY_PATH + "import/updateManagementLastImportAttempt.graphql"]
+ )
+ return self.call(mgm_mutation, query_variables=query_variables)
+
+ def set_import_lock(self, mgm_details: ManagementController, is_full_import: int, is_initial_import: int) -> int:
+ import_id = -1
+ mgm_id = mgm_details.mgm_id
+ try: # set import lock
+ lock_mutation = FwoApi.get_graphql_code([fwo_const.GRAPHQL_QUERY_PATH + "import/addImport.graphql"])
+ lock_result = self.call(
+ lock_mutation,
+ query_variables={"mgmId": mgm_id, "isFullImport": is_full_import, "isInitialImport": is_initial_import},
+ )
+ if lock_result["data"]["insert_import_control"]["returning"][0]["control_id"]:
+ import_id = lock_result["data"]["insert_import_control"]["returning"][0]["control_id"]
+ return import_id
+ except Exception:
+ FWOLogger.error("import_management - failed to get import lock for management id " + str(mgm_id))
+ if import_id == -1:
+ self.create_data_issue(
+ mgm_id=mgm_id, severity=1, description="failed to get import lock for management id " + str(mgm_id)
+ )
+ self.set_alert(
+ import_id=import_id,
+ title="import error",
+ mgm_id=mgm_id,
+ severity=1,
+ description="fwo_api: failed to get import lock",
+ source="import",
+ alert_code=15,
+ mgm_details=mgm_details,
+ )
+ raise FwoApiFailedLockImportError(
+ "fwo_api: failed to get import lock for management id " + str(mgm_id)
+ ) from None
+ return import_id
+
+ def count_rule_changes_per_import(self, import_id: int):
+ change_count_query = FwoApi.get_graphql_code(
+ [fwo_const.GRAPHQL_QUERY_PATH + "import/getRuleChangesPerImport.graphql"]
+ )
+ try:
+ count_result = self.call(change_count_query, query_variables={"importId": import_id})
+ rule_changes_in_import = int(count_result["data"]["changelog_rule_aggregate"]["aggregate"]["count"])
+ except Exception as e:
+ FWOLogger.exception(f"failed to count changes for import id {import_id!s}: {e!s}")
+ rule_changes_in_import = 0
+ return rule_changes_in_import
+
+ def count_any_changes_per_import(self, import_id: int):
+ change_count_query = FwoApi.get_graphql_code(
+ [fwo_const.GRAPHQL_QUERY_PATH + "import/getChangesPerImport.graphql"]
+ )
+ try:
+ count_result = self.call(change_count_query, query_variables={"importId": import_id})
+ changes_in_import = (
+ int(count_result["data"]["changelog_object_aggregate"]["aggregate"]["count"])
+ + int(count_result["data"]["changelog_service_aggregate"]["aggregate"]["count"])
+ + int(count_result["data"]["changelog_user_aggregate"]["aggregate"]["count"])
+ + int(count_result["data"]["changelog_rule_aggregate"]["aggregate"]["count"])
+ )
+ except Exception as e:
+ FWOLogger.exception(f"failed to count changes for import id {import_id!s}: {e!s}")
+ changes_in_import = 0
+ return changes_in_import
+
+ def unlock_import(self, import_state: "ImportState", success: bool):
+ import_id = import_state.import_id
+ mgm_id = import_state.mgm_details.mgm_id
+ import_stats = import_state.stats
+
+ try:
+ query_variables: dict[str, Any] = {
+ "stopTime": datetime.datetime.now().isoformat(),
+ "importId": import_id,
+ "success": success,
+ "anyChangesFound": import_stats.get_total_change_number() > 0,
+ "ruleChangesFound": import_stats.get_rule_change_number() > 0,
+ "changeNumber": import_stats.get_rule_change_number(),
+ }
+
+ unlock_mutation = FwoApi.get_graphql_code(
+ [fwo_const.GRAPHQL_QUERY_PATH + "import/updateImportStopTime.graphql"]
+ )
+
+ unlock_result = self.call(unlock_mutation, query_variables=query_variables)
+ if "errors" in unlock_result:
+ raise FwoApiFailedLockImportError(unlock_result["errors"])
+ _ = unlock_result["data"]["update_import_control"]["affected_rows"]
+ except Exception as e:
+ FWOLogger.exception("failed to unlock import for management id " + str(mgm_id) + ": " + str(e))
+
+ # currently temporarily only working with single chunk
+ def import_json_config(self, import_state: "ImportState", config: FwConfigNormalized, start_import: bool):
+ import_mutation = FwoApi.get_graphql_code([fwo_const.GRAPHQL_QUERY_PATH + "import/addImportConfig.graphql"])
+
+ try:
+ query_vars: dict[str, Any] = {
+ "debug_mode": FWOLogger.is_debug_level(1),
+ "mgmId": import_state.mgm_details.mgm_id,
+ "importId": import_state.import_id,
+ "config": config,
+ "start_import_flag": start_import,
+ }
+ import_result = self.call(import_mutation, query_variables=query_vars)
+ # note: this will not detect errors in triggered stored procedure run
+ if "errors" in import_result:
+ FWOLogger.exception(
+ "fwo_api:import_json_config - error while writing importable config for mgm id "
+ + str(import_state.mgm_details.mgm_id)
+ + ": "
+ + str(import_result["errors"])
+ )
+ else:
+ _ = import_result["data"]["insert_import_config"]["affected_rows"]
+ except Exception:
+ FWOLogger.exception(
+ f"failed to write normalized config for mgm id {import_state.mgm_details.mgm_id!s}: {traceback.format_exc()!s}"
+ )
+
+ def delete_json_config_in_import_table(self, query_variables: dict[str, Any]):
+ delete_mutation = FwoApi.get_graphql_code([fwo_const.GRAPHQL_QUERY_PATH + "import/deleteImportConfig.graphql"])
+ try:
+ delete_result = self.call(delete_mutation, query_variables=query_variables)
+ _ = delete_result["data"]["delete_import_config"]["affected_rows"]
+ except Exception:
+ FWOLogger.exception("failed to delete config without changes")
+
+ def get_error_string_from_imp_control(
+ self, _: "ImportStateController", query_variables: dict[str, Any]
+ ) -> list[dict[str, Any]]: # TYPING: confirm return type
+ error_query = (
+ "query getErrors($importId:bigint) { import_control(where:{control_id:{_eq:$importId}}) { import_errors } }"
+ )
+ return self.call(error_query, query_variables=query_variables)["data"]["import_control"]
+
+ def create_data_issue(
+ self,
+ obj_name: str | None = None,
+ mgm_id: int | None = None,
+ dev_id: int | None = None,
+ severity: int = 1,
+ rule_uid: str | None = None,
+ object_type: str | None = None,
+ description: str | None = None,
+ source: str = "import",
+ ) -> None:
+ if obj_name in {"all", "Original"}:
+ return # ignore resolve errors for enriched objects that are not in the native config
+
+ create_data_issue_mutation = FwoApi.get_graphql_code(
+ [fwo_const.GRAPHQL_QUERY_PATH + "monitor/addLogEntry.graphql"]
+ )
+
+ query_variables: dict[str, Any] = {"source": source, "severity": severity}
+
+ if dev_id is not None:
+ query_variables.update({"devId": dev_id})
+ if mgm_id is not None:
+ query_variables.update({"mgmId": mgm_id})
+ if obj_name is not None:
+ query_variables.update({"objectName": obj_name})
+ if object_type is not None:
+ query_variables.update({"objectType": object_type})
+ if rule_uid is not None:
+ query_variables.update({"ruleUid": rule_uid})
+ if description is not None:
+ query_variables.update({"description": description})
+
+ try:
+ result = self.call(create_data_issue_mutation, query_variables=query_variables)
+ changes = result["data"]["insert_log_data_issue"]["returning"]
+ if len(changes) != 1:
+ FWOLogger.warning(f"create_data_issue: unexpected result creating data issue: {json.dumps(result)}")
+ except Exception as e:
+ FWOLogger.error(f"failed to create log_data_issue: {json.dumps(query_variables)}: {e!s}")
+
+ def set_alert(
+ self,
+ import_id: int | None = None,
+ title: str | None = None,
+ mgm_id: int | None = None,
+ dev_id: int | None = None,
+ severity: int | None = 1,
+ json_data: dict[str, Any] | None = None,
+ description: str | None = None,
+ source: str = "import",
+ user_id: int | None = None,
+ ref_alert: str | None = None,
+ alert_code: int | None = None,
+ mgm_details: ManagementController | None = None,
+ ):
+ add_alert_mutation = FwoApi.get_graphql_code([fwo_const.GRAPHQL_QUERY_PATH + "monitor/addAlert.graphql"])
+ get_alert_query = FwoApi.get_graphql_code(
+ [fwo_const.GRAPHQL_QUERY_PATH + "monitor/getAlertByManagement.graphql"]
+ )
+ ack_alert_mutation = FwoApi.get_graphql_code([fwo_const.GRAPHQL_QUERY_PATH + "monitor/updateAlert.graphql"])
+
+ query_variables = {"source": source}
+
+ self._set_alert_build_query_vars(
+ query_variables, dev_id, user_id, mgm_id, ref_alert, title, description, alert_code
+ )
+
+ if json_data is None:
+ json_data = {}
+ if severity is not None:
+ json_data.update({"severity": severity})
+ if import_id is not None:
+ json_data.update({"import_id": import_id})
+ if mgm_details is not None:
+ json_data.update({"mgm_name": mgm_details.name})
+ query_variables.update({"jsonData": json.dumps(json_data)})
+
+ try:
+ import_result = self.call(add_alert_mutation, query_variables=query_variables)
+ new_alert_id = import_result["data"]["insert_alert"]["returning"][0]["newIdLong"]
+ if (
+ alert_code is None or mgm_id is None
+ ): # WWS-CHECK: changed: "mgm_id is not None" -> "mgm_id is None" # TODO: review
+ return
+
+ # Acknowledge older alert for same problem on same management
+ query_variables: dict[str, Any] = {"mgmId": mgm_id, "alertCode": alert_code, "currentAlertId": new_alert_id}
+ existing_unacknowledged_alerts = self.call(get_alert_query, query_variables=query_variables)
+ if "data" not in existing_unacknowledged_alerts or "alert" not in existing_unacknowledged_alerts["data"]:
+ return
+
+ for alert in existing_unacknowledged_alerts["data"]["alert"]:
+ if "alert_id" in alert:
+ now = datetime.datetime.now().isoformat()
+ query_variables = {"userId": 0, "alertId": alert["alert_id"], "ackTimeStamp": now}
+ _ = self.call(ack_alert_mutation, query_variables=query_variables)
+ except Exception as e:
+ FWOLogger.error(f"failed to create alert entry: {json.dumps(query_variables)}; exception: {e!s}")
+ raise
+
+ def _set_alert_build_query_vars(
+ self,
+ query_variables: dict[str, Any],
+ dev_id: int | None,
+ user_id: int | None,
+ mgm_id: int | None,
+ ref_alert: str | None,
+ title: str | None,
+ description: str | None,
+ alert_code: int | None,
+ ):
+ if dev_id is not None:
+ query_variables.update({"devId": dev_id})
+ if user_id is not None:
+ query_variables.update({"userId": user_id})
+ if mgm_id is not None:
+ query_variables.update({"mgmId": mgm_id})
+ if ref_alert is not None:
+ query_variables.update({"refAlert": ref_alert})
+ if title is not None:
+ query_variables.update({"title": title})
+ if description is not None:
+ query_variables.update({"description": description})
+ if alert_code is not None:
+ query_variables.update({"alertCode": alert_code})
+
+ def complete_import(self, import_state: "ImportState", exception: BaseException | None = None):
+ if not import_state.responsible_for_importing:
+ return
+
+ try:
+ self.log_import_attempt(import_state.mgm_details.mgm_id, successful=exception is None)
+ except Exception:
+ FWOLogger.error("error while trying to log import attempt")
+
+ self.unlock_import(import_state, success=exception is None)
+
+ exception_message: str | None = None
+ if exception is not None and hasattr(exception, "message"):
+ exception_message = getattr(exception, "message", None)
+ else:
+ exception_message = str(exception)
+
+ import_result = (
+ "import_management: import no. "
+ + str(import_state.import_id)
+ + " for management "
+ + import_state.mgm_details.name
+ + " (id="
+ + str(import_state.mgm_details.mgm_id)
+ + ")"
+ + str(" threw errors," if exception is not None else " successful,")
+ + " total change count: "
+ + str(import_state.stats.get_total_change_number())
+ + ", rule change count: "
+ + str(import_state.stats.get_rule_change_number())
+ + ", duration: "
+ + str(int(time.time()) - import_state.start_time)
+ + "s"
+ )
+ import_result += ", ERRORS: " + exception_message if exception_message is not None else ""
+
+ if import_state.stats.get_change_details() != {} and FWOLogger.is_debug_level(4) and exception is None:
+ import_result += ", change details: " + str(import_state.stats.get_change_details())
+
+ if exception is not None:
+ self.create_data_issue(severity=1, description=exception_message)
+ self.set_alert(
+ import_id=import_state.import_id,
+ title="import error",
+ mgm_id=import_state.mgm_details.mgm_id,
+ severity=2,
+ description=exception_message,
+ source="import",
+ alert_code=14,
+ mgm_details=import_state.mgm_details,
+ )
+
+ FWOLogger.info(import_result.encode().decode("unicode_escape"))
+
+ def get_last_complete_import(self, query_vars: dict[str, Any]) -> tuple[int, str]:
+ mgm_query = FwoApi.get_graphql_code([fwo_const.GRAPHQL_QUERY_PATH + "import/getLastCompleteImport.graphql"])
+ last_full_import_date: str = ""
+ last_full_import_id: int = 0
+ try:
+ past_details = self.call(mgm_query, query_variables=query_vars)
+ if len(past_details["data"]["import_control"]) > 0:
+ last_full_import_date = past_details["data"]["import_control"][0]["start_time"]
+ last_full_import_id = past_details["data"]["import_control"][0]["control_id"]
+ except Exception as _:
+ FWOLogger.error(
+ f"error while getting past import details for mgm {query_vars!s}: {traceback.format_exc()!s}"
+ )
+ raise
+
+ return last_full_import_id, last_full_import_date
diff --git a/roles/importer/files/importer/fwo_base.py b/roles/importer/files/importer/fwo_base.py
index 05c66b216f..a353391b2f 100644
--- a/roles/importer/files/importer/fwo_base.py
+++ b/roles/importer/files/importer/fwo_base.py
@@ -1,170 +1,324 @@
+import hashlib
+import ipaddress
import json
-import jsonpickle
-from fwo_data_networking import InterfaceSerializable, RouteSerializable
-import fwo_globals
-from fwo_const import max_objs_per_chunk, csv_delimiter, apostrophe, line_delimiter
-from fwo_log import getFwoLogger, getFwoAlertLogger
-from copy import deepcopy
+import re
+import time
+import traceback
+from enum import Enum
+from typing import TYPE_CHECKING, Any
+import fwo_config
+import fwo_const
+from fwo_const import IMPORT_TMP_PATH
+from fwo_enums import ConfFormat, ConfigAction
+from model_controllers.fwconfig_import_ruleorder import RuleOrderService
-def split_list(list_in, max_list_length):
- if len(list_in)max_number_of_chunks:
- max_number_of_chunks = len(split_list_tmp)
- else:
- conf_split_dict_of_lists.update({obj_list_name: []})
- conf_split = []
- current_chunk = 0
- while current_chunk0:
- config_split_with_metadata[len(config_split_with_metadata)-1]["start_import_flag"] = True
- else:
- logger.warning('got empty config (no chunks at all)')
- if fwo_globals.debug_level>0 and len(config_split_with_metadata)>0:
- config_split_with_metadata[len(config_split_with_metadata)-1]["debug_mode"] = True
- return config_split_with_metadata
+if TYPE_CHECKING:
+ from model_controllers.import_state_controller import ImportStateController
+ from models.import_state import ImportState
+from fwo_log import FWOLogger
+from services.enums import Lifetime, Services
+from services.global_state import GlobalState
+from services.group_flats_mapper import GroupFlatsMapper
+from services.service_provider import ServiceProvider
+from services.uid2id_mapper import Uid2IdMapper
-def csv_add_field(content, no_csv_delimiter=False):
- if (content == None or content == '') and not no_csv_delimiter: # do not add apostrophes for empty fields
- field_result = csv_delimiter
- else:
- # add apostrophes at beginning and end and remove any ocurrence of them within the string
- if (isinstance(content, str)):
- escaped_field = content.replace(apostrophe,"")
- field_result = apostrophe + escaped_field + apostrophe
- else: # leave non-string values as is
- field_result = str(content)
- if not no_csv_delimiter:
- field_result += csv_delimiter
- return field_result
-
-
-def sanitize(content):
- if content == None:
+def sanitize(content: Any, lower: bool = False) -> None | str:
+ if content is None:
return None
result = str(content)
- result = result.replace(apostrophe,"") # remove possibly contained apostrophe
- result = result.replace(line_delimiter," ") # replace possibly contained CR with space
+ result = result.replace('"', "") # remove possibly contained apostrophe
+ result = result.replace("\n", " ") # replace possibly contained CR with space
+ if lower:
+ return result.lower()
return result
-def extend_string_list(list_string, src_dict, key, delimiter, jwt=None, import_id=None):
+def extend_string_list(list_string: str | None, src_dict: dict[str, list[str]], key: str, delimiter: str) -> str:
if list_string is None:
- list_string = ''
- if list_string == '':
- if key in src_dict:
- result = delimiter.join(src_dict[key])
- else:
- result = ''
-# fwo_api.create_data_issue(fwo_api_base_url, jwt, import_id, key)
+ list_string = ""
+ if list_string == "":
+ result = delimiter.join(src_dict[key]) if key in src_dict else ""
+ elif key in src_dict:
+ old_list = list_string.split(delimiter)
+ combined_list = old_list + src_dict[key]
+ result = delimiter.join(combined_list)
else:
- if key in src_dict:
- old_list = list_string.split(delimiter)
- combined_list = old_list + src_dict[key]
- result = delimiter.join(combined_list)
- else:
- result = list_string
-# fwo_api.create_data_issue(fwo_api_base_url, jwt, import_id, key)
+ result = list_string
return result
-def jsonToLogFormat(jsonData):
- if type(jsonData) is dict:
- jsonString = json.dumps(jsonData)
- elif isinstance(jsonData, str):
- jsonString = jsonData
- else:
- jsonString = str(jsonData)
-
- if jsonString[0] == '{' and jsonString[-1] == '}':
- jsonString = jsonString[1:len(jsonString)-1]
- return jsonString
-
-
-def writeAlertToLogFile(jsonData):
- logger = getFwoAlertLogger()
- jsonDataCopy = deepcopy(jsonData) # make sure the original alert is not changed
- if type(jsonDataCopy) is dict and 'jsonData' in jsonDataCopy:
- subDict = json.loads(jsonDataCopy.pop('jsonData'))
- jsonDataCopy.update(subDict)
- alertText = "FWORCHAlert - " + jsonToLogFormat(jsonDataCopy)
- logger.info(alertText)
-
-
-def set_ssl_verification(ssl_verification_mode):
- logger = getFwoLogger()
- if ssl_verification_mode == '' or ssl_verification_mode == 'off':
- ssl_verification = False
- if fwo_globals.debug_level>5:
- logger.debug("ssl_verification: False")
- else:
- ssl_verification = ssl_verification_mode
- if fwo_globals.debug_level>5:
- logger.debug("ssl_verification: [ca]certfile=" + ssl_verification)
- return ssl_verification
+def string_is_uri(s: str) -> re.Match[str] | None: # TODO: should return bool?
+ return re.match("http://.+", s) or re.match("https://.+", s) or re.match("file://.+", s)
+
+
+def deserialize_class_to_dict_rec(
+ obj: Any, seen: set[int] | None = None
+) -> dict[str, Any] | list[Any] | Any | str | int | float | bool | None: # TYPING: using model is forbidden?
+ if seen is None:
+ seen = set()
+
+ # Handle simple immutable types directly (int, float, bool, str) and None
+ if obj is None or isinstance(obj, (int, float, bool, str, ConfFormat, ConfigAction)):
+ return obj
+
+ # Check for circular references
+ if id(obj) in seen:
+ return f""
+
+ seen.add(id(obj))
+
+ if isinstance(obj, list):
+ # If the object is a list, deserialize each item
+ return [deserialize_class_to_dict_rec(item, seen) for item in obj] # type: ignore # noqa: PGH003
+ if isinstance(obj, dict):
+ # If the object is a dictionary, deserialize each key-value pair
+ return {key: deserialize_class_to_dict_rec(value, seen) for key, value in obj.items()} # type: ignore # noqa: PGH003
+ if isinstance(obj, Enum):
+ # If the object is an Enum, convert it to its value
+ return obj.value
+ if hasattr(obj, "__dict__"):
+ # If the object is a class instance, deserialize its attributes
+ return {
+ key: deserialize_class_to_dict_rec(value, seen)
+ for key, value in obj.__dict__.items()
+ if not callable(value) and not key.startswith("__")
+ }
+ # For other types, return the value as is
+ return obj
+
+
+def cidr_to_range(ip: str | None) -> list[str] | list[None]: # TODO: I have no idea what other than string it could be
+ if isinstance(ip, str):
+ # dealing with ranges:
+ if "-" in ip:
+ return "-".split(ip)
+
+ ip_version = valid_ip_address(ip)
+ if ip_version == "Invalid":
+ FWOLogger.warning("error while decoding ip '" + ip + "'")
+ return [ip]
+ if ip_version == "IPv4":
+ net = ipaddress.IPv4Network(ip)
+ elif ip_version == "IPv6":
+ net = ipaddress.IPv6Network(ip)
+ return [str(net.network_address), str(net.broadcast_address)] # type: ignore # noqa: PGH003
+
+ return [ip]
+
+
+def valid_ip_address(ip: str) -> str:
+ try:
+ # Try as network first (handles CIDR notation)
+ network = ipaddress.ip_network(ip, strict=False)
+ if network.version == 4: # noqa: PLR2004
+ return "IPv4"
+ return "IPv6"
+ except ValueError:
+ try:
+ # Try as individual address
+ addr = ipaddress.ip_address(ip)
+ if addr.version == 4: # noqa: PLR2004
+ return "IPv4"
+ return "IPv6"
+ except ValueError:
+ return "Invalid"
+
+
+def lcs_dp(seq1: list[Any], seq2: list[Any]) -> tuple[list[list[int]], int]:
+ """
+ Compute the length and dynamic programming (DP) table for the longest common subsequence (LCS)
+ between seq1 and seq2. Returns (dp, length) where dp is a 2D table and
+ length = dp[len(seq1)][len(seq2)].
+ """
+ m: int = len(seq1)
+ n: int = len(seq2)
+ dp: list[list[int]] = [[0] * (n + 1) for _ in range(m + 1)]
+
+ for i in range(m):
+ for j in range(n):
+ if seq1[i] == seq2[j]:
+ dp[i + 1][j + 1] = dp[i][j] + 1
+ else:
+ dp[i + 1][j + 1] = max(dp[i + 1][j], dp[i][j + 1])
+ return dp, dp[m][n]
+
+
+def backtrack_lcs(seq1: list[Any], seq2: list[Any], dp: list[list[int]]) -> list[tuple[int, int]]:
+ """
+ Backtracks the dynamic programming (DP) table to recover one longest common subsequence (LCS) (as a list of (i, j) index pairs).
+ These index pairs indicate positions in seq1 and seq2 that match in the LCS.
+ """
+ lcs_indices: list[tuple[int, int]] = []
+ i: int = len(seq1)
+ j: int = len(seq2)
+ while i > 0 and j > 0:
+ if seq1[i - 1] == seq2[j - 1]:
+ lcs_indices.append((i - 1, j - 1))
+ i -= 1
+ j -= 1
+ elif dp[i - 1][j] >= dp[i][j - 1]:
+ i -= 1
+ else:
+ j -= 1
+ lcs_indices.reverse()
+ return lcs_indices
+
+
+def compute_min_moves(source: list[Any], target: list[Any]) -> dict[str, Any]:
+ """
+ Computes the minimal number of operations required to transform the source list into the target list,
+ where allowed operations are:
+ - pop-and-reinsert a common element (to reposition it)
+ - delete (an element in source not present in target)
+ - insert (an element in target not present in source)
+
+ Returns a dictionary with all gathered data (total_moves, operations, deletions, insertions and moves) where operations is a list of suggested human readable operations.
+ """
+ # Build sets (assume uniqueness for membership checks)
+ target_set: set[Any] = set(target)
+ source_set: set[Any] = set(source)
+
+ # Identify the common elements:
+ s_common: list[Any] = [elem for elem in source if elem in target_set]
+ t_common: list[Any] = [elem for elem in target if elem in source_set]
+
+ # Calculate deletions and insertions:
+ deletions: list[tuple[int, Any]] = [(i, elem) for i, elem in enumerate(source) if elem not in target_set]
+ insertions: list[tuple[int, Any]] = [(j, elem) for j, elem in enumerate(target) if elem not in source_set]
+
+ # Compute the longest common subsequence (LCS) between S_common and T_common - these are common elements already in correct relative order.
+ lcs_data: tuple[list[list[int]], int] = lcs_dp(s_common, t_common)
+ lcs_indices: list[tuple[int, int]] = backtrack_lcs(s_common, t_common, lcs_data[0])
+
+ # To decide which common elements must be repositioned, mark the indices in S_common which are part of the LCS.
+ in_place: list[bool] = [False] * len(s_common)
+ for i, _ in lcs_indices:
+ in_place[i] = True
+ # Every common element in S_common not in the LCS will need a pop-and-reinsert.
+ reposition_moves: list[tuple[int, Any, int]] = []
+ # To better explain (rough indexing): We traverse the source list and when we get to a common element,
+ # we check if it is “in place”. Note that because S_common is a filtered version of source, we need
+ # to convert back to indices in the original source. We do this by iterating over source and whenever
+ # we encounter an element in target_set, we pop the next value from S_common.
+ s_common_iter: int = 0
+ for orig_index, elem in enumerate(source):
+ if elem in target_set:
+ # This element is one of the common ones.
+ if not in_place[s_common_iter]:
+ # This element is not in the LCS so it will be repositioned.
+ # We will reinsert it to the position where it should appear in target.
+ reposition_moves.append((orig_index, elem, target.index(elem)))
+ s_common_iter += 1
+
+ total_moves: int = len(deletions) + len(insertions) + len(reposition_moves)
+
+ # Build a list of human-readable operations.
+ operations: list[str] = []
+ for idx, elem in deletions:
+ operations.append(f"Delete element '{elem}' at source index {idx}.")
+ for idx, elem in insertions:
+ operations.append(f"Insert element '{elem}' at target position {idx}.")
+ for idx, elem, target_pos in reposition_moves:
+ operations.append(f"Pop element '{elem}' from source index {idx} and reinsert at target position {target_pos}.")
+
+ return {
+ "moves": total_moves,
+ "operations": operations,
+ "deletions": deletions,
+ "insertions": insertions,
+ "reposition_moves": reposition_moves,
+ }
+
+
+def write_native_config_to_file(import_state: "ImportState", config_native: dict[str, Any] | None) -> None:
+ if FWOLogger.is_debug_level(7):
+ debug_start_time = int(time.time())
+ try:
+ full_native_config_filename = (
+ f"{IMPORT_TMP_PATH}/mgm_id_{import_state.mgm_details.mgm_id!s}_config_native.json"
+ )
+ with open(full_native_config_filename, "w") as json_data:
+ json_data.write(json.dumps(config_native, indent=2))
+ except Exception:
+ FWOLogger.error(
+ f"import_management - unspecified error while dumping config to json file: {traceback.format_exc()!s}"
+ )
+ raise
+
+ time_write_debug_json = int(time.time()) - debug_start_time
+ FWOLogger.debug(f"import_management - writing debug config json files duration {time_write_debug_json!s}s")
+
+
+def init_service_provider() -> ServiceProvider:
+ service_provider = ServiceProvider()
+ service_provider.register(Services.FWO_CONFIG, lambda: fwo_config.read_config(), Lifetime.SINGLETON)
+ service_provider.register(Services.GROUP_FLATS_MAPPER, lambda: GroupFlatsMapper(), Lifetime.IMPORT)
+ service_provider.register(Services.PREV_GROUP_FLATS_MAPPER, lambda: GroupFlatsMapper(), Lifetime.IMPORT)
+ service_provider.register(Services.UID2ID_MAPPER, lambda: Uid2IdMapper(), Lifetime.IMPORT)
+ service_provider.register(Services.RULE_ORDER_SERVICE, lambda: RuleOrderService(), Lifetime.IMPORT)
+ return service_provider
+
+
+def register_global_state(import_state: "ImportStateController") -> None:
+ service_provider = ServiceProvider()
+ service_provider.register(Services.GLOBAL_STATE, lambda: GlobalState(import_state), Lifetime.SINGLETON)
+
+
+def _diff_dicts(a: dict[Any, Any], b: dict[Any, Any], strict: bool, path: str) -> list[str]:
+ diffs: list[str] = []
+ for k, v in a.items():
+ if k not in b:
+ diffs.append(f"Key '{k}' missing in second object at {path}")
+ else:
+ diffs.extend(find_all_diffs(v, b[k], strict, f"{path}.{k}"))
+ diffs.extend([f"Key '{k}' missing in first object at {path}" for k in b if k not in a])
+ return diffs
+
+
+def _diff_lists(a: list[Any], b: list[Any], strict: bool, path: str) -> list[str]:
+ diffs: list[str] = []
+ for i, (x, y) in enumerate(zip(a, b, strict=False)):
+ diffs.extend(find_all_diffs(x, y, strict, f"{path}[{i}]"))
+ if len(a) != len(b):
+ diffs.append(f"list length mismatch at {path}: {len(a)} != {len(b)}")
+ return diffs
+
+
+def _diff_scalars(a: Any, b: Any, strict: bool, path: str) -> list[str]:
+ diffs: list[str] = []
+ if a != b:
+ if not strict and (a is None or a == "") and (b is None or b == ""):
+ return diffs
+ diffs.append(f"Value mismatch at {path}: {a} != {b}")
+ return diffs
+
+
+def find_all_diffs(a: Any, b: Any, strict: bool = False, path: str = "root") -> list[str]:
+ if isinstance(a, dict) and isinstance(b, dict):
+ return _diff_dicts(a, b, strict, path) # type: ignore # noqa: PGH003
+ if isinstance(a, list) and isinstance(b, list):
+ return _diff_lists(a, b, strict, path) # type: ignore # noqa: PGH003
+ return _diff_scalars(a, b, strict, path)
+
+
+def sort_and_join(input_list: list[str]) -> str:
+ """Sorts the input list of strings and joins them using the standard list delimiter."""
+ return fwo_const.LIST_DELIMITER.join(sorted(input_list))
+
+
+def sort_and_join_refs(input_list: list[tuple[str, str]]) -> tuple[str, str]:
+ """Sorts the input list of (uid, name) tuples and joins uids and names separately using the standard list delimiter."""
+ sorted_list = sorted(input_list, key=lambda x: x[1]) # sort by name
+ uids = [item[0] for item in sorted_list]
+ names = [item[1] for item in sorted_list]
+ joined_uids = fwo_const.LIST_DELIMITER.join(uids)
+ joined_names = fwo_const.LIST_DELIMITER.join(names)
+ return joined_uids, joined_names
+
+
+def generate_hash_from_dict(input_dict: dict[Any, Any]) -> str:
+ """Generates a consistent hash from a dictionary by serializing it with sorted keys."""
+ dict_string = json.dumps(input_dict, sort_keys=True)
+ return hashlib.sha256(dict_string.encode("utf-8")).hexdigest()
diff --git a/roles/importer/files/importer/fwo_config.py b/roles/importer/files/importer/fwo_config.py
index 8aa1ac2e47..e9720ca8d5 100644
--- a/roles/importer/files/importer/fwo_config.py
+++ b/roles/importer/files/importer/fwo_config.py
@@ -1,23 +1,37 @@
+import json
+import sys
-from fwo_log import getFwoLogger
-import sys, json
+from fwo_const import IMPORTER_PWD_FILE
+from fwo_log import FWOLogger
-def readConfig(fwo_config_filename='/etc/fworch/fworch.json'):
- logger = getFwoLogger()
+
+def read_config(fwo_config_filename: str = "/etc/fworch/fworch.json") -> dict[str, str | int | None]:
try:
# read fwo config (API URLs)
- with open(fwo_config_filename, "r") as fwo_config:
- fwo_config = json.loads(fwo_config.read())
- user_management_api_base_url = fwo_config['middleware_uri']
- fwo_api_base_url = fwo_config['api_uri']
+ with open(fwo_config_filename) as fwo_config:
+ fwo_config_json = json.loads(fwo_config.read())
+ user_management_api_base_url = fwo_config_json["middleware_uri"]
+ fwo_api_base_url = fwo_config_json["api_uri"]
+ fwo_version = fwo_config_json["product_version"]
+ fwo_major_version = int(fwo_version.split(".")[0])
+
+ # read importer password from file
+ with open(IMPORTER_PWD_FILE) as file:
+ importer_pwd = file.read().replace("\n", "")
+
except KeyError as e:
- logger.error("config key not found in "+ fwo_config_filename + ": " + e.args[0])
+ FWOLogger.error("config key not found in " + fwo_config_filename + ": " + e.args[0])
sys.exit(1)
- except FileNotFoundError as e:
- logger.error("config file not found or unable to access: "+ fwo_config_filename)
+ except FileNotFoundError:
+ FWOLogger.error("config file not found or unable to access: " + fwo_config_filename)
sys.exit(1)
- except:
- logger.error("unspecified error occured while trying to read config file: "+ fwo_config_filename)
+ except Exception:
+ FWOLogger.error("unspecified error occurred while trying to read config file: " + fwo_config_filename)
sys.exit(1)
- config = { "user_management_api_base_url": user_management_api_base_url, "fwo_api_base_url": fwo_api_base_url }
+ config: dict[str, str | int | None] = {
+ "fwo_major_version": fwo_major_version,
+ "user_management_api_base_url": user_management_api_base_url,
+ "fwo_api_base_url": fwo_api_base_url,
+ "importerPassword": importer_pwd,
+ }
return config
diff --git a/roles/importer/files/importer/fwo_const.py b/roles/importer/files/importer/fwo_const.py
index 3248fb036c..2e259abbbd 100644
--- a/roles/importer/files/importer/fwo_const.py
+++ b/roles/importer/files/importer/fwo_const.py
@@ -1,39 +1,43 @@
-import sys
-import json
-from urllib.parse import urlparse
-import socket
+from typing import Any
-base_dir = '/usr/local/fworch'
-importer_base_dir = base_dir + '/importer'
-sys.path.append(importer_base_dir) # adding absolute path here once
+BASE_DIR = "/usr/local/fworch"
+IMPORTER_BASE_DIR = BASE_DIR + "/importer"
-fw_module_name = 'fwcommon' # the module start-point for product specific code
-full_config_size_limit = 5000000 # native configs greater than 5 MB will not be stored in DB
-csv_delimiter = '%'
-list_delimiter = '|'
-line_delimiter = "\n"
-apostrophe = "\""
-section_header_uids=[]
-nat_postfix = '_NatNwObj'
-fwo_api_http_import_timeout = 14400 # 4 hours
-importer_user_name = 'importer' # todo: move to config file?
-fwo_config_filename = base_dir + '/etc/fworch.json'
-mainKeyFile=base_dir + '/etc/secrets/main_key'
-importer_pwd_file = base_dir + '/etc/secrets/importer_pwd'
-import_tmp_path = base_dir + '/tmp/import'
-fwo_config_filename = base_dir + '/etc/fworch.json'
-max_recursion_level = 25 # do not call a function recursively more than this
-default_section_header_text = 'section without name'
-# possible config-format values: normalized|checkpoint|fortimanager|fortioOS|azure|ciscoFirePower
-# legacy: barracuda|junos|netscreen
+FW_MODULE_NAME = "fwcommon" # the module start-point for product specific code
+FULL_CONFIG_SIZE_LIMIT = 5000000 # native configs greater than 5 MB will not be stored in DB
+LIST_DELIMITER = "|"
+USER_DELIMITER = "@"
+ANY_IP_IPV4 = "0.0.0.0/0"
+ANY_IP_START = "0.0.0.0/32"
+ANY_IP_END = "255.255.255.255/32"
+DUMMY_IP = "0.0.0.0/32"
+DEFAULT_COLOR = "black"
+NAT_POSTFIX = "_NatNwObj"
+FWO_API_HTTP_IMPORT_TIMEOUT = 14400 # 4 hours
+IMPORTER_USER_NAME = "importer" # TODO: move to config file?
+FWO_CONFIG_FILENAME = BASE_DIR + "/etc/fworch.json"
+MAIN_KEY_FILE = BASE_DIR + "/etc/secrets/main_key"
+IMPORTER_PWD_FILE = BASE_DIR + "/etc/secrets/importer_pwd"
+IMPORT_TMP_PATH = BASE_DIR + "/tmp/import" # noqa: S108
+DEFAULT_SECTION_HEADER_TEXT = "section without name"
+GRAPHQL_QUERY_PATH = BASE_DIR + "/fwo-api-calls/"
+
+# possible ConfigFormat values: normalized|checkpoint|fortimanager|fortioOS|azure|ciscoFirePower
+# info legacy : barracuda|junos|netscreen
# how many objects (network, services, rules, ...) should be sent to the FWO API in one go?
# should be between 500 and 2.000 in production (results in a max obj number of max. 5 x this value - nwobj/svc/rules/...)
# the database has a limit of 255 MB per jsonb
# https://stackoverflow.com/questions/12632871/size-limit-of-json-data-type-in-postgresql
# >25.000 rules exceed this limit
-max_objs_per_chunk = 1000
+API_CALL_CHUNK_SIZE = 1000
+RULE_NUM_NUMERIC_STEPS = 1024.0
-# with open(fwo_config_filename, "r") as fwo_config:
-# fwo_config = json.loads(fwo_config.read())
-# fwo_api_base_url = fwo_config['api_uri']
+EMPTY_NORMALIZED_FW_CONFIG_JSON_DICT: dict[str, list[Any]] = { # TYPING: DO NOT USE THIS!!!!
+ "network_objects": [],
+ "service_objects": [],
+ "user_objects": [],
+ "zone_objects": [],
+ "rules": [],
+ "gateways": [],
+}
diff --git a/roles/importer/files/importer/fwo_data_networking.py b/roles/importer/files/importer/fwo_data_networking.py
deleted file mode 100644
index deb8facc47..0000000000
--- a/roles/importer/files/importer/fwo_data_networking.py
+++ /dev/null
@@ -1,164 +0,0 @@
-from fwo_log import getFwoLogger
-from netaddr import IPAddress, IPNetwork
-
-
-class Interface:
- def __init__(self, device_id, name, ip, netmask_bits, state_up=True, ip_version=4):
- self.routing_device = int(device_id)
- # check if routing device id exists?
- self.name = str(name)
- self.ip = IPAddress(ip)
- netmask_bits = int(netmask_bits)
- if netmask_bits<0 or netmask_bits>128:
- logger = getFwoLogger()
- logger.error('interface ' + self.name + ' with invalid bitmask: ' + str(netmask_bits))
- else:
- self.netmask_bits = netmask_bits
- self.state_up = bool(state_up)
- ip_version = int(ip_version)
- if ip_version != 4 and ip_version != 6:
- logger = getFwoLogger()
- logger.error('interface ' + self.name + ' with invalid ip protocal: ' + str(ip_version))
- else:
- self.ip_version = ip_version
-
- self.ip_version = ip_version
-
-
-class InterfaceSerializable(Interface):
- def __init__(self, ifaceIn):
- if type(ifaceIn) is dict:
- self.name = ifaceIn['name']
- self.routing_device = ifaceIn['routing_device']
- self.ip = str(ifaceIn['ip'])
- self.netmask_bits = ifaceIn['netmask_bits']
- self.state_up = ifaceIn['state_up']
- self.ip_version = ifaceIn['ip_version']
- elif isinstance(ifaceIn, Interface):
- self.name = ifaceIn.name
- self.routing_device = ifaceIn.routing_device
- self.ip = str(ifaceIn.ip)
- self.netmask_bits = ifaceIn.netmask_bits
- self.state_up = ifaceIn.state_up
- self.ip_version = ifaceIn.ip_version
-
-
-class Route:
- def __init__(self, device_id, target_gateway, destination,
- static=True, source=None, interface=None, metric=None, distance=None, ip_version=4):
- self.routing_device = int(device_id)
- if interface is not None:
- self.interface = str(interface)
- else:
- self.interface = None
- self.target_gateway = IPAddress(target_gateway)
- self.destination = IPNetwork(destination)
- if source is not None:
- self.source = IPNetwork(source)
- else:
- self.source = None
- self.static = bool(static)
- if metric is not None:
- self.metric = int(metric)
- if distance is not None:
- self.distance = int(distance)
- ip_version = int(ip_version)
- if ip_version != 4 and ip_version != 6:
- logger = getFwoLogger()
- logger.error('found route for destination ' + str(self.destination) + ' with invalid ip protocal: ' + str(ip_version))
- else:
- self.ip_version = ip_version
-
-
- def isDefaultRoute(self):
- return self.isDefaultRouteV4() or self.isDefaultRouteV6()
-
-
- def isDefaultRouteV4(self):
- return self.ip_version == 4 and self.destination == IPNetwork('0.0.0.0/0')
-
-
- def isDefaultRouteV6(self):
- return self.ip_version==6 and self.destination == IPNetwork('::/0')
-
-
- def routeMatches(self, destination, dev_id):
- ip_n = IPNetwork(self.destination).cidr
- dest_n = IPNetwork(destination).cidr
- return dev_id == self.routing_device and (ip_n in dest_n or dest_n in ip_n)
-
-
-class RouteSerializable(Route):
- def __init__(self, routeIn):
- if type(routeIn) is dict:
- self.routing_device = routeIn['routing_device']
- self.interface = routeIn['interface']
- self.target_gateway = str(routeIn['target_gateway'])
- self.destination = str(routeIn['destination'])
- if routeIn['source'] is None:
- self.source = None
- else:
- self.source = str(routeIn['source'])
- self.static = routeIn['static']
- self.metric = routeIn['metric']
- self.distance = routeIn['distance']
- self.ip_version = routeIn['ip_version']
- elif isinstance(routeIn, Route):
- self.routing_device = routeIn.routing_device
- self.interface = routeIn.interface
- self.target_gateway = str(routeIn.target_gateway)
- self.destination = str(routeIn.destination)
- if routeIn.source is None:
- self.source = None
- else:
- self.source = str(routeIn.source)
- self.static = routeIn.static
- self.metric = routeIn.metric
- self.distance = routeIn.distance
- self.ip_version = routeIn.ip_version
-
-
-def getRouteDestination(obj):
- return obj.destination
-
-
-# def test_if_default_route_exists_obj(routing_table):
-# default_route_v4 = list(filter(lambda default_route: default_route.destination == IPNetwork('0.0.0.0/0'), routing_table))
-# default_route_v6 = list(filter(lambda default_route: default_route.destination == IPNetwork('::/0'), routing_table))
-# if default_route_v4 == [] and default_route_v6 == []:
-# return False
-# else:
-# return True
-
-
-# def get_devices_without_default_route(routing_table):
-# dev_ids = vars(routing_table)
-# default_route_v4 = list(filter(lambda default_route: default_route.destination == IPNetwork('0.0.0.0/0'), routing_table))
-# default_route_v6 = list(filter(lambda default_route: default_route.destination == IPNetwork('::/0'), routing_table))
-# return default_route_v4.append(default_route_v6)
-
-
-def get_matching_route_obj(destination_ip, routing_table, dev_id):
-
- logger = getFwoLogger()
-
- if len(routing_table)==0:
- logger.error('found empty routing table for device id ' + str(dev_id))
- return None
-
- # assuiming routing table to be in sorted state already
- for route in routing_table:
- if route.routeMatches(destination_ip, dev_id):
- return route
-
- logger.warning('src nat behind interface: found no matching route in routing table - no default route?!')
- return None
-
-
-def get_ip_of_interface_obj(interface_name, dev_id, interface_list=[]):
- interface_details = next((sub for sub in interface_list if sub.name == interface_name and sub.routing_device==dev_id), None)
-
- if interface_details is not None:
- return interface_details.ip
- else:
- return None
diff --git a/roles/importer/files/importer/fwo_encrypt.py b/roles/importer/files/importer/fwo_encrypt.py
index 3de3963c76..16d5be321d 100644
--- a/roles/importer/files/importer/fwo_encrypt.py
+++ b/roles/importer/files/importer/fwo_encrypt.py
@@ -1,17 +1,20 @@
import base64
+import traceback
+
from cryptography.hazmat.backends import default_backend
-from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding
-import traceback
-from fwo_log import getFwoLogger
+from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
+from fwo_const import MAIN_KEY_FILE
+from fwo_log import FWOLogger
+
# can be used for decrypting text encrypted with C# (mw-server)
-def decrypt_aes_ciphertext(base64_encrypted_text, passphrase):
+def decrypt_aes_ciphertext(base64_encrypted_text: str, passphrase: str) -> str:
encrypted_data = base64.b64decode(base64_encrypted_text)
- ivLength = 16 # IV length for AES is 16 bytes
+ iv_length = 16 # IV length for AES is 16 bytes
# Extract IV from the encrypted data
- iv = encrypted_data[:ivLength]
+ iv = encrypted_data[:iv_length]
# Initialize AES cipher with provided passphrase and IV
backend = default_backend()
@@ -19,23 +22,28 @@ def decrypt_aes_ciphertext(base64_encrypted_text, passphrase):
decryptor = cipher.decryptor()
# Decrypt the ciphertext
- decrypted_data = decryptor.update(encrypted_data[ivLength:]) + decryptor.finalize()
+ decrypted_data = decryptor.update(encrypted_data[iv_length:]) + decryptor.finalize()
# Remove padding
- unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
+ unpadder = padding.PKCS7(
+ algorithms.AES.block_size # type: ignore # noqa: PGH003
+ ).unpadder() # TODO: Check if block_size is correct
try:
unpadded_data = unpadder.update(decrypted_data) + unpadder.finalize()
- return unpadded_data.decode('utf-8') # Assuming plaintext is UTF-8 encoded
+ return unpadded_data.decode("utf-8") # Assuming plaintext is UTF-8 encoded
except ValueError as e:
- raise Exception ('AES decryption failed:', e)
+ raise ValueError("AES decryption failed:", e)
# wrapper for trying the different decryption methods
-def decrypt(encrypted_data, passphrase):
- logger = getFwoLogger()
+def decrypt(encrypted_data: str, passphrase: str) -> str:
try:
- decrypted = decrypt_aes_ciphertext(encrypted_data, passphrase)
- return decrypted
- except:
- logger.warning("Unspecified error while decrypting with AES: " + str(traceback.format_exc()))
+ return decrypt_aes_ciphertext(encrypted_data, passphrase)
+ except Exception:
+ FWOLogger.warning("Unspecified error while decrypting with AES: " + str(traceback.format_exc()))
return encrypted_data
+
+
+def read_main_key(file_path: str = MAIN_KEY_FILE) -> str:
+ with open(file_path) as keyfile:
+ return keyfile.read().rstrip(" \n")
diff --git a/roles/importer/files/importer/fwo_enums.py b/roles/importer/files/importer/fwo_enums.py
new file mode 100644
index 0000000000..deff9138d9
--- /dev/null
+++ b/roles/importer/files/importer/fwo_enums.py
@@ -0,0 +1,34 @@
+from enum import Enum
+
+
+class ConfigAction(Enum):
+ INSERT = "INSERT"
+ UPDATE = "UPDATE"
+ DELETE = "DELETE"
+
+
+class ConfFormat(Enum):
+ NORMALIZED = "NORMALIZED"
+
+ CHECKPOINT = "CHECKPOINT"
+ FORTINET = "FORTINET"
+ FORTIMANAGER = "FORTIMANAGER"
+ PALOALTO = "PALOALTO"
+ CISCOFIREPOWER = "CISCOFIREPOWER"
+
+ NORMALIZED_LEGACY = "NORMALIZED_LEGACY"
+
+ CHECKPOINT_LEGACY = "CHECKPOINT_LEGACY"
+ FORTINET_LEGACY = "FORTINET_LEGACY"
+ PALOALTO_LEGACY = "PALOALTO_LEGACY"
+ CISCOFIREPOWER_LEGACY = "CISCOFIREPOWER_LEGACY"
+
+ @staticmethod
+ def is_legacy_config_format(conf_format_string: str) -> bool:
+ return ConfFormat(conf_format_string) in [
+ ConfFormat.NORMALIZED_LEGACY,
+ ConfFormat.CHECKPOINT_LEGACY,
+ ConfFormat.CISCOFIREPOWER_LEGACY,
+ ConfFormat.FORTINET_LEGACY,
+ ConfFormat.PALOALTO_LEGACY,
+ ]
diff --git a/roles/importer/files/importer/fwo_exception.py b/roles/importer/files/importer/fwo_exception.py
deleted file mode 100644
index 7da8a8cc9a..0000000000
--- a/roles/importer/files/importer/fwo_exception.py
+++ /dev/null
@@ -1,71 +0,0 @@
-
-class FwLoginFailed(Exception):
- """Raised when login to FW management failed"""
-
- def __init__(self, message="Login to FW management failed"):
- self.message = message
- super().__init__(self.message)
-
-class FwLogoutFailed(Exception):
- """Raised when logout from FW management failed"""
-
- def __init__(self, message="Logout from FW management failed"):
- self.message = message
- super().__init__(self.message)
-
-class SecretDecryptionFailed(Exception):
- """Raised when the attempt to decrypt a secret with the given key fails"""
-
- def __init__(self, message="Could not decrypt an API secret with given key"):
- self.message = message
- super().__init__(self.message)
-
-class FwoApiLoginFailed(Exception):
- """Raised when login to FWO API failed"""
-
- def __init__(self, message="Login to FWO API failed"):
- self.message = message
- super().__init__(self.message)
-
-class FwoApiFailedLockImport(Exception):
- """Raised when unable to lock import (import running?)"""
-
- def __init__(self, message="Locking import failed - already running?"):
- self.message = message
- super().__init__(self.message)
-
-class FwoApiFailure(Exception):
- """Raised for any other FwoApi call exceptions"""
-
- def __init__(self, message="There was an unclassified error while executing an FWO API call"):
- self.message = message
- super().__init__(self.message)
-
-class FwoApiTimeout(Exception):
- """Raised for 502 http error with proxy due to timeout"""
-
- def __init__(self, message="reverse proxy timeout error during FWO API call - try increasing the reverse proxy timeout"):
- self.message = message
- super().__init__(self.message)
-
-class FwoApiServiceUnavailable(Exception):
- """Raised for 503 http error Serice unavailable"""
-
- def __init__(self, message="FWO API Hasura container died"):
- self.message = message
- super().__init__(self.message)
-
-class ConfigFileNotFound(Exception):
- """can only happen when specifying config file with -i switch"""
-
- def __init__(self, message="Could not read config file"):
- self.message = message
- super().__init__(self.message)
-
-
-class ImportRecursionLimitReached(Exception):
- """Raised when recursion of function inimport process reaches max allowed recursion limit"""
-
- def __init__(self, message="Max recursion level reached - aborting"):
- self.message = message
- super().__init__(self.message)
diff --git a/roles/importer/files/importer/fwo_exceptions.py b/roles/importer/files/importer/fwo_exceptions.py
new file mode 100644
index 0000000000..43b0ac0985
--- /dev/null
+++ b/roles/importer/files/importer/fwo_exceptions.py
@@ -0,0 +1,205 @@
+rollback_string = "Operation interrupted. Rollback required."
+
+
+class FwLoginFailedError(Exception):
+ """Raised when login to FW management failed"""
+
+ def __init__(self, message: str = "Login to FW management failed"):
+ self.message = message
+ super().__init__(self.message)
+
+
+class FwApiCallFailedError(Exception):
+ """Raised when FW management API call failed"""
+
+ def __init__(self, message: str = "An API call to the FW management failed"):
+ self.message = message
+ super().__init__(self.message)
+
+
+class FwLogoutFailedError(Exception):
+ """Raised when logout from FW management failed"""
+
+ def __init__(self, message: str = "Logout from FW management failed"):
+ self.message = message
+ super().__init__(self.message)
+
+
+class FwoNativeConfigFetchError(Exception):
+ """Raised when getting native config from FW management fails, no rollback necessary"""
+
+ def __init__(self, message: str = "Login to FW management failed"):
+ self.message = message
+ super().__init__(self.message)
+
+
+class FwoNormalizedConfigParseError(Exception):
+ """Raised while parsing normalized config"""
+
+ def __init__(self, message: str = "Parsing normalized config failed"):
+ self.message = message
+ super().__init__(self.message)
+
+
+class SecretDecryptionFailedError(Exception):
+ """Raised when the attempt to decrypt a secret with the given key fails"""
+
+ def __init__(self, message: str = "Could not decrypt an API secret with given key"):
+ self.message = message
+ super().__init__(self.message)
+
+
+class FwoApiLoginFailedError(Exception):
+ """Raised when login to FWO API fails"""
+
+ def __init__(self, message: str = "Login to FWO API failed"):
+ self.message = message
+ super().__init__(self.message)
+
+
+class FwoApiFailedLockImportError(Exception):
+ """Raised when unable to lock import (import running?)"""
+
+ def __init__(self, message: str = "Locking import failed - already running?"):
+ self.message = message
+ super().__init__(self.message)
+
+
+class FwoApiFailedUnLockImportError(Exception):
+ """Raised when unable to remove import lock"""
+
+ def __init__(self, message: str = "Unlocking import failed"):
+ self.message = message
+ super().__init__(self.message)
+
+
+class FwoApiWriteError(Exception):
+ """Raised when an FWO API mutation fails"""
+
+ def __init__(self, message: str = "FWO API mutation failed"):
+ self.message = message
+ super().__init__(self.message)
+
+
+class FwoApiFailureError(Exception):
+ """Raised for any other FwoApi call exceptions"""
+
+ def __init__(self, message: str = "There was an unclassified error while executing an FWO API call"):
+ self.message = message
+ super().__init__(self.message)
+
+
+class FwoApiTimeoutError(Exception):
+ """Raised for 502 http error with proxy due to timeout"""
+
+ def __init__(
+ self,
+ message: str = "reverse proxy timeout error during FWO API call - try increasing the reverse proxy timeout",
+ ):
+ self.message = message
+ super().__init__(self.message)
+
+
+class FwoApiServiceUnavailableError(Exception):
+ """Raised for 503 http error Serice unavailable"""
+
+ def __init__(self, message: str = "FWO API Hasura container died"):
+ self.message = message
+ super().__init__(self.message)
+
+
+class ConfigFileNotFoundError(Exception):
+ """can only happen when specifying config file with -i switch"""
+
+ def __init__(self, message: str = "Could not read config file"):
+ self.message = message
+ super().__init__(self.message)
+
+
+class ImportRecursionLimitReachedError(Exception):
+ """Raised when recursion of function inimport process reaches max allowed recursion limit"""
+
+ def __init__(self, message: str = "Max recursion level reached - aborting"):
+ self.message = message
+ super().__init__(self.message)
+
+
+class ImportInterruptionError(Exception):
+ """Custom exception to signal an interrupted call requiring rollback."""
+
+ def __init__(self, message: str = rollback_string):
+ super().__init__(message)
+
+
+class FwoImporterError(Exception):
+ """Custom exception to signal a failed import attempt."""
+
+ def __init__(self, message: str = rollback_string):
+ super().__init__(message)
+
+
+class FwoImporterErrorInconsistenciesError(Exception):
+ """Custom exception to signal a failed import attempt."""
+
+ def __init__(self, message: str = rollback_string):
+ super().__init__(message)
+
+
+class RollbackNecessaryError(Exception):
+ """Custom exception to signal a failed import attempt which needs a rollback."""
+
+ def __init__(self, message: str = "Rollback required."):
+ super().__init__(message)
+
+
+class RollbackError(Exception):
+ """Custom exception to signal a failed rollback attempt."""
+
+ def __init__(self, message: str = "Rollback failed."):
+ super().__init__(message)
+
+
+class FwApiError(Exception):
+ """Custom exception to signal a failure during access checkpoint api."""
+
+ def __init__(self, message: str = "Error while trying to access firewall management API."):
+ super().__init__(message)
+
+
+class FwApiResponseDecodingError(Exception):
+ """Custom exception to signal a failure during decoding checkpoint api response to JSON."""
+
+ def __init__(self, message: str = "Error while trying to decode firewall management API response into JSON."):
+ super().__init__(message)
+
+
+class FwoApiFailedDeleteOldImportsError(Exception):
+ """Custom exception to signal a failure during deletion of old import data."""
+
+ def __init__(self, message: str = "Error while trying to remove old import data."):
+ super().__init__(message)
+
+
+class FwoDuplicateKeyViolationError(Exception):
+ """Custom exception to signal a duplicate key violation during import."""
+
+ def __init__(self, message: str = "Error while trying to add data with duplicate keys"):
+ super().__init__(message)
+
+
+class FwoUnknownDeviceForManagerError(Exception):
+ """Custom exception to signal an unknown device during import."""
+
+ def __init__(self, message: str = "Could not find device in manager config"):
+ super().__init__(message)
+
+
+class FwoDeviceWithoutLocalPackageError(Exception):
+ """Custom exception to signal a device without local package."""
+
+ def __init__(self, message: str = "Could not local package for device in manager config"):
+ super().__init__(message)
+
+
+class ShutdownRequestedError(Exception):
+ pass
diff --git a/roles/importer/files/importer/fwo_file_import.py b/roles/importer/files/importer/fwo_file_import.py
index 4d3990556c..1d6a01f0c0 100644
--- a/roles/importer/files/importer/fwo_file_import.py
+++ b/roles/importer/files/importer/fwo_file_import.py
@@ -1,65 +1,149 @@
-import json, requests, requests.packages
-from fwo_log import getFwoLogger
+"""
+read config from file and convert to non-legacy format (in case of legacy input)
+"""
+
+import json
+import traceback
+from typing import Any
+
import fwo_globals
-from fwo_exception import ConfigFileNotFound
-from fwo_api import complete_import
-
-
-def readJsonConfigFromFile(importState, config):
-
- # when we read from a normalized config file, it contains non-matching dev_ids in gw_ tables
- def replace_device_id(config, mgm_details):
- logger = getFwoLogger()
- if 'routing' in config or 'interfaces' in config:
- if len(mgm_details['devices'])>1:
- logger.warning('importing from config file with more than one device - just picking the first device at random')
- if len(mgm_details['devices'])>=1:
- # just picking the first device
- dev_id = mgm_details['devices'][0]['id']
- if 'routing' in config:
- i=0
- while i dicts with uid as id
+
+ {
+ "ConfigFormat": "NORMALIZED",
+ "managers": [
+ {
+ "ManagerUid": "6ae3760206b9bfbd2282b5964f6ea07869374f427533c72faa7418c28f7a77f2",
+ "ManagerName": "MGM NAME",
+ "IsGlobal": false,
+ "Configs": {
+ "action": "INSERT",
+ "network_objects": [
+ {
+
+ }
+ }
+ }
+ ]
+ }
+
+ 3) native legacy formats
+
+ these will we wrapped with the following:
+
+ TODO: need to detect native format from file
+
+ {
+ "ConfigFormat": "_LEGACY",
+ "config": configJson
+ }
+
+ output formats:
+
+ a) NORMALIZED:
+ check point
+ {
+ "users": {},
+ "object_tables": [
+ {
+ "object_type": "hosts",
+ "object_chunks": [
+ {
+ "objects": [
+ }
+
+"""
+
+
+def read_json_config_from_file(fwo_api_call: FwoApiCall, import_state: ImportState) -> FwConfigManagerListController:
+ config_json = read_file(fwo_api_call, import_state)
+
+ # try to convert normalized config from file to config object
try:
- if importState.ImportFileName is not None:
- if importState.ImportFileName.startswith('http://') or importState.ImportFileName.startswith('https://'): # get conf file via http(s)
- session = requests.Session()
- session.headers = { 'Content-Type': 'application/json' }
- session.verify=fwo_globals.verify_certs
- r = session.get(importState.ImportFileName, )
- r.raise_for_status()
- config = json.loads(r.content)
- else: # reading from local file
- if importState.ImportFileName.startswith('file://'): # remove file uri identifier
- filename = importState.ImportFileName[7:]
- with open(filename, 'r') as json_file:
- config = json.load(json_file)
- except requests.exceptions.RequestException:
- try:
- # check if response "r" is defined:
- r
-
- importState.setErrorString('got HTTP status code{code} while trying to read config file from URL {filename}'.format(code=str(r.status_code), filename=filename))
- except NameError:
- importState.setErrorString('got error while trying to read config file from URL {filename}'.format(filename=filename))
- importState.setErrorCounter(importState.ErrorCount+1)
- complete_import(importState)
- raise ConfigFileNotFound(importState.ErrorString) from None
- except:
- # logger.exception("import_management - error while reading json import from file", traceback.format_exc())
- importState.setErrorString("Could not read config file {filename}".format(filename=filename))
- importState.setErrorCounter(importState.ErrorCount+1)
- complete_import(importState)
- raise ConfigFileNotFound(importState.ErrorString) from None
-
- replace_device_id(config, importState.MgmDetails)
-
- return config
+ manager_list = FwConfigManagerListController(**config_json) # TYPING: use model load
+ if len(manager_list.ManagerSet) == 0:
+ FWOLogger.warning(
+ f"read a config file without manager sets from {import_state.import_file_name}, trying native config"
+ )
+ manager_list.native_config = config_json
+ manager_list.ConfigFormat = detect_legacy_format(config_json)
+ return manager_list
+ except Exception: # legacy stuff from here
+ FWOLogger.info(f"could not serialize config {traceback.format_exc()!s}")
+ raise FwoImporterError(f"could not serialize config {import_state.import_file_name} - trying legacy formats")
+
+
+def detect_legacy_format(config_json: dict[str, Any]) -> ConfFormat:
+ result = ConfFormat.NORMALIZED_LEGACY
+
+ if "object_tables" in config_json:
+ result = ConfFormat.CHECKPOINT_LEGACY
+ elif "domains" in config_json:
+ result = ConfFormat.FORTIMANAGER
+
+ return result
+
+
+def read_file(fwo_api_call: FwoApiCall, import_state: ImportState) -> dict[str, Any]:
+ config_json: dict[str, Any] = {}
+ r = None
+ if import_state.import_file_name == "":
+ return config_json
+ try:
+ if import_state.import_file_name.startswith("http://") or import_state.import_file_name.startswith(
+ "https://"
+ ): # get conf file via http(s)
+ session = requests.Session()
+ session.headers = {"Content-Type": "application/json"}
+ session.verify = fwo_globals.verify_certs
+ r = session.get(
+ import_state.import_file_name,
+ )
+ if r.ok:
+ return json.loads(r.text)
+ r.raise_for_status()
+ else: # reading from local file
+ if import_state.import_file_name.startswith("file://"): # remove file uri identifier
+ filename = import_state.import_file_name[7:]
+ else:
+ filename = import_state.import_file_name
+ with open(filename) as json_file:
+ config_json = json.load(json_file)
+ except requests.exceptions.RequestException as e:
+ if r is not None:
+ FWOLogger.error(
+ f"got HTTP status code{r.status_code!s} while trying to read config file from URL {import_state.import_file_name}"
+ )
+ else:
+ FWOLogger.error(f"got error while trying to read config file from URL {import_state.import_file_name}")
+
+ fwo_api_call.complete_import(import_state, e)
+ raise ConfigFileNotFoundError(str(e)) from None
+ except Exception as e:
+ FWOLogger.error("unspecified error while reading config file: " + str(traceback.format_exc()))
+ fwo_api_call.complete_import(import_state, e)
+ raise ConfigFileNotFoundError(f"unspecified error while reading config file {import_state.import_file_name}")
+
+ return config_json
diff --git a/roles/importer/files/importer/fwo_globals.py b/roles/importer/files/importer/fwo_globals.py
index f8f762703c..86364eee2e 100644
--- a/roles/importer/files/importer/fwo_globals.py
+++ b/roles/importer/files/importer/fwo_globals.py
@@ -1,18 +1,11 @@
-from urllib.parse import urlparse
-import socket
+# fwo_globals.py
+verify_certs = None
+suppress_cert_warnings = None
+debug_level = 0
+shutdown_requested = False
-debug_level=0
-
-def setGlobalValues (
- verify_certs_in=None,
- suppress_cert_warnings_in=None,
- debug_level_in = 0,
- ):
- global verify_certs
- global suppress_cert_warnings
- global debug_level
+def set_global_values(verify_certs_in: bool | None, suppress_cert_warnings_in: bool | None):
+ global verify_certs, suppress_cert_warnings # noqa: PLW0603
verify_certs = verify_certs_in
suppress_cert_warnings = suppress_cert_warnings_in
- debug_level = int(debug_level_in)
-
\ No newline at end of file
diff --git a/roles/importer/files/importer/fwo_local_settings.py b/roles/importer/files/importer/fwo_local_settings.py
new file mode 100644
index 0000000000..a74415689c
--- /dev/null
+++ b/roles/importer/files/importer/fwo_local_settings.py
@@ -0,0 +1,24 @@
+import json
+import os
+from pathlib import Path
+
+from fwo_log import FWOLogger
+
+python_unit_tests_verbose: bool = False
+
+
+def _load_from_env():
+ global python_unit_tests_verbose # noqa: PLW0603
+
+ path = os.getenv("FWORCH_LOCAL_SETTINGS_PATH")
+
+ if path and Path(path).is_file():
+ try:
+ with open(path, encoding="utf-8") as f:
+ data = json.load(f)
+ python_unit_tests_verbose = bool(data.get("test.unittests.python.verbose", False))
+ except Exception as e:
+ FWOLogger.error(f"Reading local settings from {path} failed ({e}). Using defaults.")
+
+
+_load_from_env()
diff --git a/roles/importer/files/importer/fwo_log.py b/roles/importer/files/importer/fwo_log.py
index 03cfb1c6f2..9fa1f993a0 100644
--- a/roles/importer/files/importer/fwo_log.py
+++ b/roles/importer/files/importer/fwo_log.py
@@ -1,17 +1,24 @@
-import sys
-import fwo_globals
import logging
-import time
import threading
+import time
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from models.import_state import ImportState
+
+ from importer.services.uid2id_mapper import Uid2IdMapper
+
+from typing import Any, Literal
class LogLock:
semaphore = threading.Semaphore()
+ @staticmethod
def handle_log_lock():
# Initialize values
lock_file_path = "/var/fworch/lock/importer_api_log.lock"
- log_owned_by_external = False
+ log_owned = False
stopwatch = time.time()
while True:
@@ -21,112 +28,191 @@ def handle_log_lock():
file.seek(0)
# Read the file content
lock_file_content = file.read().strip()
- # Forcefully release lock after timeout
- if log_owned_by_external and time.time() - stopwatch > 10:
- file.write("FORCEFULLY RELEASED\n")
- stopwatch = -1
- LogLock.semaphore.release()
- log_owned_by_external = False
- # GRANTED - lock was granted by us
+
+ if log_owned:
+ # Forcefully release lock after timeout
+ if time.time() - stopwatch > 10: # noqa: PLR2004
+ file.write("FORCEFULLY RELEASED\n")
+ stopwatch = -1
+ LogLock.semaphore.release()
+ log_owned = False
+
+ elif lock_file_content.endswith("RELEASED"):
+ # RELEASED - lock was released by log swap process
+ # only release lock if it was formerly requested by us
+ stopwatch = -1
+ LogLock.semaphore.release()
+ log_owned = False
+
elif lock_file_content.endswith("GRANTED"):
# Request lock if it is not already requested by us
# (in case of restart with log already granted)
- if not log_owned_by_external:
- LogLock.semaphore.acquire()
- stopwatch = time.time()
- log_owned_by_external = True
- # REQUESTED - lock was requested by log swap process
+ LogLock.semaphore.acquire()
+ stopwatch = time.time()
+ log_owned = True
+
elif lock_file_content.endswith("REQUESTED"):
- # only request lock if it is not already requested by us
- if not log_owned_by_external:
- LogLock.semaphore.acquire()
- stopwatch = time.time()
- log_owned_by_external = True
- file.write("GRANTED\n")
- # RELEASED - lock was released by log swap process
- elif lock_file_content.endswith("RELEASED"):
- # only release lock if it was formerly requested by us
- if log_owned_by_external:
- stopwatch = -1
- LogLock.semaphore.release()
- log_owned_by_external = False
- except Exception as e:
+ # REQUESTED - lock was requested by log swap process
+ LogLock.semaphore.acquire()
+ stopwatch = time.time()
+ log_owned = True
+ file.write("GRANTED\n")
+ except Exception as _: # noqa: S110
pass
# Wait a second
time.sleep(1)
-# Used to accquire lock before log processing
-# class LogFilter(logging.Filter):
-# def filter(self, record):
-# # Acquire lock
-# LogLock.semaphore.acquire()
-# # Return True to allow the log record to be processed
-# return True
+class FWOLogger:
+ logger: logging.Logger
+ debug_level: int
+ def __new__(cls, _debug_level: int = 0):
+ if not hasattr(cls, "instance"):
+ cls.instance = super().__new__(cls)
+ return cls.instance
-# Used to release lock after log processing
-# class LogHandler(logging.StreamHandler):
-# def emit(self, record):
-# # Call the parent class's emit method to perform the actual logging
-# super().emit(record)
-# # Release lock
-# LogLock.semaphore.release()
+ def __init__(self, _debug_level: int = 0):
+ self.logger = get_fwo_logger(_debug_level)
+ self.debug_level = _debug_level
+ def get_logger(self) -> logging.Logger:
+ return self.logger
-def getFwoLogger():
- debug_level = int(fwo_globals.debug_level)
- if debug_level >= 1:
- log_level = logging.DEBUG
- else:
- log_level = logging.INFO
+ def set_debug_level(self, debug_level: int):
+ log_level = logging.DEBUG if int(debug_level) >= 1 else logging.INFO
+ self.logger.setLevel(log_level)
- logger = logging.getLogger()
- #log_handler = LogHandler(stream=sys.stdout)
- #log_filter = LogFilter()
-
- log_format = "%(asctime)s [%(levelname)-5.5s] [%(filename)-10.10s:%(funcName)-10.10s:%(lineno)4d] %(message)s"
- #log_handler.setLevel(log_level)
- #log_handler.addFilter(log_filter)
- #handlers = [log_handler]
-
- #logging.basicConfig(format=log_format, datefmt="%Y-%m-%dT%H:%M:%S%z", handlers=handlers, level=log_level)
- logging.basicConfig(format=log_format, datefmt="%Y-%m-%dT%H:%M:%S%z", level=log_level)
- logger.setLevel(log_level)
+ @staticmethod
+ def debug(msg: str, needed_level: int = 1):
+ log = FWOLogger.instance.get_logger()
+ if FWOLogger.instance.debug_level >= needed_level:
+ # Find the caller's frame to show correct file/function/line info
+ log.debug(msg, stacklevel=2)
- # Set log level for noisy requests/connectionpool module to WARNING:
- connection_log = logging.getLogger("urllib3.connectionpool")
- connection_log.setLevel(logging.WARNING)
- connection_log.propagate = True
+ @staticmethod
+ def error(msg: str):
+ logger = FWOLogger.instance.get_logger()
+ logger.error(msg, stacklevel=2)
- return logger
+ @staticmethod
+ def info(msg: str):
+ logger = FWOLogger.instance.get_logger()
+ logger.info(msg, stacklevel=2)
+ @staticmethod
+ def warning(msg: str):
+ logger = FWOLogger.instance.get_logger()
+ logger.warning(msg, stacklevel=2)
-def getFwoAlertLogger(debug_level=0):
- debug_level=int(debug_level)
- if debug_level>=1:
- llevel = logging.DEBUG
- else:
- llevel = logging.INFO
+ @staticmethod
+ def exception(msg: str, exc_info: Any = None):
+ logger = FWOLogger.instance.get_logger()
+ logger.exception(msg, exc_info=exc_info, stacklevel=2)
- logger = logging.getLogger() # use root logger
- # log_handler = LogHandler(stream=sys.stdout)
- # log_filter = LogFilter()
+ @staticmethod
+ def is_debug_level(level: int) -> bool:
+ return FWOLogger.instance.debug_level >= level
- logformat = "%(asctime)s %(message)s"
- # log_handler.setLevel(llevel)
- # log_handler.addFilter(log_filter)
- # handlers = [log_handler]
- # logging.basicConfig(format=logformat, datefmt="", handlers=handlers, level=llevel)
- logging.basicConfig(format=logformat, datefmt="", level=llevel)
- logger.setLevel(llevel)
+def get_fwo_logger(debug_level: int = 0) -> logging.Logger:
+ log_level = logging.DEBUG if int(debug_level) >= 1 else logging.INFO
- # set log level for noisy requests/connectionpool module to WARNING:
+ logger = logging.getLogger()
+ log_format = "%(asctime)s [%(levelname)-5.5s] [%(filename)-25.25s:%(funcName)-25.25s:%(lineno)4d] %(message)s"
+
+ logging.basicConfig(format=log_format, datefmt="%Y-%m-%dT%H:%M:%S%z", level=log_level)
+ logger.setLevel(log_level)
+
+ # Set log level for noisy requests/connectionpool module to WARNING:
connection_log = logging.getLogger("urllib3.connectionpool")
connection_log.setLevel(logging.WARNING)
connection_log.propagate = True
-
- if debug_level>8:
- logger.debug ("debug_level=" + str(debug_level) )
return logger
+
+
+class ChangeLogger:
+ """
+ A singleton service that holds data and provides logic to compute changelog data for network objects, services and rules.
+ """
+
+ _instance = None
+ changed_nwobj_id_map: dict[int, int]
+ changed_svc_id_map: dict[int, int]
+ _import_state: "ImportState | None" = None
+ _uid2id_mapper: "Uid2IdMapper | None" = None
+
+ def __new__(cls):
+ """
+ Singleton pattern: Creates instance and sets defaults if constructed first time and sets that object to a protected class variable.
+ If the constructor is called when there is already an instance returns that instance instead. That way there will only be one instance of this type throudgh the whole runtime.
+ """
+ if cls._instance is None:
+ cls._instance = super().__new__(cls)
+ cls.changed_nwobj_id_map = {}
+ cls.changed_svc_id_map = {}
+ return cls._instance
+
+ def create_change_id_maps(
+ self,
+ uid2id_mapper: "Uid2IdMapper",
+ changed_nw_objs: list[str],
+ changed_svcs: list[str],
+ removed_nw_objs: list[dict[str, Any]],
+ removed_nw_svcs: list[dict[str, Any]],
+ ):
+ self._uid2id_mapper = uid2id_mapper
+
+ self.changed_object_id_map = {
+ next(
+ removedNwObjId["obj_id"] for removedNwObjId in removed_nw_objs if removedNwObjId["obj_uid"] == old_item
+ ): self._uid2id_mapper.get_network_object_id(old_item)
+ for old_item in changed_nw_objs
+ }
+
+ self.changed_service_id_map = {
+ next(
+ removedNwSvcId["svc_id"] for removedNwSvcId in removed_nw_svcs if removedNwSvcId["svc_uid"] == old_item
+ ): self._uid2id_mapper.get_service_object_id(old_item)
+ for old_item in changed_svcs
+ }
+
+ def create_changelog_import_object(
+ self,
+ typ: str,
+ import_state: "ImportState",
+ change_action: str,
+ change_typ: Literal[2, 3],
+ import_time: str,
+ rule_id: int,
+ rule_id_alternative: int = 0,
+ ) -> dict[str, Any]:
+ unique_name = self._get_changelog_import_object_unique_name(rule_id)
+ old_rule_id = None
+ new_rule_id = None
+ self._import_state = import_state
+
+ if change_action in ["I", "C"]:
+ new_rule_id = rule_id
+
+ if change_action == "C":
+ old_rule_id = rule_id_alternative
+
+ if change_action == "D":
+ old_rule_id = rule_id
+
+ rule_changelog_object: dict[str, Any] = {
+ f"new_{typ}_id": new_rule_id,
+ f"old_{typ}_id": old_rule_id,
+ "control_id": self._import_state.import_id,
+ "change_action": change_action,
+ "mgm_id": self._import_state.mgm_details.mgm_id,
+ "change_type_id": change_typ,
+ "change_time": import_time,
+ "unique_name": unique_name,
+ }
+
+ return rule_changelog_object
+
+ def _get_changelog_import_object_unique_name(self, changelog_entity_id: int) -> str:
+ return str(changelog_entity_id)
diff --git a/roles/importer/files/importer/fwo_mail_unused b/roles/importer/files/importer/fwo_mail_unused
deleted file mode 100644
index 4556310b48..0000000000
--- a/roles/importer/files/importer/fwo_mail_unused
+++ /dev/null
@@ -1,82 +0,0 @@
-import json
-import jsonpickle
-from fwo_data_networking import InterfaceSerializable, RouteSerializable
-import fwo_globals
-from fwo_const import max_objs_per_chunk, csv_delimiter, apostrophe, line_delimiter
-from fwo_log import getFwoLogger, getFwoAlertLogger
-from copy import deepcopy
-import smtplib, ssl
-from email.message import EmailMessage
-
-
-def send_mail(recipient_list, subject, body, fwo_config):
- logger = getFwoLogger()
- # Create a text/plain message
- msg = EmailMessage()
- senderAddress = ""
- msg.set_content(body)
- msg['Subject'] = subject
- if 'emailSenderAddress' in fwo_config:
- senderAddress = fwo_config['emailSenderAddress']
- msg['From'] = senderAddress
- msg['To'] = recipient_list
- tlsSetting = ""
-
- try:
- if 'emailTls' not in fwo_config or fwo_config['emailTls']=='StartTls':
- smtp_server = smtplib.SMTP(fwo_config['emailServerAddress'], int(fwo_config['emailPort']))
- if 'emailTls' in fwo_config and fwo_config['emailTls']=='StartTls':
- tlsSetting = fwo_config['emailTls']
- smtp_server.starttls() #setting up to TLS connection
- smtp_server.ehlo() #calling the ehlo() again as encryption happens on calling startttls()
- else:
- smtp_server.ehlo() #setting the ESMTP protocol
- elif fwo_config['emailTls']=='Tls':
- context = ssl.create_default_context()
- context.check_hostname = False
- context.verify_mode = ssl.CERT_NONE
- smtp_server = smtplib.SMTP(fwo_config['emailServerAddress'], int(fwo_config['emailPort']))
- smtp_server.starttls(context=context)
- smtp_server.ehlo()
- if 'emailUser' in fwo_config and 'emailPassword' in fwo_config and fwo_config['emailUser']!="":
- smtp_server.login(fwo_config['emailUser'], fwo_config['emailPassword']) #logging into out email id
-
- #sending the mail by specifying the from and to address and the message
- smtp_server.send_message(msg)
- smtp_server.quit() #terminating the server
- except Exception as e:
- if 'emailPort' not in fwo_config:
- logger.warning("Missing email server port config. Double-check your emailPort configuration")
- elif int(fwo_config['emailPort'])<1 or int(fwo_config['emailPort'])>65535:
- logger.warning("Email server port configuration out of bounds: " + str(fwo_config['emailPort']) + ". Double-check your emailPort configuration")
- elif 'emailServer' not in fwo_config:
- logger.warning("Missing email server address. Double-check your emailServer configuration")
- elif len(fwo_config['emailServer'])==0:
- logger.warning("Empty email server address. Double-check your emailServer configuration")
- elif recipient_list is None:
- logger.warning("Undefined email recipient list. Double-check your email recipient list")
- elif len(recipient_list)==0:
- logger.warning("Empty email recipient list. Double-check your email recipient list")
- else:
- logger.warning("error while sending import change notification email: " +
- "emailServer: " + fwo_config['emailServerAddress'] + ", " +
- "emailSenderAddress: " + senderAddress + ", " +
- "emailPort: " + fwo_config['emailPort'] + ", " +
- "emailTls: " + str(tlsSetting) + ", " +
- "impChangeNotifyRecipients: " + str(recipient_list) + ", " +
- "error: " + str(e)
- )
-
-
-# def send_change_notification_mail(fwo_config, number_of_changes, mgm_name, mgm_id):
-# if 'impChangeNotifyActive' in fwo_config and bool(fwo_config['impChangeNotifyActive']) and 'impChangeNotifyRecipients' in fwo_config:
-# body = ""
-# if 'impChangeNotifyBody' in fwo_config:
-# body += fwo_config['impChangeNotifyBody'] + ": "
-# body += str(number_of_changes) + ", Management: " + mgm_name + " (id=" + mgm_id + ")"
-# send_mail(
-# fwo_config['impChangeNotifyRecipients'].split(','),
-# fwo_config['impChangeNotifySubject'] if 'impChangeNotifySubject' in fwo_config else "firewall orchestrator change notification",
-# body,
-# fwo_config
-# )
diff --git a/roles/importer/files/importer/fwo_signalling.py b/roles/importer/files/importer/fwo_signalling.py
new file mode 100644
index 0000000000..8c14b71aeb
--- /dev/null
+++ b/roles/importer/files/importer/fwo_signalling.py
@@ -0,0 +1,16 @@
+import signal
+from typing import Any
+
+import fwo_globals
+from fwo_exceptions import ShutdownRequestedError
+
+
+def handle_shutdown_signal(_: int, __: Any):
+ fwo_globals.shutdown_requested = True
+ raise ShutdownRequestedError
+
+
+def register_signalling_handlers():
+ # Register signal handlers for system shutdown interrupts
+ signal.signal(signal.SIGTERM, handle_shutdown_signal) # Handle termination signal
+ signal.signal(signal.SIGINT, handle_shutdown_signal) # Handle interrupt signal (e.g., Ctrl+C)
diff --git a/roles/importer/files/importer/fworch-importer-main.pl b/roles/importer/files/importer/fworch-importer-main.pl
deleted file mode 100755
index 02e24785b1..0000000000
--- a/roles/importer/files/importer/fworch-importer-main.pl
+++ /dev/null
@@ -1,48 +0,0 @@
-#! /usr/bin/perl -w
-use strict;
-use lib '.';
-use CACTUS::FWORCH;
-use CACTUS::read_config;
-use CGI qw(:standard);
-use Sys::Hostname;
-
-my $isobase = &CACTUS::read_config::read_config('TopDir');
-my $importdir = &CACTUS::read_config::read_config('ImportDir');
-my $sleep_time = &CACTUS::read_config::read_config('ImportSleepTime');
-my $hostname_localhost = hostname();
-my $importer_hostname = $hostname_localhost;
-my ($res, $mgm_name, $mgm_id, $fehler);
-
-if ($#ARGV>=0) { if (defined($ARGV[0]) && is_numeric($ARGV[0])) { $sleep_time = $ARGV[0] * 1; } }
-
-while (1) {
- # output_txt("Import: another loop is starting... ");
- # get management systems from the database
- my $dbh1 = DBI->connect("dbi:Pg:dbname=$fworch_database;host=$fworch_srv_host;port=$fworch_srv_port","$fworch_srv_user","$fworch_srv_pw");
- if ( !defined $dbh1 ) { die "Cannot connect to database!\n"; }
- my $sth1 = $dbh1->prepare("SELECT mgm_id, mgm_name, do_not_import, importer_hostname from management LEFT JOIN stm_dev_typ USING (dev_typ_id)" .
- " WHERE NOT do_not_import ORDER BY mgm_name" );
- if ( !defined $sth1 ) { die "Cannot prepare statement: $DBI::errstr\n"; }
- $res = $sth1->execute;
- my $management_hash = $sth1->fetchall_hashref('mgm_name');
- $sth1->finish;
- $dbh1->disconnect;
- # loop across all management systems
- foreach $mgm_name (sort keys %{$management_hash}) {
- $fehler = 0;
- # output_txt("Import: looking at $mgm_name ... ");
- $mgm_id = $management_hash->{"$mgm_name"}->{"mgm_id"};
- if (defined($management_hash->{"$mgm_name"}->{"importer_hostname"})) {
- $importer_hostname = $management_hash->{"$mgm_name"}->{"importer_hostname"};
- }
- if ($importer_hostname eq $hostname_localhost) {
- # output_txt("Import: running on responsible importer $importer_hostname ... ");
- $fehler = system("$importdir/fworch-importer-single.pl mgm_id=$mgm_id");
- if ($fehler) {
- output_txt("Import error: $fehler");
- }
- }
- }
- output_txt("-------- legacy import module going back to sleep for $sleep_time seconds --------\n");
- sleep $sleep_time;
-}
diff --git a/roles/importer/files/importer/fworch-importer-reset-all.pl b/roles/importer/files/importer/fworch-importer-reset-all.pl
deleted file mode 100755
index 65892f0ce4..0000000000
--- a/roles/importer/files/importer/fworch-importer-reset-all.pl
+++ /dev/null
@@ -1,30 +0,0 @@
-#! /usr/bin/perl -w
-use strict;
-use lib '.';
-use CACTUS::FWORCH;
-use CACTUS::read_config;
-use CGI qw(:standard);
-
-my ($res, $mgm_name, $mgm_id, $fehler);
-
-# get management systems from the database
-my $dbh1 = DBI->connect("dbi:Pg:dbname=$fworch_database;host=$fworch_srv_host;port=$fworch_srv_port","$fworch_srv_user","$fworch_srv_pw");
-if ( !defined $dbh1 ) { die "Cannot connect to database!\n"; }
-my $sth1 = $dbh1->prepare("SELECT mgm_id, mgm_name from management LEFT JOIN stm_dev_typ USING (dev_typ_id)" .
- " ORDER BY mgm_name" );
-if ( !defined $sth1 ) { die "Cannot prepare statement: $DBI::errstr\n"; }
-$res = $sth1->execute;
-my $management_hash = $sth1->fetchall_hashref('mgm_name');
-$sth1->finish;
-$dbh1->disconnect;
-
-my $isobase = &CACTUS::read_config::read_config('TopDir');
-my $importdir = &CACTUS::read_config::read_config('ImportDir');
-
-# Schleife ueber alle Managementsysteme: Loeschen und wieder einlesen aller Configs
-foreach $mgm_name (sort keys %{$management_hash}) {
- $mgm_id = $management_hash->{"$mgm_name"}->{"mgm_id"};
- $fehler = system("$importdir/fworch-importer-single.pl mgm_id=$mgm_id -no-md5-checks -clear-management");
- $fehler = system("$importdir/fworch-importer-single.pl mgm_id=$mgm_id -no-md5-checks");
-}
-exit(0);
diff --git a/roles/importer/files/importer/fworch-importer-single.pl b/roles/importer/files/importer/fworch-importer-single.pl
deleted file mode 100755
index 109c290c52..0000000000
--- a/roles/importer/files/importer/fworch-importer-single.pl
+++ /dev/null
@@ -1,272 +0,0 @@
-#! /usr/bin/perl -w
-
-use strict;
-use lib '.';
-use CACTUS::FWORCH; # base functions and variables for fworch db access
-use CACTUS::FWORCH::import; # import functions, parsers
-use CGI qw(:standard); # provides argument handling
-use Time::HiRes qw(time tv_interval); # provides exact measurement of import execution time
-use File::Path qw(make_path rmtree);
-use File::Find;
-use CACTUS::read_config;
-
-##############################################
-
-sub empty_rule_files { # deletes a rule file and creates an empty rule file instead
- if ($File::Find::name =~ /\_rulebase\.csv$/) {
- system("rm '$File::Find::name'");
- system("touch '$File::Find::name'");
- }
-}
-
-sub empty_config_files { # deletes a csv config file and creates an empty csv file instead
- if ($File::Find::name =~ /\.csv$/) {
- system("rm '$File::Find::name'");
- system("touch '$File::Find::name'");
- print ("emptying csv config file '" . $File::Find::name . "'\n");
- } else {
- print ("leaving config file '" . $File::Find::name . "' untouched\n");
- }
-}
-
-my ($user, $output, $dev_name, $cfg_typ, $admin, $hersteller);
-my ($cmd, $dauer, $first_start_time, $start_time, $sqlcode, $fields);
-my ($current_import_id, $dev_id, $dev_typ_id);
-my $changes = '';
-my $error_str_local = ''; my $error_count_global = 0; my $error_count_local = 0; my $error_level;
-my ($template, $obj_file, $obj_file_base, $rule_file, $rule_file_base, $user_file, $user_file_base, $config_files, $config_files_str,
- $cmd_str, $is_netscreen,$ssh_hostname,$ssh_user,$ssh_private_key, $ssh_public_key, $ssh_port, $config_path_on_mgmt);
-my ($sqldatafile, $csv_zone_file, $csv_obj_file, $csv_svc_file, $csv_usr_file, $csv_auditlog_file, $csv_rule_file, $logfiles,
- $show_all_import_errors, $fullauditlog, $clear_all_rules, $clear_whole_mgm_config, $no_md5_checks);
-my $prev_imp_id;
-my $prev_imp_time = "2000-01-01 00:00:00"; # management has never been imported --> default value
-my $do_not_copy = 0;
-my $use_scp = 0;
-my $no_cleanup = 0;
-my $debug_level=0;
-my $configfile="";
-my $csvonly = 0;
-
-
-$first_start_time = time; $start_time = $first_start_time;
-
-my ($mgm_id, $mgm_name) =
- &evaluate_parameters((defined(scalar param("mgm_id")))?scalar param("mgm_id"):'', (defined(scalar param("mgm_name")))?scalar param("mgm_name"):'');
-if (defined(param("-show-only-first-import-error"))) { $show_all_import_errors = 0; } else { $show_all_import_errors = 1; }
-if (defined(param("-fullauditlog"))) { $fullauditlog = 1; } else { $fullauditlog = 0; }
-if (defined(param("-clear-all-rules"))) { $clear_all_rules = 1; } else { $clear_all_rules = 0; }
-if (defined(param("-clear-management"))) { $clear_whole_mgm_config = 1; } else { $clear_whole_mgm_config = 0; }
-if (defined(param("-no-md5-checks"))) { $no_md5_checks = 1; } else { $no_md5_checks = 0; }
-if (defined(param("-do-not-copy"))) { $do_not_copy = 1; $no_md5_checks = 1; } # assumes that config has already been copied to fworch importer
-if (defined(param("-use-scp"))) { $use_scp = 1; $no_md5_checks = 1; } # assumes that config has already been copied to fworch importer
-if (defined(param("-no-cleanup"))) { $no_cleanup = 1; $no_md5_checks = 1; } # assumes that config has already been copied to fworch importer
-if (defined(param("-debug"))) { $debug_level = param("-debug"); }
-if (defined(param("-configfile"))) { $configfile = param("-configfile"); }
-if (defined(param("-csvonly"))) { $csvonly = 1; $do_not_copy = 1; $no_md5_checks = 1; } # for testing - only run db import from existing csv files
-
-# set basic parameters (read from import.conf)
-my $fworch_workdir = &read_config('fworch_workdir') . "/$mgm_id";
-my $archive_dir = &read_config('archive_dir');
-my $cfg_dir = "${fworch_workdir}/cfg";
-my $audit_log_file = "auditlog.export";
-my $bin_path = &read_config('simple_bin_dir') . "/";
-my $save_import_results_to_file = &read_config('save_import_results_to_file');
-my $new_md5sum = 1;
-my $stored_md5sum_of_last_import = 2;
-my $rulebases;
-
-# get import info
-($error_count_local, $error_str_local, $mgm_name, $dev_typ_id, $obj_file_base,$obj_file,$user_file_base,$user_file,$rule_file_base,$rule_file,
- $csv_zone_file, $csv_obj_file, $csv_svc_file, $csv_usr_file, $csv_auditlog_file,
- $ssh_hostname,$ssh_user,$ssh_private_key,$ssh_public_key,$hersteller,$is_netscreen, $config_files, $ssh_port, $config_path_on_mgmt) =
- &get_import_infos_for_mgm($mgm_id, $fworch_workdir, $cfg_dir);
-$error_count_global = &error_handler_add(undef, $error_level = 5, "mgm-id-not-found: $mgm_id", $error_count_local, $error_count_global);
-
-# check if device is a legacy device, otherwise exit here without doing anything
-# (2,'Netscreen','5.x-6.x','Netscreen', '');
-# (4,'FortiGateStandalone','5ff','Fortinet','');
-# (5,'Barracuda Firewall Control Center','Vx','phion','');
-# (6,'phion netfence','3.x','phion','');
-# (7,'Check Point','R5x-R7x','Check Point','');
-if (!(grep {$_ eq $dev_typ_id} (2,4,5,6,7,8))) {
- output_txt("Management $mgm_name (mgm_id=$mgm_id, dev_typ_id=$dev_typ_id): not a legacy device type, skipping\n");
- exit (0);
-}
-
-my $import_was_already_running = (&is_import_running($mgm_id))?1:0;
-my $initial_import_flag = &is_initial_import($mgm_id);
-$current_import_id = &insert_control_entry($initial_import_flag,$mgm_id); # set import lock
-
-output_txt ("current_import_id=$current_import_id");
-$error_count_global = &error_handler_add($current_import_id, $error_level = 3, "set-import-lock-failed", !defined($current_import_id), $error_count_global);
-$error_count_global = &error_handler_add($current_import_id, $error_level = 2, "import-already-running: $mgm_name (ID: $mgm_id)",
- $import_was_already_running, $error_count_global);
-
-if (!$error_count_global) {
- require "CACTUS/FWORCH/import/$hersteller.pm"; # load the matching parser at run time
- $rulebases = &get_rulebase_names($mgm_id, $CACTUS::FWORCH::dbdriver, $fworch_database, $fworch_srv_host, $fworch_srv_port, $fworch_srv_user, $fworch_srv_pw);
- if (!$do_not_copy && !$csvonly) {
- rmtree($fworch_workdir); make_path($fworch_workdir,{mode => 0700}); make_path($cfg_dir,{mode => 0700});
- $error_count_global = &error_handler_add($current_import_id, $error_level = 2, "copy-ssh-keys-failed",
- $error_count_local = &put_ssh_keys_in_place ($fworch_workdir, $ssh_public_key, $ssh_private_key), $error_count_global);
- if (!$initial_import_flag) {
- $prev_imp_id = exec_pgsql_cmd_return_value("SELECT get_last_import_id_for_mgmt($mgm_id)");
- $prev_imp_time = exec_pgsql_cmd_return_value("SELECT start_time FROM import_control WHERE control_id=$prev_imp_id AND successful_import");
- }
- # 1) read names of rulebases of each device from database
- # copy config data from management system to fworch import system
- if ($configfile ne "") {
- system ("${bin_path}scp $configfile $cfg_dir/$obj_file_base");
- }
- else {
- ($error_count_local, $config_files_str) =
- &CACTUS::FWORCH::import::parser::copy_config_from_mgm_to_iso
- ($ssh_user, $ssh_hostname, $mgm_name, $obj_file_base, $cfg_dir, $rule_file_base,
- $fworch_workdir, $audit_log_file, $prev_imp_time, $ssh_port, $config_path_on_mgmt, $rulebases, $debug_level); # TODO: add use_scp parameter
- }
- if ($error_count_local) {
- if ($is_netscreen) { # file-check wg. Netscreen-Return-Code eingebaut
- my $file_size = -s "$cfg_dir/$obj_file_base";
- if (!defined ($file_size) || $file_size==0) {
- $error_count_global = &error_handler_add($current_import_id, $error_level = 3, "netscreen-copy-config-failed: $mgm_name, $error_str_local",
- $error_count_local=1, $error_count_global);
- }
- } else {
- $error_count_global = &error_handler_add($current_import_id, $error_level = 3, "copy-config-failed: $mgm_name, $error_str_local",
- $error_count_local=1, $error_count_global);
- }
- }
- # check if config has changed
- $stored_md5sum_of_last_import = exec_pgsql_cmd_return_value ("SELECT last_import_md5_complete_config FROM management WHERE mgm_id=$mgm_id");
- if (!defined($stored_md5sum_of_last_import)) { $stored_md5sum_of_last_import = -1; }
- # clear stored md5 hash in any case to force import during next run even if transfer of config fails this time
- &exec_pgsql_cmd_no_result("UPDATE management SET last_import_md5_complete_config='cleared' WHERE mgm_id=$mgm_id");
- &iconv_config_files_2_utf8($config_files_str, $fworch_workdir); # convert config files from latin1 to utf-8
-
- $new_md5sum = &calc_md5_of_files($config_files_str, $fworch_workdir); # fworch_workdir is here tmpdir
- $error_count_global = &error_handler_add ($current_import_id, $error_level = 3, "calc-md5sum-failed", (defined($new_md5sum))?0:1, $error_count_global);
- } # end of do_not_copy / $csvonly
- if (!$error_count_global) {
- if ($new_md5sum ne $stored_md5sum_of_last_import || $no_md5_checks) {
- if (!$csvonly)
- {
- $start_time = time(); # parse start time
- output_txt("---------------------------------------------------------------------------\n");
- output_txt("Starting import of management: $mgm_name\n");
- # 2) parse config
- $error_count_local = &CACTUS::FWORCH::import::parser::parse_config ($obj_file, $rule_file, $user_file, $rulebases, $fworch_workdir, $debug_level, $mgm_name, $cfg_dir,
- $current_import_id, "$cfg_dir/$audit_log_file", $prev_imp_time, $fullauditlog, $debug_level);
- if ($error_count_local) {
- $error_count_global = &error_handler_add( $current_import_id, $error_level = 3, "parse-$error_count_local", $error_count_local=1, $error_count_global);
- if (defined($current_import_id))
- {
- $error_count_local = &exec_pgsql_cmd_no_result("SELECT remove_import_lock($current_import_id)");
- }
- $error_count_global = &error_handler_add
- ($current_import_id, $error_level = 3, "remove-import-lock-failed: $error_count_local", $error_count_local, $error_count_global);
- }
- output_txt("Parsing done in " . sprintf("%.2f",(time() - $start_time)) . " seconds");
- }
- if (!$error_count_global) {
- if (!$csvonly)
- {
- &set_last_change_time($last_change_time_of_config, $current_import_id); # Zeit eintragen, zu der die letzte Aenderung an der Config vorgenommen wurde (tuning)
- }
- # starting import from csv to database
- # 3a) fill import tables with bulk copy cmd from csv
- if ($clear_all_rules) # clear rule csv files, if we want to enforce an initial import
- {
- find(\&empty_rule_files,$fworch_workdir);
- print("clearing rule files\n");
- }
- if ($clear_whole_mgm_config) # clear all data to force initial import
- {
- find(\&empty_config_files,$fworch_workdir);
- print("clearing all csv files\n");
- }
- if (-e $csv_usr_file) { &iconv_2_utf8($csv_usr_file, $fworch_workdir); } # utf-8 conversion of user data
-
- # if $csvonly is set: replace import id in all csv files with $current_import_id
- if ($csvonly)
- {
- my @rulebase_basenames = split(/,/, get_local_ruleset_name_list($rulebases));
- my @rulebase_fullnames = ();
- for my $filename (@rulebase_basenames) {
- @rulebase_fullnames = (@rulebase_fullnames, $fworch_workdir . '/' . $filename . '_rulebase.csv' );
- }
- for my $csvfile (($csv_zone_file, $csv_obj_file, $csv_svc_file, $csv_usr_file, @rulebase_fullnames)) {
- # print ("replacing import_id in csvfile=$csvfile\n");
- if (-e $csvfile) {
- replace_import_id_in_csv($csvfile, $current_import_id);
- }
- }
- }
-
- $error_count_local = &fill_import_tables_from_csv($dev_typ_id,$csv_zone_file, $csv_obj_file, $csv_svc_file, $csv_usr_file, $rulebases, $fworch_workdir, $csv_auditlog_file);
-
- # 3b) if an error occured, import everything in single sql statement steps to be able to spot the error
- if ($error_count_local) {
- $error_count_global = &error_handler_add ($current_import_id, $error_level = 3, "first problems while filling database: $error_count_local",
- $error_count_local, $error_count_global);
- if ($show_all_import_errors) {
-
- &fill_import_tables_from_csv_with_sql ($dev_typ_id,$csv_zone_file, $csv_obj_file, $csv_svc_file, $csv_usr_file, $rulebases, $fworch_workdir, $csv_auditlog_file);
-
- }
- }
- # 4) wrapping up
- # updating last import attempt date directly in DB
- &exec_pgsql_cmd_no_result("UPDATE management SET last_import_attempt=now() WHERE mgm_id=$mgm_id");
-
- if (!$error_count_global) { # import ony when no previous errors occured
- $error_count_local = 0;
- my $imp_result_str = &exec_pgsql_cmd_return_value("SET client_min_messages TO NOTICE; SELECT import_all_main($current_import_id, FALSE)");
- if ($imp_result_str ne '') {
- $error_count_local = 1;
- print("first import run found errors: " . $imp_result_str . ", re-running import with DEBUG option\n");
- &exec_pgsql_cmd_return_value("SET client_min_messages TO DEBUG1; SELECT import_all_main($current_import_id, FALSE)");
- print("second import run with debugging completed\n");
- } else {
- print("found no errors during import\n");
- }
- $error_count_global = &error_handler_add ($current_import_id, $error_level = 3, "", $error_count_local, $error_count_global);
- $changes = &exec_pgsql_cmd_return_value("SELECT show_change_summary($current_import_id)");
- # updating md5sum
- if (!$error_count_global) { &exec_pgsql_cmd_no_result("UPDATE management SET last_import_md5_complete_config='$new_md5sum' WHERE mgm_id=$mgm_id"); }
- }
- }
- # output results and set import status
- if ($changes ne '') { output_txt("Changes: $changes\n"); }
- if ($error_count_global) { output_txt ("Import of management $mgm_name (mgm_id=$mgm_id, import_id=$current_import_id): FOUND $error_count_global error(s)\n", 2); }
- else {
- my $changes_found = 0;
- if ($changes ne '') { $changes_found = 1; }
- output_txt ("Import of management $mgm_name (mgm_id=$mgm_id, import_id=$current_import_id)" .
- ", total time: " . sprintf("%.2fs",(time() - $first_start_time)) . "): no errors found" . (($changes_found )?"":", no changes found") . "\n");
- my $sql_cmd = 'UPDATE import_control SET successful_import=TRUE' . (($changes_found)? ', changes_found=TRUE':'') . ' WHERE control_id=' . $current_import_id;
- &exec_pgsql_cmd_no_result($sql_cmd); # if no error occured - set import_control.successful_import to true
- }
- } else {
- output_txt("Management $mgm_name (mgm_id=$mgm_id), no changes in configuration files (MD5)\n");
- &exec_pgsql_cmd_no_result("DELETE FROM import_control WHERE control_id=$current_import_id"); # remove imports with unchanged data
- &exec_pgsql_cmd_no_result("UPDATE management SET last_import_md5_complete_config='$new_md5sum' WHERE mgm_id=$mgm_id");
- }
- }
- # Cleanup and statistics
- if (defined($current_import_id))
- {
- &exec_pgsql_cmd_no_result("SELECT remove_import_lock($current_import_id)"); # this sets import_control.stop_time to now()
- }
- &clean_up_fworch_db($current_import_id);
- if (defined($save_import_results_to_file) && $save_import_results_to_file && ($error_count_global || $changes ne '')) { # if changes or errors occured: move config & csv to archive
- system ("${bin_path}mkdir -p $archive_dir; cd $fworch_workdir; ${bin_path}tar cfz $archive_dir/${current_import_id}_`${bin_path}date +%F_%T`_mgm_id_$mgm_id.tgz .");
- }
- #`cp -f $fworch_workdir/cfg/*.cfg /var/itsecorg/fw-config/`; # special backup for several configs - dos-box
- if (!$no_cleanup) { rmtree $fworch_workdir; }
-} else {
- if (defined($current_import_id))
- {
- &exec_pgsql_cmd_no_result("SELECT remove_import_lock($current_import_id)"); # this sets import_control.stop_time to now()
- }
-}
-exit ($error_count_global);
diff --git a/roles/importer/files/importer/fworch-importer-srv-helper b/roles/importer/files/importer/fworch-importer-srv-helper
deleted file mode 100755
index 3d60311ac1..0000000000
--- a/roles/importer/files/importer/fworch-importer-srv-helper
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/sh
-
-IMPORTER_SINGLE=fworch-importer-single.pl
-RES=`pidof -x $IMPORTER_SINGLE >/dev/null;echo $?`
-if [ $RES -eq 0 ]; then
- while [ $RES -eq 0 ]; do
- sleep 5
- RES=`pidof -x $IMPORTER_SINGLE >/dev/null;echo $?`
- done
-fi
diff --git a/roles/importer/files/importer/import-api-stop-helper b/roles/importer/files/importer/import-api-stop-helper
deleted file mode 100755
index 6ba5135e7c..0000000000
--- a/roles/importer/files/importer/import-api-stop-helper
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/sh
-
-IMPORTER_SINGLE=import-mgm.py
-RES=`pidof -x $IMPORTER_SINGLE >/dev/null;echo $?`
-if [ $RES -eq 0 ]; then
- while [ $RES -eq 0 ]; do
- sleep 5
- RES=`pidof -x $IMPORTER_SINGLE >/dev/null;echo $?`
- done
-fi
diff --git a/roles/importer/files/importer/import-main-loop.py b/roles/importer/files/importer/import-main-loop.py
index f58a1c099a..9b836878a5 100755
--- a/roles/importer/files/importer/import-main-loop.py
+++ b/roles/importer/files/importer/import-main-loop.py
@@ -1,197 +1,256 @@
-#!/usr/bin/python3
-# add main importer loop in pyhton (also able to run distributed)
-# run import loop every x seconds (adjust sleep time per management depending on the change frequency )
+# main importer loop in python (also able to run distributed) # noqa: N999
+# run import loop every x seconds (adjust sleep time per management depending on the change frequency )
-import signal
-import traceback
import argparse
import sys
import time
-import json
-import threading
-import requests, warnings
-import fwo_api# common # from current working dir
+import traceback
+import warnings
+
+import fwo_globals
+import urllib3
from common import import_management
-from fwo_log import getFwoLogger, LogLock
-import fwo_globals, fwo_config
-from fwo_const import base_dir, importer_base_dir
-from fwo_exception import FwoApiLoginFailed, FwoApiFailedLockImport, FwLoginFailed
+from fwo_api import FwoApi
+from fwo_api_call import FwoApiCall
+from fwo_base import init_service_provider, register_global_state
+from fwo_const import BASE_DIR, IMPORTER_BASE_DIR
+from fwo_exceptions import FwLoginFailedError, FwoApiFailedLockImportError, FwoApiLoginFailedError
+from fwo_log import FWOLogger
+from model_controllers.import_state_controller import ImportStateController
+from model_controllers.management_controller import (
+ ConnectionInfo,
+ CredentialInfo,
+ DeviceInfo,
+ DomainInfo,
+ ManagementController,
+ ManagerInfo,
+)
+
+def get_fwo_jwt(import_user: str, import_pwd: str, user_management_api: str) -> str | None:
+ try:
+ return FwoApi.login(import_user, import_pwd, user_management_api)
+ except FwoApiLoginFailedError as e:
+ FWOLogger.error(e.message)
+ except Exception:
+ FWOLogger.error(
+ "import-main-loop - unspecified error during FWO API login - skipping: " + str(traceback.format_exc())
+ )
-# https://stackoverflow.com/questions/18499497/how-to-process-sigterm-signal-gracefully
-class GracefulKiller:
- kill_now = False
+def wait_with_shutdown_check(sleep_time: int):
+ counter = 0
+ while counter < sleep_time:
+ if fwo_globals.shutdown_requested:
+ FWOLogger.info("import-main-loop - shutdown requested. Exiting...")
+ raise SystemExit("import-main-loop - shutdown requested")
+ time.sleep(1)
+ counter += 1
- def __init__(self):
- signal.signal(signal.SIGINT, self.exit_gracefully)
- signal.signal(signal.SIGTERM, self.exit_gracefully)
+def import_single_management(
+ mgm_id: int,
+ fwo_api_call: FwoApiCall,
+ verify_certificates: bool,
+ api_fetch_limit: int,
+ clear: bool,
+ suppress_certificate_warnings: bool,
+ jwt: str,
+ force: bool,
+ fwo_major_version: int,
+ sleep_timer: int,
+ is_full_import: bool,
+ fwo_api: FwoApi,
+):
+ wait_with_shutdown_check(0)
+ import_state = ImportStateController.initialize_import(
+ mgm_id, jwt, suppress_certificate_warnings, verify_certificates, force, fwo_major_version, clear, is_full_import
+ )
- def exit_gracefully(self, *args):
- self.kill_now = True
+ register_global_state(import_state)
+ try:
+ mgm_controller = ManagementController(
+ mgm_id, "", [], DeviceInfo(), ConnectionInfo(), "", CredentialInfo(), ManagerInfo(), DomainInfo()
+ )
+ mgm_details = mgm_controller.get_mgm_details(fwo_api, mgm_id)
+ except Exception:
+ FWOLogger.error(
+ "import-main-loop - error while getting FW management details for mgm_id="
+ + str(mgm_id)
+ + " - skipping: "
+ + str(traceback.format_exc())
+ )
+ wait_with_shutdown_check(sleep_timer)
+ return
-class LogLockerTask(threading.Thread):
- def __init__(self):
- super().__init__()
- self._stop_event = threading.Event()
- # signal.signal(signal.SIGINT, self.exit_gracefully)
- # signal.signal(signal.SIGTERM, self.exit_gracefully)
+ # only handle CPR8x Manager, fortiManager, Cisco MgmCenter, Palo Panorama, Palo FW, FortiOS REST, Cisco Asa, Asa on FirePower
+ if mgm_details["deviceType"]["id"] not in (9, 12, 17, 22, 23, 24, 28, 29):
+ return
+ FWOLogger.debug(f"import-main-loop: starting import of mgm_id={mgm_id}")
- def run(self):
- while not self._stop_event.is_set():
- threading.Thread(target = LogLock.handle_log_lock)
- time.sleep(1)
+ try:
+ import_management(
+ mgm_id, fwo_api_call, verify_certificates, api_fetch_limit, clear, suppress_certificate_warnings
+ )
+ except (FwoApiFailedLockImportError, FwLoginFailedError):
+ FWOLogger.info(f"import-main-loop - minor error while importing mgm_id={mgm_id}, {traceback.format_exc()!s}")
+ return # minor errors for a single mgm, go to next one
+ except Exception: # all other exceptions are logged here
+ FWOLogger.error(
+ f"import-main-loop - unspecific error while importing mgm_id={mgm_id}, {traceback.format_exc()!s}"
+ )
- def exit_gracefully(self, *args):
- self.kill_now = True
+def main_loop(
+ importer_pwd_file: str,
+ importer_user_name: str,
+ user_management_api_base_url: str,
+ fwo_api_base_url: str,
+ fwo_major_version: int,
+ api_fetch_limit: int,
+ sleep_timer: int,
+ clear: bool,
+ force: bool,
+ is_full_import: bool,
+):
+ wait_with_shutdown_check(0)
+ try:
+ with open(importer_pwd_file) as f:
+ importer_pwd = f.read().replace("\n", "")
+ except Exception:
+ FWOLogger.error("import-main-loop - error while reading importer pwd file")
+ raise
- def stop(self):
- self._stop_event.set()
- # self.kill_now = True
+ jwt = get_fwo_jwt(importer_user_name, importer_pwd, user_management_api_base_url)
+ # check if login was successful - if not, wait and retry
+ if jwt is None:
+ wait_with_shutdown_check(sleep_timer)
+ return
+ fwo_api = FwoApi(fwo_api_base_url, jwt)
+ fwo_api_call = FwoApiCall(fwo_api)
-if __name__ == '__main__':
- parser = argparse.ArgumentParser(
- description='Run import loop across all managements to read configuration from FW managements via API calls')
- parser.add_argument('-d', '--debug', metavar='debug_level', default='0',
- help='Debug Level: 0=off, 1=send debug to console, 2=send debug to file, 3=keep temporary config files; default=0')
- parser.add_argument('-v', "--verify_certificates", action='store_true', default = None,
- help = "verify certificates")
- parser.add_argument('-s', "--suppress_certificate_warnings", action='store_true', default = None,
- help = "suppress certificate warnings")
- parser.add_argument('-c', '--clear', action='store_true', default=False,
- help='If set all imports will run once to delete all data instead of importing')
- parser.add_argument('-f', '--force', action='store_true', default=False,
- help='If set all imports will be run without checking for changes before')
+ urllib3.disable_warnings() # suppress ssl warnings only
+ verify_certificates = fwo_api_call.get_config_value(key="importCheckCertificates") == "True"
+ suppress_certificate_warnings = fwo_api_call.get_config_value(key="importSuppressCertificateWarnings") == "True"
+ if not suppress_certificate_warnings:
+ warnings.resetwarnings()
- args = parser.parse_args()
+ try:
+ mgm_ids = fwo_api_call.get_mgm_ids()
+ except Exception:
+ FWOLogger.error(f"import-main-loop - error while getting FW management ids: {traceback.format_exc()!s}")
+ wait_with_shutdown_check(sleep_timer)
+ return
+
+ api_fetch_limit = int(fwo_api_call.get_config_value(key="fwApiElementsPerFetch") or api_fetch_limit)
+ sleep_timer = int(fwo_api_call.get_config_value(key="importSleepTime") or sleep_timer)
- # logLockerTask = LogLockerTask() # create logLocker
- # logLockerTask.start() # start Log locking
+ ## loop through all managements
+ for mgm_id in mgm_ids:
+ import_single_management(
+ mgm_id,
+ fwo_api_call,
+ verify_certificates,
+ api_fetch_limit,
+ clear,
+ suppress_certificate_warnings,
+ jwt,
+ force,
+ fwo_major_version,
+ sleep_timer,
+ is_full_import,
+ fwo_api,
+ )
- fwo_config = fwo_config.readConfig()
- fwo_globals.setGlobalValues(verify_certs_in=args.verify_certificates,
- suppress_cert_warnings_in=args.suppress_certificate_warnings,
- debug_level_in=args.debug)
- if args.suppress_certificate_warnings:
- requests.packages.urllib3.disable_warnings()
+ FWOLogger.info(f"import-main-loop: sleeping for {sleep_timer} seconds until next import cycle")
+ wait_with_shutdown_check(sleep_timer)
- debug_level = int(args.debug)
- logger = getFwoLogger()
- logger.info("importer-main-loop starting ...")
- sys.path.append(importer_base_dir)
- importer_user_name = 'importer' # todo: move to config file?
- fwo_config_filename = base_dir + '/etc/fworch.json'
- importer_pwd_file = base_dir + '/etc/secrets/importer_pwd'
+def main(
+ debug_level: int,
+ verify_certificates: bool | None = None,
+ suppress_certificate_warnings: bool | None = None,
+ clear: bool = False,
+ force: bool = False,
+ is_full_import: bool = False,
+):
+ FWOLogger(debug_level)
+ service_provider = init_service_provider()
+ fwo_config = service_provider.get_fwo_config()
+ fwo_api_base_url = fwo_config["fwo_api_base_url"]
+ fwo_major_version = fwo_config["fwo_major_version"]
+ user_management_api_base_url = fwo_config["user_management_api_base_url"]
+ fwo_globals.set_global_values(verify_certificates, suppress_certificate_warnings)
+ if suppress_certificate_warnings:
+ urllib3.disable_warnings()
+
+ FWOLogger.info("importer-main-loop starting ...")
+ if IMPORTER_BASE_DIR not in sys.path:
+ sys.path.append(IMPORTER_BASE_DIR)
+ importer_user_name = "importer" # TODO: move to config file?
+ importer_pwd_file = BASE_DIR + "/etc/secrets/importer_pwd"
# setting defaults (only as fallback if config defaults cannot be fetched via API):
- api_fetch_limit = 150
- sleep_timer = 90
- jwt = ""
- mgm_ids = []
-
- # read fwo config (API URLs)
- try:
- with open(fwo_config_filename, "r") as fwo_config:
- fwo_config = json.loads(fwo_config.read())
- user_management_api_base_url = fwo_config['middleware_uri']
- fwo_api_base_url = fwo_config['api_uri']
- except:
- logger.error("import-main-loop - error while reading FWO config file")
- raise
+ api_fetch_limit: int = 150
+ sleep_timer: int = 90
+
+ while True:
+ main_loop(
+ importer_pwd_file,
+ importer_user_name,
+ user_management_api_base_url,
+ fwo_api_base_url,
+ fwo_major_version,
+ api_fetch_limit,
+ sleep_timer,
+ clear,
+ force,
+ is_full_import,
+ )
+ if clear:
+ break
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(
+ description="Run import loop across all managements to read configuration from FW managements via API calls"
+ )
+ parser.add_argument(
+ "-d",
+ "--debug",
+ metavar="debug_level",
+ default="0",
+ help="Debug Level: 0=off, 1=send debug to console, 2=send debug to file, 3=keep temporary config files; default=0",
+ )
+ parser.add_argument("-v", "--verify_certificates", action="store_true", default=None, help="verify certificates")
+ parser.add_argument(
+ "-s", "--suppress_certificate_warnings", action="store_true", default=None, help="suppress certificate warnings"
+ )
+ parser.add_argument(
+ "-c",
+ "--clear",
+ action="store_true",
+ default=False,
+ help="If set all imports will run once to delete all data instead of importing",
+ )
+ parser.add_argument(
+ "-f",
+ "--force",
+ action="store_true",
+ default=False,
+ help="If set all imports will be run without checking for changes before",
+ )
+
+ args = parser.parse_args()
- mgm_details = {}
- killer = GracefulKiller()
- while not killer.kill_now:
- # authenticate to get JWT
- skipping = False
- try:
- with open(importer_pwd_file, 'r') as file:
- importer_pwd = file.read().replace('\n', '')
- except:
- logger.error("import-main-loop - error while reading importer pwd file")
- raise
-
- try:
- jwt = fwo_api.login(importer_user_name, importer_pwd, user_management_api_base_url)
- except FwoApiLoginFailed as e:
- logger.error(e.message)
- skipping = True
- except:
- logger.error("import-main-loop - Unspecified error while logging into FWO API: " + str(traceback.format_exc()))
- skipping = True
-
- requests.packages.urllib3.disable_warnings() # suppress ssl warnings only
- verify_certificates = fwo_api.get_config_value(fwo_api_base_url, jwt, key='importCheckCertificates')=='True'
- suppress_certificate_warnings = fwo_api.get_config_value(fwo_api_base_url, jwt, key='importSuppressCertificateWarnings')=='True'
- if not suppress_certificate_warnings:
- warnings.resetwarnings()
-
- if not skipping:
- try:
- mgm_ids = fwo_api.get_mgm_ids(fwo_api_base_url, jwt, {})
- except:
- logger.error("import-main-loop - error while getting FW management ids: " + str(traceback.format_exc()))
- skipping = True
-
- try:
- api_fetch_limit = fwo_api.get_config_value(fwo_api_base_url, jwt, key='fwApiElementsPerFetch')
- sleep_timer = fwo_api.get_config_value(fwo_api_base_url, jwt, key='importSleepTime')
- if api_fetch_limit == None:
- api_fetch_limit = 150
- if sleep_timer == None:
- sleep_timer = 90
- except:
- logger.debug("import-main-loop - could not get config values from FWO API - using default values")
-
- if not skipping:
- for mgm_id in mgm_ids:
- if killer.kill_now:
- break
- if 'id' not in mgm_id:
- logger.error("import-main-loop - did not get mgm_id: " + str(traceback.format_exc()))
- else:
- id = str(mgm_id['id'])
- # getting a new JWT in case the old one is not valid anymore after a long previous import
- try:
- jwt = fwo_api.login(importer_user_name, importer_pwd, user_management_api_base_url)
- except FwoApiLoginFailed as e:
- logger.error(e.message)
- skipping = True
- except:
- logger.error("import-main-loop - unspecified error during FWO API login - skipping: " + str(traceback.format_exc()))
- skipping = True
- if not skipping:
- try:
- mgm_details = fwo_api.get_mgm_details(fwo_api_base_url, jwt, {"mgmId": id})
- except:
- logger.error("import-main-loop - error while getting FW management details for mgm_id=" + str(id) + " - skipping: " + str(traceback.format_exc()))
- skipping = True
- if not skipping and mgm_details["deviceType"]["id"] in (9, 11, 17, 22, 23, 24): # only handle CPR8x Manager, fortiManager, Cisco MgmCenter, Palo Panorama, Palo FW, FortiOS REST
- logger.debug("import-main-loop: starting import of mgm_id=" + id)
- try:
- import_result = import_management(mgmId=id, debug_level_in=debug_level,
- clearManagementData=args.clear, force=args.force, limit=str(api_fetch_limit))
- except (FwoApiFailedLockImport, FwLoginFailed):
- pass # minor errors for a single mgm, go to next one
- except: # all other exceptions are logged here
- logger.error("import-main-loop - unspecific error while importing mgm_id=" + str(id) + ", " + str(traceback.format_exc()))
- if args.clear:
- break # while loop
- if not killer.kill_now:
- logger.info("import-main-loop.py: sleeping between loops for " + str(sleep_timer) + " seconds")
- counter=0
- while counter < int(sleep_timer) and not killer.kill_now:
- time.sleep(1)
- counter += 1
-
- # got break signal stopping background process for handling log locking
- # logLockerTask.stop()
- # logLockerTask.join()
- logger.info("importer-main-loop exited gracefully.")
+ main(
+ debug_level=int(args.debug),
+ verify_certificates=args.verify_certificates,
+ suppress_certificate_warnings=args.suppress_certificate_warnings,
+ clear=args.clear,
+ force=args.force,
+ )
diff --git a/roles/importer/files/importer/import-mgm.py b/roles/importer/files/importer/import-mgm.py
index 090d5962f2..5e3a8b704e 100755
--- a/roles/importer/files/importer/import-mgm.py
+++ b/roles/importer/files/importer/import-mgm.py
@@ -1,24 +1,125 @@
-#!/usr/bin/python3
-import sys, traceback
-from fwo_log import getFwoLogger
-import argparse
-import requests, requests.packages
-from common import importer_base_dir, import_management
-import fwo_globals, fwo_config
-sys.path.append(importer_base_dir)
-
-
-if __name__ == "__main__":
- parser = argparse.ArgumentParser(
- description='Read configuration from FW management via API calls')
- parser.add_argument('-m', '--mgmId', metavar='management_id',
- required=True, help='FWORCH DB ID of the management server to import')
- parser.add_argument('-c', '--clear', action='store_true', default=False,
- help='If set the import will delete all data for the given management instead of importing')
- parser.add_argument('-f', '--force', action='store_true', default=False,
- help='If set the import will be attempted without checking for changes or if the importer module is the one defined')
- parser.add_argument('-d', '--debug', metavar='debug_level', default='0',
- help='Debug Level: \
+import argparse # noqa: N999
+import sys
+import traceback
+import warnings
+
+import urllib3
+from common import IMPORTER_BASE_DIR, import_management
+from fwo_api import FwoApi
+from fwo_api_call import FwoApiCall
+from fwo_base import init_service_provider, register_global_state
+from fwo_const import BASE_DIR
+from fwo_exceptions import FwoApiLoginFailedError
+from fwo_log import FWOLogger
+from model_controllers.import_state_controller import ImportStateController
+
+if IMPORTER_BASE_DIR not in sys.path:
+ sys.path.append(IMPORTER_BASE_DIR)
+
+
+def get_fwo_jwt(import_user: str, import_pwd: str, user_management_api: str) -> str | None:
+ try:
+ return FwoApi.login(import_user, import_pwd, user_management_api)
+ except FwoApiLoginFailedError as e:
+ FWOLogger.error(e.message)
+ except Exception:
+ FWOLogger.error(
+ "import-main-loop - unspecified error during FWO API login - skipping: " + str(traceback.format_exc())
+ )
+
+
+def main(
+ mgm_id: int,
+ file: str | None = None,
+ debug_level: int = 0,
+ verify_certificates_default: bool = False,
+ force: bool = False,
+ limit: int = 150,
+ clear_management_data: bool = False,
+ suppress_certificate_warnings: bool = False,
+):
+ FWOLogger(debug_level)
+ FWOLogger.debug("debug level set to " + str(debug_level))
+
+ service_provider = init_service_provider()
+ fwo_config = service_provider.get_fwo_config()
+ verify_certificates = verify_certificates_default
+ fwo_api_base_url = fwo_config["fwo_api_base_url"]
+ fwo_major_version = fwo_config["fwo_major_version"]
+ user_management_api_base_url = fwo_config["user_management_api_base_url"]
+ if suppress_certificate_warnings:
+ urllib3.disable_warnings()
+
+ FWOLogger.info("import-mgm starting ...")
+ if IMPORTER_BASE_DIR not in sys.path:
+ sys.path.append(IMPORTER_BASE_DIR)
+
+ importer_user_name = "importer" # TODO: move to config file?
+ importer_pwd_file = BASE_DIR + "/etc/secrets/importer_pwd"
+
+ try:
+ importer_pwd = open(importer_pwd_file).read().replace("\n", "") # noqa: SIM115
+ except Exception:
+ FWOLogger.error("error while reading importer pwd file")
+ raise
+
+ jwt = get_fwo_jwt(importer_user_name, importer_pwd, user_management_api_base_url)
+ # check if login was successful - if not, wait and retry
+ if jwt is None:
+ FWOLogger.error("cannot proceed without successful login - exiting")
+ return
+
+ fwo_api = FwoApi(fwo_api_base_url, jwt)
+ fwo_api_call = FwoApiCall(fwo_api)
+
+ urllib3.disable_warnings() # suppress ssl warnings only
+ verify_certificates = fwo_api_call.get_config_value(key="importCheckCertificates") == "True"
+ suppress_certificate_warnings = fwo_api_call.get_config_value(key="importSuppressCertificateWarnings") == "True"
+ if not suppress_certificate_warnings:
+ warnings.resetwarnings()
+
+ import_state = ImportStateController.initialize_import(
+ mgm_id,
+ jwt,
+ suppress_certificate_warnings,
+ verify_certificates,
+ force,
+ fwo_major_version,
+ clear_management_data,
+ is_full_import=True,
+ )
+ register_global_state(import_state)
+
+ import_management(
+ mgm_id, fwo_api_call, verify_certificates, limit, clear_management_data, suppress_certificate_warnings, file
+ )
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(description="Read configuration from FW management via API calls")
+ parser.add_argument(
+ "-m", "--mgmId", metavar="management_id", required=True, help="FWORCH DB ID of the management server to import"
+ )
+ parser.add_argument(
+ "-c",
+ "--clear",
+ action="store_true",
+ default=False,
+ help="If set the import will delete all data for the given management instead of importing",
+ )
+ parser.add_argument(
+ "-f",
+ "--force",
+ action="store_true",
+ default=False,
+ help="If set the import will be attempted without checking for changes or if the importer module is the one defined",
+ )
+ parser.add_argument(
+ "-d",
+ "--debug",
+ metavar="debug_level",
+ default="0",
+ help="Debug Level: \
0=off, \
1=send debug to console, \
2=send debug to file, \
@@ -27,38 +128,42 @@
8=send native config (as read from firewall) to standard out, \
9=send normalized config to standard out, \
(default=0), \
- config files are saved to $FWORCH/tmp/import dir')
- parser.add_argument('-v', "--verify_certificates", action='store_true', default = None,
- help = "verify certificates")
- parser.add_argument('-s', "--suppress_certificate_warnings", action='store_true', default = None,
- help = "suppress certificate warnings")
- parser.add_argument('-l', '--limit', metavar='api_limit', default='150',
- help='The maximal number of returned results per HTTPS Connection; default=150')
- parser.add_argument('-i', '--in_file', metavar='config_file_input',
- help='if set, the config will not be fetched from firewall but read from json config (native or normalized) file specified here; may also be an url.')
+ config files are saved to $FWORCH/tmp/import dir",
+ )
+ parser.add_argument("-v", "--verify_certificates", action="store_true", default=None, help="verify certificates")
+ parser.add_argument(
+ "-s", "--suppress_certificate_warnings", action="store_true", default=None, help="suppress certificate warnings"
+ )
+ parser.add_argument(
+ "-l",
+ "--limit",
+ metavar="api_limit",
+ default="150",
+ help="The maximal number of returned results per HTTPS Connection; default=150",
+ )
+ parser.add_argument(
+ "-i",
+ "--in_file",
+ metavar="config_file_input",
+ help="if set, the config will not be fetched from firewall but read from json config (native or normalized) file specified here; may also be an url.",
+ )
args = parser.parse_args()
- if len(sys.argv) == 1:
- parser.print_help(sys.stderr)
- sys.exit(1)
-
- fwo_config = fwo_config.readConfig()
- fwo_globals.setGlobalValues(verify_certs_in=args.verify_certificates,
- suppress_cert_warnings_in=args.suppress_certificate_warnings,
- debug_level_in=args.debug)
- if args.suppress_certificate_warnings:
- requests.packages.urllib3.disable_warnings()
- logger = getFwoLogger()
try:
- error_count = import_management(
- mgmId=args.mgmId, in_file=args.in_file, debug_level_in=args.debug, ssl_verification=args.verify_certificates,
- force=args.force, limit=args.limit, clearManagementData=args.clear, suppress_cert_warnings_in=args.suppress_certificate_warnings)
- except SystemExit:
- logger.error("import-mgm - error while importing mgmId=" + str(args.mgmId) + ": " + str(traceback.format_exc()))
- error_count = 1
- except:
- logger.error("import-mgm - error while importing mgmId=" + str(args.mgmId) + ": " + str(traceback.format_exc()))
- error_count = 1
-
- sys.exit(error_count)
+ main(
+ int(args.mgmId), # TYPING: this should be snake case
+ args.in_file,
+ int(args.debug),
+ args.verify_certificates,
+ args.force,
+ int(args.limit),
+ args.clear,
+ args.suppress_certificate_warnings,
+ )
+ except Exception:
+ FWOLogger.error(
+ "import-mgm - error while importing mgmId=" + str(args.mgmId) + ": " + str(traceback.format_exc())
+ )
+
+ sys.exit()
diff --git a/roles/importer/files/importer/import-stop-helper b/roles/importer/files/importer/import-stop-helper
deleted file mode 100755
index 3d60311ac1..0000000000
--- a/roles/importer/files/importer/import-stop-helper
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/sh
-
-IMPORTER_SINGLE=fworch-importer-single.pl
-RES=`pidof -x $IMPORTER_SINGLE >/dev/null;echo $?`
-if [ $RES -eq 0 ]; then
- while [ $RES -eq 0 ]; do
- sleep 5
- RES=`pidof -x $IMPORTER_SINGLE >/dev/null;echo $?`
- done
-fi
diff --git a/roles/tests-integration/files/importer/anonymizer/use_current_version_from_github.txt b/roles/importer/files/importer/model_controllers/__init__.py
similarity index 100%
rename from roles/tests-integration/files/importer/anonymizer/use_current_version_from_github.txt
rename to roles/importer/files/importer/model_controllers/__init__.py
diff --git a/roles/importer/files/importer/model_controllers/check_consistency.py b/roles/importer/files/importer/model_controllers/check_consistency.py
new file mode 100644
index 0000000000..986850f3a4
--- /dev/null
+++ b/roles/importer/files/importer/model_controllers/check_consistency.py
@@ -0,0 +1,484 @@
+from typing import TYPE_CHECKING, Any
+
+import fwo_const
+from fwo_exceptions import FwoImporterErrorInconsistenciesError
+from fwo_log import FWOLogger
+from model_controllers.fwconfig_import import FwConfigImport
+from model_controllers.fwconfig_import_object import FwConfigImportObject
+from model_controllers.fwconfigmanager_controller import FwConfigManager
+from model_controllers.fwconfigmanagerlist_controller import FwConfigManagerListController
+from model_controllers.import_state_controller import ImportStateController
+from models.fwconfig_normalized import FwConfigNormalized
+from models.gateway import Gateway
+from models.rulebase import Rulebase
+from models.rulebase_link import RulebaseLinkUidBased
+from services.service_provider import ServiceProvider
+
+if TYPE_CHECKING:
+ from models.networkobject import NetworkObject
+
+
+# this class is used for importing a config into the FWO API
+class FwConfigImportCheckConsistency(FwConfigImport):
+ issues: dict[str, Any]
+ maps: FwConfigImportObject # = FwConfigImportObject()
+ config: FwConfigNormalized = FwConfigNormalized()
+
+ # merges all configs in the set together to prepare for consistency checks
+ def __init__(self, import_details: ImportStateController, config_list: FwConfigManagerListController):
+ service_provider = ServiceProvider()
+ self._global_state = service_provider.get_global_state()
+ self.import_state = import_details
+ self.issues = {}
+
+ self.maps = FwConfigImportObject() # TODO: don't use like this (separation of concerns) - see #3154
+ for mgr in config_list.ManagerSet:
+ for cfg in mgr.configs:
+ import_worker = FwConfigImport()
+ self.config.merge(cfg)
+ self.maps.network_object_type_map.update(import_worker.fwconfig_import_object.network_object_type_map)
+ self.maps.service_object_type_map.update(import_worker.fwconfig_import_object.service_object_type_map)
+ self.maps.user_object_type_map.update(import_worker.fwconfig_import_object.user_object_type_map)
+
+ # pre-flight checks
+ def check_config_consistency(self, config: FwConfigManagerListController):
+ self.check_color_consistency(config, fix=True)
+ self.check_network_object_consistency(config)
+ self.check_service_object_consistency(config)
+ self.check_user_object_consistency(config)
+ self.check_zone_object_consistency(config)
+ self.check_rulebase_consistency(config)
+ self.check_gateway_consistency(config)
+ self.check_rulebase_link_consistency(config)
+
+ if len(self.issues) > 0:
+ raise FwoImporterErrorInconsistenciesError(
+ "Inconsistencies found in the configuration: " + str(self.issues)
+ )
+
+ FWOLogger.debug("Consistency check completed without issues.")
+
+ def check_network_object_consistency(self, config: FwConfigManagerListController):
+ # check if all uid refs are valid
+ global_objects: set[str] = set()
+ single_config: FwConfigNormalized
+
+ # add all new obj refs from all rules
+ for mgr in sorted(config.ManagerSet, key=lambda m: not getattr(m, "IsSuperManager", False)):
+ if mgr.is_super_manager:
+ global_objects = config.get_all_network_object_uids(mgr.manager_uid)
+ all_used_obj_refs: list[str] = []
+ for single_config in mgr.configs:
+ for rb in single_config.rulebases:
+ all_used_obj_refs += self._collect_all_used_objects_from_rules(rb)
+
+ all_used_obj_refs += self._collect_all_used_objects_from_groups(single_config)
+
+ # now make list unique and get all refs not contained in network_objects
+ unresolvable_nw_obj_refs = (
+ set(all_used_obj_refs) - config.get_all_network_object_uids(mgr.manager_uid) - global_objects
+ )
+ if len(unresolvable_nw_obj_refs) > 0:
+ self.issues.update({"unresolvableNwObRefs": list(unresolvable_nw_obj_refs)})
+
+ self._check_network_object_types_exist(mgr)
+ self._check_objects_with_missing_ips(mgr)
+
+ def _check_network_object_types_exist(self, mgr: FwConfigManager):
+ all_used_obj_types: set[str] = set()
+
+ for single_config in mgr.configs:
+ for obj_id in single_config.network_objects:
+ all_used_obj_types.add(single_config.network_objects[obj_id].obj_typ)
+ missing_nw_obj_types = all_used_obj_types - self.maps.network_object_type_map.keys()
+ if len(missing_nw_obj_types) > 0:
+ self.issues.update({"unresolvableNwObjTypes": list(missing_nw_obj_types)})
+
+ @staticmethod
+ def _collect_all_used_objects_from_groups(single_config: FwConfigNormalized) -> list[str]:
+ all_used_obj_refs: list[str] = []
+ # add all nw obj refs from groups
+ for obj_id in single_config.network_objects:
+ if single_config.network_objects[obj_id].obj_typ == "group":
+ obj_member_refs = single_config.network_objects[obj_id].obj_member_refs
+ if obj_member_refs is not None and len(obj_member_refs) > 0:
+ all_used_obj_refs += obj_member_refs.split(fwo_const.LIST_DELIMITER)
+ return all_used_obj_refs
+
+ def _collect_all_used_objects_from_rules(self, rb: Rulebase) -> list[str]:
+ all_used_obj_refs: list[str] = []
+ for rule_id in rb.rules:
+ all_used_obj_refs += rb.rules[rule_id].rule_src_refs.split(fwo_const.LIST_DELIMITER)
+ all_used_obj_refs += rb.rules[rule_id].rule_dst_refs.split(fwo_const.LIST_DELIMITER)
+
+ return all_used_obj_refs
+
+ def _check_objects_with_missing_ips(self, single_config: FwConfigManager):
+ # check if there are any objects with obj_typ<>group and empty ip addresses (breaking constraint)
+ non_group_nw_obj_with_missing_ips: list[NetworkObject] = []
+ for conf in single_config.configs:
+ for obj_id in conf.network_objects:
+ if conf.network_objects[obj_id].obj_typ != "group":
+ ip1 = conf.network_objects[obj_id].obj_ip
+ ip2 = conf.network_objects[obj_id].obj_ip_end
+ if ip1 is None or ip2 is None:
+ non_group_nw_obj_with_missing_ips.append(conf.network_objects[obj_id])
+ if len(non_group_nw_obj_with_missing_ips) > 0:
+ self.issues.update(
+ {"non-group network object with undefined IP addresse(s)": list(non_group_nw_obj_with_missing_ips)}
+ )
+
+ def check_service_object_consistency(self, config: FwConfigManagerListController):
+ # check if all uid refs are valid
+ global_objects: set[str] = set()
+
+ for mgr in sorted(config.ManagerSet, key=lambda m: not getattr(m, "IsSuperManager", False)):
+ if len(mgr.configs) == 0:
+ continue
+ if mgr.is_super_manager:
+ global_objects = config.get_all_service_object_uids(mgr.manager_uid)
+ all_used_obj_refs: set[str] = set()
+ for single_config in mgr.configs:
+ all_used_obj_refs |= self._collect_service_object_refs_from_rules(single_config)
+ all_used_obj_refs |= self._collect_all_service_object_refs_from_groups(single_config)
+ self._check_service_object_types_exist(single_config)
+ # now make list unique
+ all_used_obj_refs = set(all_used_obj_refs)
+ # and get all refs not contained in serivce_objects
+
+ unresolvable_obj_refs = (
+ all_used_obj_refs - config.get_all_service_object_uids(mgr.manager_uid) - global_objects
+ )
+ if len(unresolvable_obj_refs) > 0:
+ self.issues.update({"unresolvableSvcObRefs": list(unresolvable_obj_refs)})
+
+ def _check_service_object_types_exist(self, single_config: FwConfigNormalized):
+ # check that all obj_typ exist
+ all_used_obj_types: set[str] = set()
+ for obj_id in single_config.service_objects:
+ all_used_obj_types.add(single_config.service_objects[obj_id].svc_typ)
+ missing_obj_types = list(all_used_obj_types) - self.maps.service_object_type_map.keys()
+ if len(missing_obj_types) > 0:
+ self.issues.update({"unresolvableSvcObjTypes": list(missing_obj_types)})
+
+ @staticmethod
+ def _collect_all_service_object_refs_from_groups(single_config: FwConfigNormalized) -> set[str]:
+ all_used_obj_refs: set[str] = set()
+ for obj_id in single_config.service_objects:
+ if (
+ single_config.service_objects[obj_id].svc_typ == "group"
+ and single_config.service_objects[obj_id].svc_member_refs is not None
+ ):
+ member_refs = single_config.service_objects[obj_id].svc_member_refs
+ if member_refs is None or len(member_refs) == 0:
+ continue
+ all_used_obj_refs |= set(member_refs.split(fwo_const.LIST_DELIMITER))
+ return all_used_obj_refs
+
+ @staticmethod
+ def _collect_service_object_refs_from_rules(single_config: FwConfigNormalized) -> set[str]:
+ all_used_obj_refs: set[str] = set()
+ for rb in single_config.rulebases:
+ for rule_id in rb.rules:
+ all_used_obj_refs |= set(rb.rules[rule_id].rule_svc_refs.split(fwo_const.LIST_DELIMITER))
+ return all_used_obj_refs
+
+ def check_user_object_consistency(self, config: FwConfigManagerListController):
+ global_objects: set[str] = set()
+ # add all user refs from all rules
+ for mgr in sorted(config.ManagerSet, key=lambda m: not getattr(m, "IsSuperManager", False)):
+ all_used_obj_refs: list[str] = []
+ if mgr.is_super_manager:
+ global_objects = config.get_all_user_object_uids(mgr.manager_uid)
+ for single_config in mgr.configs:
+ all_used_obj_refs += self._collect_users_from_rules(single_config)
+ self._collect_users_from_groups(single_config, all_used_obj_refs)
+ self._check_user_types_exist(single_config)
+
+ # now make list unique and get all refs not contained in users
+ unresolvable_obj_refs = (
+ set(all_used_obj_refs) - config.get_all_user_object_uids(mgr.manager_uid) - global_objects
+ )
+ if len(unresolvable_obj_refs) > 0:
+ self.issues.update({"unresolvableUserObjRefs": list(unresolvable_obj_refs)})
+
+ def _collect_users_from_rules(self, single_config: FwConfigNormalized) -> list[str]:
+ all_used_obj_refs: list[str] = []
+ for rb in single_config.rulebases:
+ for rule_id in rb.rules:
+ if fwo_const.USER_DELIMITER in rb.rules[rule_id].rule_src_refs:
+ all_used_obj_refs += self._collect_users_from_rule(
+ rb.rules[rule_id].rule_src_refs.split(fwo_const.LIST_DELIMITER)
+ )
+ all_used_obj_refs += self._collect_users_from_rule(
+ rb.rules[rule_id].rule_dst_refs.split(fwo_const.LIST_DELIMITER)
+ )
+ return all_used_obj_refs
+
+ def _collect_users_from_groups(self, _single_config: FwConfigNormalized, _all_used_obj_refs: list[str]):
+ return
+
+ def _check_user_types_exist(self, single_config: FwConfigNormalized):
+ # check that all obj_typ exist
+ all_used_obj_types: set[str] = set()
+ for obj_id in single_config.users:
+ all_used_obj_types.add(single_config.users[obj_id].user_typ) # make list unique
+ missing_obj_types = (
+ list(set(all_used_obj_types)) - self.maps.user_object_type_map.keys()
+ ) # TODO: why list(set())?
+ if len(missing_obj_types) > 0:
+ self.issues.update({"unresolvableUserObjTypes": list(missing_obj_types)})
+
+ @staticmethod
+ def _collect_users_from_rule(list_of_elements: list[str]) -> list[str]:
+ user_refs: list[str] = []
+ for el in list_of_elements:
+ split_result = el.split(fwo_const.USER_DELIMITER)
+ if len(split_result) == 2: # noqa: PLR2004
+ user_refs.append(split_result[0])
+ return user_refs
+
+ def check_zone_object_consistency(self, config: FwConfigManagerListController):
+ global_objects: set[str] = set()
+ for mgr in sorted(config.ManagerSet, key=lambda m: not getattr(m, "IsSuperManager", False)):
+ if len(mgr.configs) == 0:
+ continue
+ if mgr.is_super_manager:
+ global_objects = config.get_all_zone_names(mgr.manager_uid)
+
+ all_used_obj_refs: set[str] = set()
+ for single_config in mgr.configs:
+ all_used_obj_refs |= self._collect_zone_refs_from_rules(single_config)
+ # now make list unique
+ all_used_obj_refs = set(all_used_obj_refs)
+
+ # and get all refs not contained in zone_objects
+ unresolvable_object_refs = all_used_obj_refs - config.get_all_zone_names(mgr.manager_uid) - global_objects
+ if len(unresolvable_object_refs) > 0:
+ self.issues.update({"unresolvableZoneObRefs": list(unresolvable_object_refs)})
+
+ @staticmethod
+ def _collect_zone_refs_from_rules(single_config: FwConfigNormalized) -> set[str]:
+ all_used_zones_refs: set[str] = set()
+ for rb in single_config.rulebases:
+ for rule_id in rb.rules:
+ rule = rb.rules[rule_id]
+ if rule.rule_src_zone is not None:
+ all_used_zones_refs.update(rule.rule_src_zone.split(fwo_const.LIST_DELIMITER))
+ if rule.rule_dst_zone is not None:
+ all_used_zones_refs.update(rule.rule_dst_zone.split(fwo_const.LIST_DELIMITER))
+ return all_used_zones_refs
+
+ # check if all color refs are valid (in the DB)
+ # fix=True means that missing color refs will be replaced by the default color (black)
+ def check_color_consistency(self, config: FwConfigManagerListController, fix: bool = True):
+ self.import_state.set_color_ref_map()
+
+ # collect all colors
+
+ for mgr in config.ManagerSet:
+ for single_config in mgr.configs:
+ all_used_nw_obj_color_ref_set, all_used_svc_color_ref_set, all_used_user_color_ref_set = (
+ self._collect_all_used_colors(single_config)
+ )
+
+ unresolvable_nw_obj_colors, unresolvable_svc_colors, unresolvable_user_colors = (
+ self._check_resolvability_of_used_colors(
+ all_used_nw_obj_color_ref_set, all_used_svc_color_ref_set, all_used_user_color_ref_set
+ )
+ )
+
+ if fix:
+ self._fix_colors(
+ single_config, unresolvable_nw_obj_colors, unresolvable_svc_colors, unresolvable_user_colors
+ )
+ elif (
+ len(unresolvable_nw_obj_colors) > 0
+ or len(unresolvable_svc_colors) > 0
+ or len(unresolvable_user_colors) > 0
+ ):
+ self.issues.update(
+ {
+ "unresolvableColorRefs": {
+ "nwObjColors": unresolvable_nw_obj_colors,
+ "svcColors": unresolvable_svc_colors,
+ "userColors": unresolvable_user_colors,
+ }
+ }
+ )
+
+ @staticmethod
+ def _collect_all_used_colors(single_config: FwConfigNormalized):
+ all_used_nw_obj_color_ref_set: set[str] = set()
+ all_used_svc_color_ref_set: set[str] = set()
+ all_used_user_color_ref_set: set[str] = set()
+
+ for uid in single_config.network_objects:
+ if single_config.network_objects[uid].obj_color is not None: # type: ignore #TODO: obj_color cant be None # noqa: PGH003
+ all_used_nw_obj_color_ref_set.add(single_config.network_objects[uid].obj_color)
+ for uid in single_config.service_objects:
+ if single_config.service_objects[uid].svc_color is not None: # type: ignore #TODO: svc_color cant be None # noqa: PGH003
+ all_used_svc_color_ref_set.add(single_config.service_objects[uid].svc_color)
+ for uid in single_config.users:
+ if single_config.users[uid].user_color is not None:
+ all_used_user_color_ref_set.add(single_config.users[uid].user_color)
+
+ return all_used_nw_obj_color_ref_set, all_used_svc_color_ref_set, all_used_user_color_ref_set
+
+ def _check_resolvability_of_used_colors(
+ self,
+ all_used_nw_obj_color_ref_set: set[str],
+ all_used_svc_color_ref_set: set[str],
+ all_used_user_color_ref_set: set[str],
+ ):
+ unresolvable_nw_obj_colors: list[str] = []
+ unresolvable_svc_colors: list[str] = []
+ unresolvable_user_colors: list[str] = []
+ # check all nwobj color refs
+ for color_string in all_used_nw_obj_color_ref_set:
+ color_id = self.import_state.state.lookup_color_id(color_string)
+ if color_id is None: # type: ignore # TODO: lookupColorId cant return None # noqa: PGH003
+ unresolvable_nw_obj_colors.append(color_string)
+
+ # check all nwobj color refs
+ for color_string in all_used_svc_color_ref_set:
+ color_id = self.import_state.state.lookup_color_id(color_string)
+ if color_id is None: # type: ignore # TODO: lookupColorId cant return None # noqa: PGH003
+ unresolvable_svc_colors.append(color_string)
+
+ # check all user color refs
+ for color_string in all_used_user_color_ref_set:
+ color_id = self.import_state.state.lookup_color_id(color_string)
+ if color_id is None: # type: ignore # TODO: lookupColorId cant return None # noqa: PGH003
+ unresolvable_user_colors.append(color_string)
+
+ return unresolvable_nw_obj_colors, unresolvable_svc_colors, unresolvable_user_colors
+
+ @staticmethod
+ def _fix_colors(
+ config: FwConfigNormalized,
+ unresolvable_nw_obj_colors: list[str],
+ unresolvable_svc_colors: list[str],
+ unresolvable_user_colors: list[str],
+ ):
+ # Replace unresolvable network object colors
+ for obj in config.network_objects.values():
+ if obj.obj_color in unresolvable_nw_obj_colors:
+ obj.obj_color = fwo_const.DEFAULT_COLOR
+ # Replace unresolvable service object colors
+ for obj in config.service_objects.values():
+ if obj.svc_color in unresolvable_svc_colors:
+ obj.svc_color = fwo_const.DEFAULT_COLOR
+ # Replace unresolvable user object colors
+ for obj in config.users.values():
+ if obj.user_color in unresolvable_user_colors:
+ obj.user_color = fwo_const.DEFAULT_COLOR
+
+ @staticmethod
+ def _extract_rule_track_n_action_refs(rulebases: list[Rulebase]) -> tuple[list[str], list[str]]:
+ track_refs: list[str] = []
+ action_refs: list[str] = []
+ for rb in rulebases:
+ track_refs.extend(rule.rule_track for rule in rb.rules.values())
+ action_refs.extend(rule.rule_action for rule in rb.rules.values())
+ return track_refs, action_refs
+
+ def check_rulebase_consistency(self, config: FwConfigManagerListController):
+ all_used_track_refs: list[str] = []
+ all_used_action_refs: list[str] = []
+
+ for mgr in config.ManagerSet:
+ for single_config in mgr.configs:
+ track_refs, action_refs = self._extract_rule_track_n_action_refs(single_config.rulebases)
+ all_used_track_refs.extend(track_refs)
+ all_used_action_refs.extend(action_refs)
+
+ all_used_track_refs = list(set(all_used_track_refs))
+ all_used_action_refs = list(set(all_used_action_refs))
+
+ unresolvable_tracks = all_used_track_refs - self.import_state.state.tracks.keys()
+ if unresolvable_tracks:
+ self.issues.update({"unresolvableRuleTracks": list(unresolvable_tracks)})
+
+ unresolvable_actions = all_used_action_refs - self.import_state.state.actions.keys()
+ if unresolvable_actions:
+ self.issues.update({"unresolvableRuleActions": list(unresolvable_actions)})
+
+ # e.g. check routing, interfaces refs
+ def check_gateway_consistency(self, config: FwConfigManagerListController):
+ # TODO: implement
+ pass
+
+ # e.g. check rule to rule refs
+ # TODO: check if the rule & rulebases referenced belong to either
+ # - the same submanager or
+ # - the super manager but not another sub manager
+ def check_rulebase_link_consistency(self, config: FwConfigManagerListController):
+ broken_rulebase_links: list[dict[str, Any]] = []
+
+ all_rulebase_uids, all_rule_uids = self._collect_uids(config)
+
+ # check consistency of links
+ for mgr in config.ManagerSet:
+ if self.import_state.state.mgm_details.import_disabled:
+ continue
+ for single_config in mgr.configs:
+ # now check rblinks for all gateways
+ for gw in single_config.gateways:
+ self._check_rulebase_links_for_gateway(gw, broken_rulebase_links, all_rule_uids, all_rulebase_uids)
+
+ if len(broken_rulebase_links) > 0:
+ self.issues.update({"brokenRulebaseLinks": broken_rulebase_links})
+
+ def _check_rulebase_links_for_gateway(
+ self,
+ gw: Gateway,
+ broken_rulebase_links: list[dict[str, Any]],
+ all_rule_uids: set[str],
+ all_rulebase_uids: set[str],
+ ):
+ if not gw.ImportDisabled:
+ for rbl in gw.RulebaseLinks:
+ self._check_rulebase_link(gw, rbl, broken_rulebase_links, all_rule_uids, all_rulebase_uids)
+
+ def _collect_uids(self, config: FwConfigManagerListController):
+ all_rulebase_uids: set[str] = set()
+ all_rule_uids: set[str] = set()
+ for mgr in config.ManagerSet:
+ if self.import_state.state.mgm_details.import_disabled:
+ continue
+ for single_config in mgr.configs:
+ # collect rulebase UIDs
+ for rb in single_config.rulebases:
+ all_rulebase_uids.add(rb.uid)
+ # collect rule UIDs
+ for rule_uid in rb.rules:
+ all_rule_uids.add(rule_uid)
+ return all_rulebase_uids, all_rule_uids
+
+ def _check_rulebase_link(
+ self,
+ gw: Gateway,
+ rbl: RulebaseLinkUidBased,
+ broken_rulebase_links: list[dict[str, Any]],
+ all_rule_uids: set[str],
+ all_rulebase_uids: set[str],
+ ):
+ if (
+ rbl.from_rulebase_uid is not None
+ and rbl.from_rulebase_uid != ""
+ and rbl.from_rulebase_uid not in all_rulebase_uids
+ ):
+ self._add_issue(broken_rulebase_links, rbl, gw, "from_rulebase_uid broken")
+ if rbl.to_rulebase_uid != "" and rbl.to_rulebase_uid not in all_rulebase_uids:
+ self._add_issue(broken_rulebase_links, rbl, gw, "to_rulebase_uid broken")
+ if rbl.from_rule_uid is not None and rbl.from_rule_uid != "" and rbl.from_rule_uid not in all_rule_uids:
+ self._add_issue(broken_rulebase_links, rbl, gw, "from_rule_uid broken")
+
+ @staticmethod
+ def _add_issue(broken_rulebase_links: list[dict[str, Any]], rbl: RulebaseLinkUidBased, gw: Gateway, error_txt: str):
+ rbl_dict = rbl.to_dict()
+ rbl_dict.update({"error": error_txt})
+ rbl_dict.update({"gw": f"{gw.Name} ({gw.Uid})"})
+ if rbl_dict not in broken_rulebase_links:
+ broken_rulebase_links.append(rbl_dict)
diff --git a/roles/importer/files/importer/model_controllers/fwconfig_import.py b/roles/importer/files/importer/model_controllers/fwconfig_import.py
new file mode 100644
index 0000000000..a075b1a26a
--- /dev/null
+++ b/roles/importer/files/importer/model_controllers/fwconfig_import.py
@@ -0,0 +1,382 @@
+import traceback
+from typing import Any
+
+import fwo_const
+import fwo_globals
+from fwo_api import FwoApi
+from fwo_api_call import FwoApiCall
+from fwo_base import ConfigAction, find_all_diffs
+from fwo_exceptions import FwoApiFailedDeleteOldImportsError, FwoImporterError, ImportInterruptionError
+from fwo_log import FWOLogger
+from model_controllers.fwconfig_import_gateway import FwConfigImportGateway
+from model_controllers.fwconfig_import_object import FwConfigImportObject
+from model_controllers.fwconfig_import_rule import FwConfigImportRule
+from model_controllers.fwconfigmanagerlist_controller import FwConfigManagerListController
+from model_controllers.import_state_controller import ImportStateController
+from model_controllers.management_controller import (
+ ConnectionInfo,
+ CredentialInfo,
+ DeviceInfo,
+ DomainInfo,
+ ManagementController,
+ ManagerInfo,
+)
+from model_controllers.rule_enforced_on_gateway_controller import RuleEnforcedOnGatewayController
+from models.fwconfig_normalized import FwConfigNormalized
+from models.fwconfigmanagerlist import FwConfigManager
+from services.global_state import GlobalState
+from services.service_provider import ServiceProvider
+
+
+# this class is used for importing a config into the FWO API
+class FwConfigImport:
+ import_state: ImportStateController
+ normalized_config: FwConfigNormalized | None
+
+ _fw_config_import_rule: FwConfigImportRule
+ _fw_config_import_object: FwConfigImportObject
+ _fw_config_import_gateway: FwConfigImportGateway
+ _global_state: GlobalState
+
+ @property
+ def fwconfig_import_object(self):
+ return self._fw_config_import_object
+
+ def __init__(self):
+ service_provider = ServiceProvider()
+ self._global_state = service_provider.get_global_state()
+ self.import_state = self._global_state.import_state
+
+ self.normalized_config = self._global_state.normalized_config
+
+ self._fw_config_import_object = FwConfigImportObject()
+ self._fw_config_import_rule = FwConfigImportRule()
+ self._fw_config_import_gateway = FwConfigImportGateway()
+
+ def import_single_config(self, single_manager: FwConfigManager):
+ # current implementation restriction: assuming we always get the full config (only inserts) from API
+ mgm_id = self.import_state.state.lookup_management_id(single_manager.manager_uid)
+ if mgm_id is None:
+ raise FwoImporterError(f"could not find manager id in DB for UID {single_manager.manager_uid}")
+ previous_config = self.get_latest_config_from_db()
+ self._global_state.previous_config = previous_config
+ if single_manager.is_super_manager:
+ self._global_state.previous_global_config = previous_config
+
+ # calculate differences and write them to the database via API
+ self.update_diffs(previous_config, self._global_state.previous_global_config, single_manager)
+
+ def import_management_set(self, service_provider: ServiceProvider, mgr_set: FwConfigManagerListController):
+ for manager in sorted(mgr_set.ManagerSet, key=lambda m: not getattr(m, "IsSuperManager", False)):
+ """
+ the following loop is a preparation for future functionality
+ we might add support for multiple configs per manager
+ e.g. one config only adds data, one only deletes data, etc.
+ currently we always only have one config per manager
+ """
+ for config in manager.configs:
+ self.import_config(service_provider, manager, config)
+
+ def import_config(self, service_provider: ServiceProvider, manager: FwConfigManager, config: FwConfigNormalized):
+ global_state = service_provider.get_global_state()
+ global_state.normalized_config = config
+ if manager.is_super_manager:
+ # store global config as it is needed when importing sub managers which might reference it
+ global_state.global_normalized_config = config
+ mgm_id = self.import_state.state.lookup_management_id(manager.manager_uid)
+ if mgm_id is None:
+ raise FwoImporterError(f"could not find manager id in DB for UID {manager.manager_uid}")
+ # TODO: clean separation between values relevant for all managers and those only relevant for specific managers - see #3646
+ self.import_state.state.mgm_details.current_mgm_id = mgm_id
+ self.import_state.state.mgm_details.current_mgm_is_super_manager = manager.is_super_manager
+ config_importer = FwConfigImport() # TODO: strange to create another import object here - see #3154
+ config_importer.import_single_config(manager)
+ config_importer.consistency_check_db()
+ config_importer.write_latest_config()
+
+ def clear_management(self) -> FwConfigManagerListController:
+ FWOLogger.info('this import run will reset the configuration of this management to "empty"')
+ config_normalized = FwConfigManagerListController()
+ mgm_details = self.import_state.state.mgm_details
+ # Reset management
+ config_normalized.add_manager(
+ manager=FwConfigManager(
+ manager_uid=mgm_details.calc_manager_uid_hash(),
+ manager_name=mgm_details.name,
+ is_super_manager=mgm_details.is_super_manager,
+ sub_manager_ids=mgm_details.sub_manager_ids,
+ domain_name=mgm_details.domain_name,
+ domain_uid=mgm_details.domain_uid,
+ configs=[],
+ )
+ )
+ if len(self.import_state.state.mgm_details.sub_manager_ids) > 0:
+ # Read config
+ fwo_api = self.import_state.api_connection
+ _ = FwoApiCall(fwo_api) # TODO: why not used ??
+
+ # Reset submanagement
+ for sub_manager_id in self.import_state.state.mgm_details.sub_manager_ids:
+ # Fetch sub management details
+ mgm_controller = ManagementController(
+ mgm_id=int(sub_manager_id),
+ uid="",
+ devices=[],
+ device_info=DeviceInfo(),
+ connection_info=ConnectionInfo(),
+ importer_hostname="",
+ credential_info=CredentialInfo(),
+ manager_info=ManagerInfo(),
+ domain_info=DomainInfo(),
+ )
+ mgm_details_raw = mgm_controller.get_mgm_details(fwo_api, sub_manager_id)
+ mgm_details = ManagementController.from_json(mgm_details_raw)
+ config_normalized.add_manager(
+ manager=FwConfigManager(
+ manager_uid=ManagementController.calc_manager_uid_hash(mgm_details_raw), # type: ignore # TODO: check: should be mgm_details # noqa: PGH003
+ manager_name=mgm_details.name,
+ is_super_manager=mgm_details.is_super_manager,
+ sub_manager_ids=mgm_details.sub_manager_ids,
+ domain_name=mgm_details.domain_name,
+ domain_uid=mgm_details.domain_uid,
+ configs=[],
+ )
+ )
+ # Reset objects
+ for management in config_normalized.ManagerSet:
+ management.configs.append(
+ FwConfigNormalized(
+ action=ConfigAction.INSERT,
+ network_objects={},
+ service_objects={},
+ users={},
+ zone_objects={},
+ rulebases=[],
+ gateways=[],
+ )
+ )
+ self.import_state.state.is_clearing_import = True # the now following import is a full one
+
+ return config_normalized
+
+ def update_diffs(
+ self,
+ prev_config: FwConfigNormalized,
+ prev_global_config: FwConfigNormalized | None,
+ single_manager: FwConfigManager,
+ ):
+ self._fw_config_import_object.update_object_diffs(prev_config, prev_global_config, single_manager)
+
+ if fwo_globals.shutdown_requested:
+ raise ImportInterruptionError("Shutdown requested during updateObjectDiffs.")
+
+ new_rule_ids = self._fw_config_import_rule.update_rulebase_diffs(prev_config)
+
+ if fwo_globals.shutdown_requested:
+ raise ImportInterruptionError("Shutdown requested during updateRulebaseDiffs.")
+
+ self.import_state.set_rule_map() # update all rule entries (from currently running import for rulebase_links)
+ self._fw_config_import_gateway.update_gateway_diffs()
+
+ # get new rules details from API (for obj refs as well as enforcing gateways)
+ new_rules = self._fw_config_import_rule.get_rules_by_id_with_ref_uids(new_rule_ids)
+
+ RuleEnforcedOnGatewayController().add_new_rule_enforced_on_gateway_refs(
+ new_rules, self.import_state.state, self.import_state.api_call, self.import_state.state.stats
+ )
+
+ # cleanup configs which do not need to be retained according to data retention time
+ def delete_old_imports(self) -> None:
+ mgm_id = int(self.import_state.state.mgm_details.mgm_id)
+ delete_mutation = FwoApi.get_graphql_code([fwo_const.GRAPHQL_QUERY_PATH + "import/deleteOldImports.graphql"])
+
+ try:
+ delete_result = self.import_state.api_call.call(
+ delete_mutation,
+ query_variables={"mgmId": mgm_id, "is_full_import": self.import_state.state.is_full_import},
+ )
+ if delete_result["data"]["delete_import_control"]["returning"]["control_id"]:
+ imports_deleted = len(delete_result["data"]["delete_import_control"]["returning"]["control_id"])
+ if imports_deleted > 0:
+ FWOLogger.info(
+ f"deleted {imports_deleted!s} imports which passed the retention time of {self.import_state.state.data_retention_days} days"
+ )
+ except Exception:
+ fwo_api_call = self.import_state.api_call
+ FWOLogger.error(
+ f"error while trying to delete old imports for mgm {self.import_state.state.mgm_details.mgm_id!s}"
+ )
+ fwo_api_call.create_data_issue(
+ mgm_id=self.import_state.state.mgm_details.mgm_id,
+ severity=1,
+ description="failed to get import lock for management id " + str(mgm_id),
+ )
+ fwo_api_call.set_alert(
+ import_id=self.import_state.state.import_id,
+ title="import error",
+ mgm_id=mgm_id,
+ severity=1,
+ description="fwo_api: failed to get import lock",
+ source="import",
+ alert_code=15,
+ mgm_details=self.import_state.state.mgm_details,
+ )
+ raise FwoApiFailedDeleteOldImportsError(f"management id: {mgm_id}") from None
+
+ def write_latest_config(self):
+ if self.import_state.state.import_version > 8: # noqa: PLR2004
+ if self.normalized_config is None:
+ raise FwoImporterError("cannot write latest config: NormalizedConfig is None")
+ # convert FwConfigImport to FwConfigNormalized
+ self.normalized_config = FwConfigNormalized(
+ action=self.normalized_config.action,
+ network_objects=self.normalized_config.network_objects,
+ service_objects=self.normalized_config.service_objects,
+ users=self.normalized_config.users,
+ zone_objects=self.normalized_config.zone_objects,
+ rulebases=self.normalized_config.rulebases,
+ gateways=self.normalized_config.gateways,
+ ConfigFormat=self.normalized_config.ConfigFormat,
+ )
+
+ self.delete_latest_config_of_management()
+ insert_mutation = FwoApi.get_graphql_code(
+ [fwo_const.GRAPHQL_QUERY_PATH + "import/storeLatestConfig.graphql"]
+ )
+ try:
+ query_variables: dict[str, Any] = {
+ "mgmId": self.import_state.state.mgm_details.current_mgm_id,
+ "importId": self.import_state.state.import_id,
+ "config": self.normalized_config.model_dump_json(),
+ }
+ import_result = self.import_state.api_call.call(insert_mutation, query_variables=query_variables)
+ if "errors" in import_result:
+ FWOLogger.exception(
+ "fwo_api:storeLatestConfig - error while writing importable config for mgm id "
+ + str(self.import_state.state.mgm_details.current_mgm_id)
+ + ": "
+ + str(import_result["errors"])
+ )
+ FWOLogger.warning(
+ f"error while writing latest config for import_id {self.import_state.state.import_id}, mgm_id: {self.import_state.state.mgm_details.mgm_id}, mgm_uid: {self.import_state.state.mgm_details.uid}"
+ )
+ else:
+ _ = import_result["data"]["insert_latest_config"]["affected_rows"]
+ except Exception:
+ FWOLogger.exception(
+ f"failed to write latest normalized config for mgm id {self.import_state.state.mgm_details.current_mgm_id!s}: {traceback.format_exc()!s}"
+ )
+ raise
+
+ def delete_latest_config_of_management(self):
+ delete_mutation = FwoApi.get_graphql_code(
+ [fwo_const.GRAPHQL_QUERY_PATH + "import/deleteLatestConfigOfManagement.graphql"]
+ )
+ try:
+ query_variables = {"mgmId": self.import_state.state.mgm_details.current_mgm_id}
+ import_result = self.import_state.api_call.call(delete_mutation, query_variables=query_variables)
+ if "errors" in import_result:
+ FWOLogger.exception(
+ "fwo_api:import_latest_config - error while deleting last config for mgm id "
+ + str(self.import_state.state.mgm_details.current_mgm_id)
+ + ": "
+ + str(import_result["errors"])
+ )
+ else:
+ _ = import_result["data"]["delete_latest_config"]["affected_rows"]
+ except Exception:
+ FWOLogger.exception(
+ f"failed to delete latest normalized config for mgm id {self.import_state.state.mgm_details.current_mgm_id!s}: {traceback.format_exc()!s}"
+ )
+
+ def get_latest_import_id(self) -> int | None:
+ query = FwoApi.get_graphql_code([fwo_const.GRAPHQL_QUERY_PATH + "import/getLastSuccessImport.graphql"])
+ query_variables = {"mgmId": self.import_state.state.mgm_details.mgm_id}
+ try:
+ query_result = self.import_state.api_connection.call(query, query_variables=query_variables)
+ if "errors" in query_result:
+ raise FwoImporterError(
+ f"failed to get latest import id for mgm id {self.import_state.state.mgm_details.mgm_id!s}: {query_result['errors']!s}"
+ )
+ if len(query_result["data"]["import_control"]) == 0:
+ return None
+ return query_result["data"]["import_control"][0]["control_id"]
+ except Exception:
+ FWOLogger.exception(
+ f"failed to get latest import id for mgm id {self.import_state.state.mgm_details.mgm_id!s}: {traceback.format_exc()!s}"
+ )
+ raise FwoImporterError("error while trying to get the latest import id")
+
+ # return previous config or empty config if there is none; only returns the config of a single management
+ def get_latest_config(self) -> FwConfigNormalized:
+ mgm_id = self.import_state.state.mgm_details.current_mgm_id
+ prev_config = FwConfigNormalized()
+
+ latest_import_id = self.get_latest_import_id()
+ if latest_import_id is None:
+ FWOLogger.info(f"first import - no existing import was found for mgm id {mgm_id}") # TODO: change msg
+ return prev_config
+
+ query = FwoApi.get_graphql_code([fwo_const.GRAPHQL_QUERY_PATH + "import/getLatestConfig.graphql"])
+ query_variables = {"mgmId": mgm_id}
+ try:
+ query_result = self.import_state.api_connection.call(query, query_variables=query_variables)
+ if "errors" in query_result:
+ raise FwoImporterError(
+ f"failed to get latest config for mgm id {self.import_state.state.mgm_details.mgm_id!s}: {query_result['errors']!s}"
+ )
+ if len(query_result["data"]["latest_config"]) > 0: # do we have a prev config?
+ if query_result["data"]["latest_config"][0]["import_id"] == latest_import_id:
+ return FwConfigNormalized.model_validate_json(query_result["data"]["latest_config"][0]["config"])
+ FWOLogger.warning(
+ f"fwo_api:import_latest_config - latest config for mgm id {mgm_id} did not match last import id {latest_import_id}"
+ )
+ FWOLogger.info("fetching latest config from DB as fallback")
+ return self.get_latest_config_from_db()
+ except Exception:
+ FWOLogger.exception(
+ f"failed to get latest normalized config for mgm id {self.import_state.state.mgm_details.current_mgm_id!s}: {traceback.format_exc()!s}"
+ )
+ raise FwoImporterError("error while trying to get the previous config")
+
+ def get_latest_config_from_db(self) -> FwConfigNormalized:
+ params = {"mgm-ids": [self.import_state.state.mgm_details.current_mgm_id]}
+ result = self.import_state.api_connection.call_endpoint("POST", "api/NormalizedConfig/Get", params=params)
+ try:
+ return FwConfigNormalized.model_validate(result)
+ except Exception:
+ FWOLogger.exception(
+ f"failed to get latest normalized config from db for mgm id {self.import_state.state.mgm_details.mgm_id!s}: {traceback.format_exc()!s}"
+ )
+ raise FwoImporterError("error while trying to get the latest config")
+
+ def _sort_lists(self, config: FwConfigNormalized):
+ # sort lists in config to have consistent ordering for diff checks
+ config.rulebases.sort(key=lambda rb: rb.uid)
+ if any(gw.Uid is None for gw in config.gateways):
+ raise FwoImporterError(
+ "found gateway without UID while sorting gateways for consistency check - this should not happen"
+ )
+ config.gateways.sort(key=lambda gw: gw.Uid or "")
+ for gw in config.gateways:
+ gw.RulebaseLinks.sort(key=lambda rbl: f"{rbl.from_rulebase_uid}-{rbl.from_rule_uid}-{rbl.to_rulebase_uid}")
+ if gw.EnforcedPolicyUids is not None:
+ gw.EnforcedPolicyUids.sort()
+ if gw.EnforcedNatPolicyUids is not None:
+ gw.EnforcedNatPolicyUids.sort()
+ # TODO: interfaces and routing as soon as they are implemented
+
+ def consistency_check_db(self):
+ normalized_config = self.normalized_config
+ if normalized_config is None:
+ raise FwoImporterError("cannot perform consistency check: NormalizedConfig is None")
+ normalized_config_from_db = self.get_latest_config_from_db()
+ self._sort_lists(normalized_config)
+ self._sort_lists(normalized_config_from_db)
+ all_diffs = find_all_diffs(normalized_config.model_dump(), normalized_config_from_db.model_dump(), strict=True)
+ if len(all_diffs) > 0:
+ FWOLogger.warning(
+ f"normalized config for mgm id {self.import_state.state.mgm_details.current_mgm_id} is inconsistent to database state: {all_diffs[0]}"
+ )
+ FWOLogger.debug(f"all {len(all_diffs)} differences:\n\t" + "\n\t".join(all_diffs))
+ # TODO: long-term this should raise an error:
diff --git a/roles/importer/files/importer/model_controllers/fwconfig_import_gateway.py b/roles/importer/files/importer/model_controllers/fwconfig_import_gateway.py
new file mode 100644
index 0000000000..29a14a0578
--- /dev/null
+++ b/roles/importer/files/importer/model_controllers/fwconfig_import_gateway.py
@@ -0,0 +1,187 @@
+from typing import Any
+
+from fwo_exceptions import FwoImporterError
+from fwo_log import FWOLogger
+from model_controllers.rulebase_link_controller import RulebaseLinkController
+from models.gateway import Gateway
+from models.rulebase_link import ( # TODO: check if we need RulebaseLinkUidBased as well
+ RulebaseLink,
+ RulebaseLinkUidBased,
+)
+from services.global_state import GlobalState
+from services.service_provider import ServiceProvider
+
+
+class FwConfigImportGateway:
+ """
+ Provides methods import gateway information into the FWO API.
+ """
+
+ _global_state: GlobalState
+ _rb_link_controller: RulebaseLinkController
+
+ def __init__(self):
+ service_provider = ServiceProvider()
+ self._global_state = service_provider.get_global_state()
+ self._rb_link_controller = RulebaseLinkController()
+
+ def update_gateway_diffs(self):
+ # add gateway details:
+ self._rb_link_controller.get_rulebase_links(
+ self._global_state.import_state.state, self._global_state.import_state.api_call
+ )
+ required_inserts, required_removes = self.update_rulebase_link_diffs()
+ self._rb_link_controller.insert_rulebase_links(
+ self._global_state.import_state.api_call, self._global_state.import_state.state.stats, required_inserts
+ )
+ self._rb_link_controller.remove_rulebase_links(
+ self._global_state.import_state.api_call,
+ self._global_state.import_state.state.stats,
+ self._global_state.import_state.state.import_id,
+ required_removes,
+ )
+ self.update_interface_diffs()
+ self.update_routing_diffs()
+
+ def update_rulebase_link_diffs(self) -> tuple[list[dict[str, Any]], list[int | None]]:
+ if self._global_state.normalized_config is None:
+ raise FwoImporterError("normalized_config is None in update_rulebase_link_diffs")
+ if self._global_state.previous_config is None:
+ raise FwoImporterError("previous_config is None in update_rulebase_link_diffs")
+
+ required_inserts: list[dict[str, Any]] = []
+ required_removes: list[int | None] = []
+
+ for gw in self._global_state.normalized_config.gateways:
+ previous_config_gw = next(
+ (p_gw for p_gw in self._global_state.previous_config.gateways if gw.Uid == p_gw.Uid), None
+ )
+
+ if (
+ gw in self._global_state.previous_config.gateways
+ ): # this check finds all changes in gateway (including rulebase link changes)
+ # gateway found with exactly same properties in previous config
+ continue
+
+ FWOLogger.debug(f"gateway {gw!s} NOT found in previous config", 9)
+ if gw.Uid is None:
+ raise FwoImporterError("found gateway with Uid = None")
+ gw_id = self._global_state.import_state.state.lookup_gateway_id(gw.Uid)
+ if gw_id is None:
+ FWOLogger.warning(f"did not find a gwId for UID {gw.Uid}")
+
+ self._create_insert_args(gw, previous_config_gw, gw_id, required_inserts)
+
+ if previous_config_gw:
+ self._create_remove_args(gw, previous_config_gw, gw_id, required_removes)
+
+ return required_inserts, required_removes
+
+ def _create_insert_args(
+ self,
+ normalized_gateway: Gateway,
+ previous_gateway: Gateway | None,
+ gw_id: int | None,
+ arg_list: list[dict[str, Any]],
+ ):
+ rulebase_links = []
+
+ for link in normalized_gateway.RulebaseLinks:
+ if previous_gateway:
+ rulebase_links = previous_gateway.RulebaseLinks
+ self._try_add_single_link(arg_list, link, rulebase_links, gw_id, is_insert=True)
+
+ def _create_remove_args(
+ self, normalized_gateway: Gateway, previous_gateway: Gateway, gw_id: int | None, arg_list: list[int | None]
+ ):
+ removed_rulebase_links: list[dict[str, Any]] = []
+
+ for link in previous_gateway.RulebaseLinks:
+ self._try_add_single_link(
+ removed_rulebase_links, link, normalized_gateway.RulebaseLinks, gw_id, is_insert=False
+ )
+ for link in removed_rulebase_links:
+ link_in_db = self._try_get_id_based_link(link, self._rb_link_controller.rb_links)
+ if link_in_db:
+ arg_list.append(link_in_db.id)
+
+ def _try_add_single_link(
+ self,
+ rb_link_list: list[dict[str, Any]],
+ link: RulebaseLinkUidBased,
+ link_list: list[RulebaseLinkUidBased],
+ gw_id: int | None,
+ is_insert: bool,
+ ):
+ # If rule changed we need the id of the old version, since the rulebase links still have the old fks (for updates)
+
+ from_rule_id = (
+ self._global_state.import_state.state.removed_rules_map.get(link.from_rule_uid, None)
+ if link.from_rule_uid is not None
+ else None
+ )
+
+ # If rule is unchanged or new id can be fetched from RuleMap, because it has been updated already
+ if not from_rule_id or is_insert:
+ from_rule_id = (
+ self._global_state.import_state.state.lookup_rule(link.from_rule_uid)
+ if link.from_rule_uid is not None
+ else None
+ )
+ if link.from_rulebase_uid is None or link.from_rulebase_uid == "":
+ from_rulebase_id = None
+ else:
+ from_rulebase_id = self._global_state.import_state.state.lookup_rulebase_id(link.from_rulebase_uid)
+ to_rulebase_id = self._global_state.import_state.state.lookup_rulebase_id(link.to_rulebase_uid)
+ link_type_id = self._global_state.import_state.state.lookup_link_type(link.link_type)
+ if type(link_type_id) is not int:
+ FWOLogger.warning(f"did not find a link_type_id for link_type {link.link_type}")
+
+ if not self._link_is_in_link_list(link, link_list):
+ if gw_id is None:
+ FWOLogger.warning(f"did not find a gwId for UID {link}")
+ return
+ rb_link_list.append(
+ RulebaseLink(
+ gw_id=gw_id,
+ from_rule_id=from_rule_id,
+ to_rulebase_id=to_rulebase_id,
+ link_type=link_type_id,
+ is_initial=link.is_initial,
+ is_global=link.is_global,
+ is_section=link.is_section,
+ from_rulebase_id=from_rulebase_id,
+ created=self._global_state.import_state.state.import_id,
+ ).to_dict()
+ )
+
+ FWOLogger.debug(f"link {link} was added", 9)
+
+ def _link_is_in_link_list(self, link: RulebaseLinkUidBased, link_list: list[RulebaseLinkUidBased]) -> bool:
+ if link_list:
+ existing_link = next(
+ (existing_link for existing_link in link_list if existing_link.to_dict() == link.to_dict()), None
+ )
+
+ if existing_link:
+ return True
+
+ return False
+
+ def _try_get_id_based_link(self, link: dict[str, Any], link_list: list[RulebaseLink]):
+ return next(
+ (
+ existing_link
+ for existing_link in link_list
+ if {**existing_link.to_dict(), "created": 0} == {**link, "created": 0}
+ ),
+ None,
+ )
+
+ def update_interface_diffs(self):
+ # TODO: needs to be implemented
+ pass
+
+ def update_routing_diffs(self):
+ # TODO: needs to be implemented
+ pass
diff --git a/roles/importer/files/importer/model_controllers/fwconfig_import_object.py b/roles/importer/files/importer/model_controllers/fwconfig_import_object.py
new file mode 100644
index 0000000000..6106b2828a
--- /dev/null
+++ b/roles/importer/files/importer/model_controllers/fwconfig_import_object.py
@@ -0,0 +1,913 @@
+import datetime
+import json
+import traceback
+from enum import Enum
+from typing import Any
+
+import fwo_const
+from fwo_api_call import FwoApi
+from fwo_exceptions import FwoDuplicateKeyViolationError, FwoImporterError
+from fwo_log import ChangeLogger, FWOLogger
+from model_controllers.import_state_controller import ImportStateController
+from models.fwconfig_normalized import FwConfigNormalized
+from models.fwconfigmanager import FwConfigManager
+from models.networkobject import NetworkObjectForImport
+from models.serviceobject import ServiceObjectForImport
+from services.group_flats_mapper import GroupFlatsMapper
+from services.service_provider import ServiceProvider
+from services.uid2id_mapper import Uid2IdMapper
+
+
+class Type(Enum):
+ NETWORK_OBJECT = "network_object"
+ SERVICE_OBJECT = "service_object"
+ USER = "user"
+
+
+# this class is used for importing a config into the FWO API
+class FwConfigImportObject:
+ import_state: ImportStateController
+ normalized_config: FwConfigNormalized | None = None
+ global_normalized_config: FwConfigNormalized | None = None
+ group_flats_mapper: GroupFlatsMapper
+ prev_group_flats_mapper: GroupFlatsMapper
+ uid2id_mapper: Uid2IdMapper
+
+ def __init__(self):
+ # Get state, config and services.
+ service_provider = ServiceProvider()
+ global_state = service_provider.get_global_state()
+ self.import_state = global_state.import_state
+ self.normalized_config = global_state.normalized_config
+ self.global_normalized_config = global_state.global_normalized_config
+ self.group_flats_mapper = service_provider.get_group_flats_mapper(self.import_state.state.import_id)
+ self.prev_group_flats_mapper = service_provider.get_prev_group_flats_mapper(self.import_state.state.import_id)
+ self.uid2id_mapper = service_provider.get_uid2id_mapper(self.import_state.state.import_id)
+
+ # Create maps.
+ self.network_object_type_map = self.get_network_obj_type_map()
+ self.service_object_type_map = self.get_service_obj_type_map()
+ self.user_object_type_map = self.get_user_obj_type_map()
+ self.protocol_map = self.get_protocol_map()
+
+ def update_object_diffs(
+ self,
+ prev_config: FwConfigNormalized,
+ prev_global_config: FwConfigNormalized | None,
+ single_manager: FwConfigManager,
+ ):
+ change_logger = ChangeLogger()
+ if self.normalized_config is None:
+ raise FwoImporterError("no normalized config available in FwConfigImportObject.update_object_diffs")
+ # calculate network object diffs
+ # here we are handling the previous config as a dict for a while
+ deleted_nw_obj_uids: list[str] = list(
+ prev_config.network_objects.keys() - self.normalized_config.network_objects.keys()
+ )
+ new_nw_obj_uids: list[str] = list(
+ self.normalized_config.network_objects.keys() - prev_config.network_objects.keys()
+ )
+ nw_obj_uids_in_both: list[str] = list(
+ self.normalized_config.network_objects.keys() & prev_config.network_objects.keys()
+ )
+
+ # For correct changelog and stats.
+ changed_nw_objs: list[str] = []
+ changed_svcs: list[str] = []
+
+ # decide if it is prudent to mix changed, deleted and added rules here:
+ for nw_obj_uid in nw_obj_uids_in_both:
+ if self.normalized_config.network_objects[nw_obj_uid] != prev_config.network_objects[nw_obj_uid]:
+ new_nw_obj_uids.append(nw_obj_uid)
+ deleted_nw_obj_uids.append(nw_obj_uid)
+ changed_nw_objs.append(nw_obj_uid)
+
+ # calculate service object diffs
+ deleted_svc_obj_uids: list[str] = list(
+ prev_config.service_objects.keys() - self.normalized_config.service_objects.keys()
+ )
+ new_svc_obj_uids: list[str] = list(
+ self.normalized_config.service_objects.keys() - prev_config.service_objects.keys()
+ )
+ svc_obj_uids_in_both: list[str] = list(
+ self.normalized_config.service_objects.keys() & prev_config.service_objects.keys()
+ )
+
+ for svc_obj_uid in svc_obj_uids_in_both:
+ if self.normalized_config.service_objects[svc_obj_uid] != prev_config.service_objects[svc_obj_uid]:
+ new_svc_obj_uids.append(svc_obj_uid)
+ deleted_svc_obj_uids.append(svc_obj_uid)
+ changed_svcs.append(svc_obj_uid)
+
+ # calculate user diffs
+ deleted_user_uids: list[str] = list(prev_config.users.keys() - self.normalized_config.users.keys())
+ new_user_uids: list[str] = list(self.normalized_config.users.keys() - prev_config.users.keys())
+ user_uids_in_both: list[str] = list(self.normalized_config.users.keys() & prev_config.users.keys())
+ for user_uid in user_uids_in_both:
+ if self.normalized_config.users[user_uid] != prev_config.users[user_uid]:
+ new_user_uids.append(user_uid)
+ deleted_user_uids.append(user_uid)
+
+ # initial mapping of object uids to ids. needs to be updated, if more objects are created in the db after this point
+ # TODO: only fetch objects needed later. Esp for !isFullImport. but: newNwObjIds not enough!
+ # -> newObjs + extract all objects from new/changed rules and groups, flatten them. Complete?
+ self.uid2id_mapper.update_network_object_mapping(is_global=single_manager.is_super_manager)
+ self.uid2id_mapper.update_service_object_mapping(is_global=single_manager.is_super_manager)
+ self.uid2id_mapper.update_user_mapping(is_global=single_manager.is_super_manager)
+ self.uid2id_mapper.update_zone_mapping(is_global=single_manager.is_super_manager)
+
+ self.group_flats_mapper.init_config(self.normalized_config, self.global_normalized_config)
+ self.prev_group_flats_mapper.init_config(prev_config, prev_global_config)
+
+ # need to do this first, since we need the old object IDs for the group memberships
+ # TODO: computationally expensive? Even without changes, all group objects and their members are compared to the previous config.
+ self.remove_outdated_memberships(prev_config, Type.NETWORK_OBJECT)
+ self.remove_outdated_memberships(prev_config, Type.SERVICE_OBJECT)
+ self.remove_outdated_memberships(prev_config, Type.USER)
+
+ # calculate zone object diffs
+ deleted_zone_names: list[str] = list(
+ prev_config.zone_objects.keys() - self.normalized_config.zone_objects.keys()
+ )
+ new_zone_names: list[str] = list(self.normalized_config.zone_objects.keys() - prev_config.zone_objects.keys())
+ zone_names_in_both: list[str] = list(
+ self.normalized_config.zone_objects.keys() & prev_config.zone_objects.keys()
+ )
+ changed_zones: list[str] = []
+
+ for zone_name in zone_names_in_both:
+ if self.normalized_config.zone_objects[zone_name] != prev_config.zone_objects[zone_name]:
+ new_zone_names.append(zone_name)
+ deleted_zone_names.append(zone_name)
+ changed_zones.append(zone_name)
+
+ # add newly created objects
+ (
+ new_nw_obj_ids,
+ new_svc_obj_ids,
+ new_user_ids,
+ new_zone_ids,
+ removed_nw_obj_ids,
+ removed_svc_obj_ids,
+ _,
+ _,
+ ) = self.update_objects_via_api(
+ single_manager,
+ new_nw_obj_uids,
+ new_svc_obj_uids,
+ new_user_uids,
+ new_zone_names,
+ deleted_nw_obj_uids,
+ deleted_svc_obj_uids,
+ deleted_user_uids,
+ deleted_zone_names,
+ )
+
+ self.uid2id_mapper.add_network_object_mappings(new_nw_obj_ids, is_global=single_manager.is_super_manager)
+ self.uid2id_mapper.add_service_object_mappings(new_svc_obj_ids, is_global=single_manager.is_super_manager)
+ self.uid2id_mapper.add_user_mappings(new_user_ids, is_global=single_manager.is_super_manager)
+ self.uid2id_mapper.add_zone_mappings(new_zone_ids, is_global=single_manager.is_super_manager)
+
+ # insert new and updated group memberships
+ self.add_group_memberships(prev_config, Type.NETWORK_OBJECT)
+ self.add_group_memberships(prev_config, Type.SERVICE_OBJECT)
+ self.add_group_memberships(prev_config, Type.USER)
+
+ # these objects have really been deleted so there should be no refs to them anywhere! verify this
+
+ # TODO: calculate user diffs
+ # TODO: write changelog for zones
+ # Get Changed Ids.
+
+ change_logger.create_change_id_maps(
+ self.uid2id_mapper,
+ changed_nw_objs,
+ changed_svcs,
+ removed_nw_obj_ids,
+ removed_svc_obj_ids,
+ )
+
+ # Seperate changes from adds and removes for changelog and stats.
+
+ new_nw_obj_ids = [
+ new_nw_obj_id
+ for new_nw_obj_id in new_nw_obj_ids
+ if new_nw_obj_id["obj_id"] not in list(change_logger.changed_object_id_map.values())
+ ]
+ removed_nw_obj_ids = [
+ removed_nw_obj_id
+ for removed_nw_obj_id in removed_nw_obj_ids
+ if removed_nw_obj_id["obj_id"] not in list(change_logger.changed_object_id_map.keys())
+ ]
+ new_svc_obj_ids = [
+ new_svc_obj_id
+ for new_svc_obj_id in new_svc_obj_ids
+ if new_svc_obj_id["svc_id"] not in list(change_logger.changed_service_id_map.values())
+ ]
+ removed_svc_obj_ids = [
+ removed_svc_obj_id
+ for removed_svc_obj_id in removed_svc_obj_ids
+ if removed_svc_obj_id["svc_id"] not in list(change_logger.changed_service_id_map.keys())
+ ]
+
+ # Write change logs to tables.
+
+ self.add_changelog_objs(new_nw_obj_ids, new_svc_obj_ids, removed_nw_obj_ids, removed_svc_obj_ids)
+
+ # note changes:
+ self.import_state.state.stats.increment_network_object_add_count(len(new_nw_obj_ids))
+ self.import_state.state.stats.increment_network_object_delete_count(len(removed_nw_obj_ids))
+ self.import_state.state.stats.increment_network_object_change_count(
+ len(change_logger.changed_object_id_map.items())
+ )
+ self.import_state.state.stats.increment_service_object_add_count(len(new_svc_obj_ids))
+ self.import_state.state.stats.increment_service_object_delete_count(len(removed_svc_obj_ids))
+ self.import_state.state.stats.increment_service_object_change_count(
+ len(change_logger.changed_service_id_map.items())
+ )
+
+ def get_network_obj_type_map(self) -> dict[str, int]:
+ query = "query getNetworkObjTypeMap { stm_obj_typ { obj_typ_name obj_typ_id } }"
+ try:
+ result = self.import_state.api_call.call(query=query, query_variables={})
+ except Exception as e:
+ FWOLogger.error(f"Error while getting stm_obj_typ: str{e}")
+ return {}
+
+ nwobj_type_map: dict[str, Any] = {}
+ for nw_type in result["data"]["stm_obj_typ"]:
+ nwobj_type_map.update({nw_type["obj_typ_name"]: nw_type["obj_typ_id"]})
+ return nwobj_type_map
+
+ def get_service_obj_type_map(self) -> dict[str, int]:
+ query = "query getServiceObjTypeMap { stm_svc_typ { svc_typ_name svc_typ_id } }"
+ try:
+ result = self.import_state.api_call.call(query=query, query_variables={})
+ except Exception as e:
+ FWOLogger.error(f"Error while getting stm_svc_typ: {e!s}")
+ return {}
+
+ svc_type_map: dict[str, Any] = {}
+ for svc_type in result["data"]["stm_svc_typ"]:
+ svc_type_map.update({svc_type["svc_typ_name"]: svc_type["svc_typ_id"]})
+ return svc_type_map
+
+ def get_user_obj_type_map(self) -> dict[str, int]:
+ query = "query getUserObjTypeMap { stm_usr_typ { usr_typ_name usr_typ_id } }"
+ try:
+ result = self.import_state.api_call.call(query=query, query_variables={})
+ except Exception as e:
+ FWOLogger.error(f"Error while getting stm_usr_typ: {e!s}")
+ return {}
+
+ user_type_map: dict[str, Any] = {}
+ for usr_type in result["data"]["stm_usr_typ"]:
+ user_type_map.update({usr_type["usr_typ_name"]: usr_type["usr_typ_id"]})
+ return user_type_map
+
+ def get_protocol_map(self) -> dict[str, int]:
+ query = "query getIpProtocols { stm_ip_proto { ip_proto_id ip_proto_name } }"
+ try:
+ result = self.import_state.api_call.call(query=query, query_variables={})
+ except Exception as e:
+ FWOLogger.error(f"Error while getting stm_ip_proto: {e!s}")
+ return {}
+
+ protocol_map: dict[str, Any] = {}
+ for proto in result["data"]["stm_ip_proto"]:
+ protocol_map.update({proto["ip_proto_name"].lower(): proto["ip_proto_id"]})
+ return protocol_map
+
+ def update_objects_via_api(
+ self,
+ single_manager: FwConfigManager,
+ new_nw_object_uids: list[str],
+ new_svc_obj_uids: list[str],
+ new_user_uids: list[str],
+ new_zone_names: list[str],
+ removed_nw_object_uids: list[str],
+ removed_svc_object_uids: list[str],
+ removed_user_uids: list[str],
+ removed_zone_names: list[str],
+ ):
+ # here we also mark old objects removed before adding the new versions
+ new_nwobj_ids = []
+ new_nwsvc_ids = []
+ new_user_ids = []
+ new_zone_ids = []
+ removed_nwobj_ids = []
+ removed_nwsvc_ids = []
+ removed_user_ids = []
+ removed_zone_ids = []
+ this_managements_id = self.import_state.state.lookup_management_id(single_manager.manager_uid)
+ if this_managements_id is None:
+ raise FwoImporterError(
+ f"failed to update objects in updateObjectsViaApi: no management id found for manager uid '{single_manager.manager_uid}'"
+ )
+ import_mutation = FwoApi.get_graphql_code(
+ file_list=[fwo_const.GRAPHQL_QUERY_PATH + "allObjects/upsertObjects.graphql"]
+ )
+ query_variables: dict[str, Any] = {
+ "mgmId": this_managements_id,
+ "importId": self.import_state.state.import_id,
+ "newNwObjects": self.prepare_new_nwobjs(new_nw_object_uids, this_managements_id),
+ "newSvcObjects": self.prepare_new_svcobjs(new_svc_obj_uids, this_managements_id),
+ "newUsers": self.prepare_new_userobjs(new_user_uids, this_managements_id),
+ "newZones": self.prepare_new_zones(new_zone_names, this_managements_id),
+ "removedNwObjectUids": removed_nw_object_uids,
+ "removedSvcObjectUids": removed_svc_object_uids,
+ "removedUserUids": removed_user_uids,
+ "removedZoneUids": removed_zone_names,
+ }
+
+ FWOLogger.debug(f"fwo_api:importNwObject - import_mutation: {import_mutation}", 9)
+ if FWOLogger.is_debug_level(9):
+ json.dump(
+ query_variables,
+ open( # noqa: SIM115
+ f"/usr/local/fworch/tmp/import/mgm_id_{self.import_state.state.mgm_details.mgm_id}_query_variables.json",
+ "w",
+ ),
+ indent=4,
+ )
+
+ try:
+ import_result = self.import_state.api_call.call(
+ import_mutation, query_variables=query_variables, analyze_payload=True
+ )
+ if "errors" in import_result:
+ raise FwoImporterError(f"failed to update objects in updateObjectsViaApi: {import_result['errors']!s}")
+ _ = (
+ int(import_result["data"]["insert_object"]["affected_rows"])
+ + int(import_result["data"]["insert_service"]["affected_rows"])
+ + int(import_result["data"]["insert_usr"]["affected_rows"])
+ + int(import_result["data"]["update_object"]["affected_rows"])
+ + int(import_result["data"]["update_service"]["affected_rows"])
+ + int(import_result["data"]["update_usr"]["affected_rows"])
+ + int(import_result["data"]["update_zone"]["affected_rows"])
+ )
+ new_nwobj_ids = import_result["data"]["insert_object"]["returning"]
+ new_nwsvc_ids = import_result["data"]["insert_service"]["returning"]
+ new_user_ids = import_result["data"]["insert_usr"]["returning"]
+ new_zone_ids = import_result["data"]["insert_zone"]["returning"]
+ removed_nwobj_ids = import_result["data"]["update_object"]["returning"]
+ removed_nwsvc_ids = import_result["data"]["update_service"]["returning"]
+ removed_user_ids = import_result["data"]["update_usr"]["returning"]
+ removed_zone_ids = import_result["data"]["update_zone"]["returning"]
+ except Exception:
+ raise FwoImporterError(f"failed to update objects: {traceback.format_exc()!s}")
+ return (
+ new_nwobj_ids,
+ new_nwsvc_ids,
+ new_user_ids,
+ new_zone_ids,
+ removed_nwobj_ids,
+ removed_nwsvc_ids,
+ removed_user_ids,
+ removed_zone_ids,
+ )
+
+ def prepare_new_nwobjs(self, new_nwobj_uids: list[str], mgm_id: int) -> list[dict[str, Any]]:
+ if self.normalized_config is None:
+ raise FwoImporterError("no normalized config available in FwConfigImportObject.prepare_new_nwobjs")
+ new_nwobjs: list[dict[str, Any]] = []
+ for nwobj_uid in new_nwobj_uids:
+ new_nwobj = NetworkObjectForImport(
+ nw_object=self.normalized_config.network_objects[nwobj_uid],
+ mgm_id=mgm_id,
+ import_id=self.import_state.state.import_id,
+ color_id=self.import_state.state.lookup_color_id(
+ self.normalized_config.network_objects[nwobj_uid].obj_color
+ ),
+ typ_id=self.lookup_obj_type(self.normalized_config.network_objects[nwobj_uid].obj_typ),
+ )
+ new_nwobj_dict = new_nwobj.to_dict()
+ new_nwobjs.append(new_nwobj_dict)
+ return new_nwobjs
+
+ def prepare_new_svcobjs(self, new_svcobj_uids: list[str], mgm_id: int) -> list[dict[str, Any]]:
+ if self.normalized_config is None:
+ raise FwoImporterError("no normalized config available in FwConfigImportObject.prepare_new_svcobjs")
+ return [
+ ServiceObjectForImport(
+ svc_object=self.normalized_config.service_objects[uid],
+ mgm_id=mgm_id,
+ import_id=self.import_state.state.import_id,
+ color_id=self.import_state.state.lookup_color_id(self.normalized_config.service_objects[uid].svc_color),
+ typ_id=self.lookup_svc_type(self.normalized_config.service_objects[uid].svc_typ),
+ ).to_dict()
+ for uid in new_svcobj_uids
+ ]
+
+ def prepare_new_userobjs(self, new_user_uids: list[str], mgm_id: int) -> list[dict[str, Any]]:
+ if self.normalized_config is None:
+ raise FwoImporterError("no normalized config available in FwConfigImportObject.prepare_new_userobjs")
+ return [
+ {
+ "user_uid": uid,
+ "mgm_id": mgm_id,
+ "user_create": self.import_state.state.import_id,
+ "user_last_seen": self.import_state.state.import_id,
+ "usr_typ_id": self.lookup_user_type(self.normalized_config.users[uid]["user_typ"]),
+ "user_name": self.normalized_config.users[uid]["user_name"],
+ }
+ for uid in new_user_uids
+ ]
+
+ def prepare_new_zones(self, new_zone_names: list[str], mgm_id: int) -> list[dict[str, Any]]:
+ if self.normalized_config is None:
+ raise FwoImporterError("no normalized config available in FwConfigImportObject.prepare_new_zones")
+
+ return [
+ {
+ "mgm_id": mgm_id,
+ "zone_create": self.import_state.state.import_id,
+ "zone_last_seen": self.import_state.state.import_id,
+ "zone_name": self.normalized_config.zone_objects[uid]["zone_name"],
+ }
+ for uid in new_zone_names
+ ]
+
+ def get_config_objects(self, typ: Type, prev_config: FwConfigNormalized):
+ if self.normalized_config is None:
+ raise FwoImporterError("no normalized config available in FwConfigImportObject.get_config_objects")
+ if typ == Type.NETWORK_OBJECT:
+ return prev_config.network_objects, self.normalized_config.network_objects
+ if typ == Type.SERVICE_OBJECT:
+ return prev_config.service_objects, self.normalized_config.service_objects
+ if typ == Type.USER:
+ return prev_config.users, self.normalized_config.users
+ return None
+
+ def get_id(self, typ: Type, uid: str, before_update: bool = False) -> int | None:
+ if typ == Type.NETWORK_OBJECT:
+ return self.uid2id_mapper.get_network_object_id(uid, before_update)
+ if typ == Type.SERVICE_OBJECT:
+ return self.uid2id_mapper.get_service_object_id(uid, before_update)
+ return self.uid2id_mapper.get_user_id(uid, before_update)
+
+ def get_local_id(self, typ: Type, uid: str, before_update: bool = False) -> int | None:
+ if typ == Type.NETWORK_OBJECT:
+ return self.uid2id_mapper.get_network_object_id(uid, before_update, local_only=True)
+ if typ == Type.SERVICE_OBJECT:
+ return self.uid2id_mapper.get_service_object_id(uid, before_update, local_only=True)
+ return self.uid2id_mapper.get_user_id(uid, before_update, local_only=True)
+
+ def is_group(self, typ: Type, obj: Any) -> bool | None:
+ if typ == Type.NETWORK_OBJECT:
+ return obj.obj_typ == "group"
+ if typ == Type.SERVICE_OBJECT:
+ return obj.svc_typ == "group"
+ if typ == Type.USER:
+ return obj.get("user_typ", None) == "group"
+ return None
+
+ def get_refs(self, typ: Type, obj: Any) -> str | None:
+ if typ == Type.NETWORK_OBJECT:
+ return obj.obj_member_refs
+ if typ == Type.SERVICE_OBJECT:
+ return obj.svc_member_refs
+ return obj.get("user_member_refs", None)
+
+ def get_members(self, typ: Type, refs: str | None) -> list[str]:
+ if typ == Type.NETWORK_OBJECT:
+ return (
+ [member.split(fwo_const.USER_DELIMITER)[0] for member in refs.split(fwo_const.LIST_DELIMITER) if member]
+ if refs
+ else []
+ )
+ return refs.split(fwo_const.LIST_DELIMITER) if refs else []
+
+ def get_flats(self, typ: Type, uid: str) -> list[str]:
+ if typ == Type.NETWORK_OBJECT:
+ return self.group_flats_mapper.get_network_object_flats([uid])
+ if typ == Type.SERVICE_OBJECT:
+ return self.group_flats_mapper.get_service_object_flats([uid])
+ return self.group_flats_mapper.get_user_flats([uid])
+
+ def get_prev_flats(self, typ: Type, uid: str) -> list[str]:
+ if typ == Type.NETWORK_OBJECT:
+ return self.prev_group_flats_mapper.get_network_object_flats([uid])
+ if typ == Type.SERVICE_OBJECT:
+ return self.prev_group_flats_mapper.get_service_object_flats([uid])
+ return self.prev_group_flats_mapper.get_user_flats([uid])
+
+ def get_prefix(self, typ: Type):
+ if typ == Type.NETWORK_OBJECT:
+ return "objgrp"
+ if typ == Type.SERVICE_OBJECT:
+ return "svcgrp"
+ return "usrgrp"
+
+ def remove_outdated_memberships(self, prev_config: FwConfigNormalized, typ: Type):
+ removed_members: list[dict[str, Any]] = []
+ removed_flats: list[dict[str, Any]] = []
+
+ prev_config_objects, current_config_objects = self.get_config_objects(typ, prev_config)
+ prefix = self.get_prefix(typ)
+
+ for uid in prev_config_objects:
+ self.find_removed_objects(
+ current_config_objects,
+ prev_config_objects,
+ removed_members,
+ removed_flats,
+ prefix,
+ uid,
+ typ,
+ )
+ # remove outdated group memberships
+ if len(removed_members) == 0:
+ return
+
+ import_mutation = f"""
+ mutation removeOutdated{prefix.capitalize()}Memberships($importId: bigint!, $removedMembers: [{prefix}_bool_exp!]!, $removedFlats: [{prefix}_flat_bool_exp!]!) {{
+ update_{prefix}(where: {{_and: [{{_or: $removedMembers}}, {{removed: {{_is_null: true}}}}]}},
+ _set: {{
+ removed: $importId,
+ active: false
+ }}
+ ) {{
+ affected_rows
+ }}
+ update_{prefix}_flat(where: {{_and: [{{_or: $removedFlats}}, {{removed: {{_is_null: true}}}}]}},
+ _set: {{
+ removed: $importId,
+ active: false
+ }}
+ ) {{
+ affected_rows
+ }}
+ }}
+ """
+ query_variables: dict[str, Any] = {
+ "importId": self.import_state.state.import_id,
+ "removedMembers": removed_members,
+ "removedFlats": removed_flats,
+ }
+ try:
+ import_result = self.import_state.api_call.call(import_mutation, query_variables, analyze_payload=True)
+ if "errors" in import_result:
+ FWOLogger.exception(
+ f"fwo_api:importNwObject - error in removeOutdated{prefix.capitalize()}Memberships: {import_result['errors']!s}"
+ )
+ else:
+ _ = int(import_result["data"][f"update_{prefix}"]["affected_rows"]) + int(
+ import_result["data"][f"update_{prefix}_flat"]["affected_rows"]
+ )
+ except Exception:
+ FWOLogger.exception(f"failed to remove outdated group memberships for {typ}: {traceback.format_exc()!s}")
+
+ def find_removed_objects(
+ self,
+ current_config_objects: dict[str, Any],
+ prev_config_objects: dict[str, Any],
+ removed_members: list[dict[str, Any]],
+ removed_flats: list[dict[str, Any]],
+ prefix: str,
+ uid: str,
+ typ: Type,
+ ) -> None:
+ if not self.is_group(typ, prev_config_objects[uid]):
+ return
+ db_id = self.get_id(typ, uid, before_update=True)
+ prev_member_uids = self.get_members(typ, self.get_refs(typ, prev_config_objects[uid]))
+ prev_flat_member_uids = self.get_prev_flats(typ, uid)
+ member_uids = [] # all members need to be removed if group deleted or changed
+ flat_member_uids = []
+ # group not removed and group not changed -> check for changes in members
+ if uid in current_config_objects and current_config_objects[uid] == prev_config_objects[uid]:
+ member_uids = self.get_members(typ, self.get_refs(typ, current_config_objects[uid]))
+ flat_member_uids = self.get_flats(typ, uid)
+ for prev_member_uid in prev_member_uids:
+ if (
+ prev_member_uid in member_uids
+ and current_config_objects[prev_member_uid] == prev_config_objects[prev_member_uid]
+ ):
+ continue # member was not removed or changed
+ prev_member_id = self.get_id(typ, prev_member_uid, before_update=True)
+ removed_members.append(
+ {
+ "_and": [
+ {f"{prefix}_id": {"_eq": db_id}},
+ {f"{prefix}_member_id": {"_eq": prev_member_id}},
+ ]
+ }
+ )
+ for prev_flat_member_uid in prev_flat_member_uids:
+ if (
+ prev_flat_member_uid in flat_member_uids
+ and current_config_objects[prev_flat_member_uid] == prev_config_objects[prev_flat_member_uid]
+ ):
+ continue # flat member was not removed or changed
+ prev_flat_member_id = self.get_id(typ, prev_flat_member_uid, before_update=True)
+ removed_flats.append(
+ {
+ "_and": [
+ {f"{prefix}_flat_id": {"_eq": db_id}},
+ {f"{prefix}_flat_member_id": {"_eq": prev_flat_member_id}},
+ ]
+ }
+ )
+
+ def add_group_memberships(self, prev_config: FwConfigNormalized, obj_type: Type):
+ """
+ Function is used to update group memberships for nwobjs, services or users in the database.
+ It adds group memberships and flats for new and updated members.
+
+ Args:
+ prev_config (FwConfigNormalized): The previous normalized config.
+
+ """
+ new_group_members: list[dict[str, Any]] = []
+ new_group_member_flats: list[dict[str, Any]] = []
+ prev_config_objects, current_config_objects = self.get_config_objects(obj_type, prev_config)
+ prefix = self.get_prefix(obj_type)
+ for uid in current_config_objects:
+ if not self.is_group(obj_type, current_config_objects[uid]):
+ continue
+ member_uids = self.get_members(obj_type, self.get_refs(obj_type, current_config_objects[uid]))
+ prev_member_uids = [] # all members need to be added if group added or changed
+ prev_flat_member_uids = []
+ if uid in prev_config_objects and current_config_objects[uid] == prev_config_objects[uid]:
+ # group not changed -> check for changes in members
+ prev_member_uids = self.get_members(obj_type, self.get_refs(obj_type, prev_config_objects[uid]))
+ prev_flat_member_uids = self.get_prev_flats(obj_type, uid)
+
+ group_id = self.get_id(obj_type, uid)
+ if group_id is None:
+ FWOLogger.error(f"failed to add group memberships: no id found for group uid '{uid}'")
+ continue
+ self.collect_group_members(
+ group_id,
+ current_config_objects,
+ new_group_members,
+ member_uids,
+ obj_type,
+ prefix,
+ prev_member_uids,
+ prev_config_objects,
+ )
+ flat_member_uids = self.get_flats(obj_type, uid)
+ self.collect_flat_group_members(
+ group_id,
+ current_config_objects,
+ new_group_member_flats,
+ flat_member_uids,
+ obj_type,
+ prefix,
+ prev_flat_member_uids,
+ prev_config_objects,
+ )
+
+ if len(new_group_members) == 0:
+ return
+
+ self.write_member_updates(new_group_members, new_group_member_flats, prefix)
+
+ def collect_flat_group_members(
+ self,
+ group_id: int,
+ current_config_objects: dict[str, Any],
+ new_group_member_flats: list[dict[str, Any]],
+ flat_member_uids: list[str],
+ obj_type: Type,
+ prefix: str,
+ prev_flat_member_uids: list[str],
+ prev_config_objects: dict[str, Any],
+ ):
+ for flat_member_uid in flat_member_uids:
+ if (
+ flat_member_uid in prev_flat_member_uids
+ and prev_config_objects[flat_member_uid] == current_config_objects[flat_member_uid]
+ ):
+ continue # flat member was not added or changed
+ flat_member_id = self.get_id(obj_type, flat_member_uid)
+ new_group_member_flats.append(
+ {
+ f"{prefix}_flat_id": group_id,
+ f"{prefix}_flat_member_id": flat_member_id,
+ "import_created": self.import_state.state.import_id,
+ "import_last_seen": self.import_state.state.import_id, # to be removed in the future
+ }
+ )
+
+ def collect_group_members(
+ self,
+ group_id: int,
+ current_config_objects: dict[str, Any],
+ new_group_members: list[dict[str, Any]],
+ member_uids: list[str],
+ obj_type: Type,
+ prefix: str,
+ prev_member_uids: list[str],
+ prev_config_objects: dict[str, Any],
+ ):
+ for member_uid in member_uids:
+ if member_uid in prev_member_uids and prev_config_objects[member_uid] == current_config_objects[member_uid]:
+ continue # member was not added or changed
+ member_id = self.get_id(obj_type, member_uid)
+ new_group_members.append(
+ {
+ f"{prefix}_id": group_id,
+ f"{prefix}_member_id": member_id,
+ "import_created": self.import_state.state.import_id,
+ "import_last_seen": self.import_state.state.import_id, # to be removed in the future
+ }
+ )
+
+ def write_member_updates(
+ self,
+ new_group_members: list[dict[str, Any]],
+ new_group_member_flats: list[dict[str, Any]],
+ prefix: str,
+ ):
+ import_mutation = f"""
+ mutation update{prefix.capitalize()}Groups($groups: [{prefix}_insert_input!]!, $groupFlats: [{prefix}_flat_insert_input!]!) {{
+ insert_{prefix}(objects: $groups) {{
+ affected_rows
+ }}
+ insert_{prefix}_flat(objects: $groupFlats) {{
+ affected_rows
+ }}
+ }}
+ """
+ query_variables = {
+ "groups": new_group_members,
+ "groupFlats": new_group_member_flats,
+ }
+ try:
+ import_result = self.import_state.api_call.call(
+ import_mutation, query_variables=query_variables, analyze_payload=True
+ )
+ if "errors" in import_result:
+ FWOLogger.exception(f"fwo_api:addGroupMemberships: {import_result['errors']!s}")
+ if "duplicate" in import_result["errors"]:
+ raise FwoDuplicateKeyViolationError(str(import_result["errors"]))
+ raise FwoImporterError(str(import_result["errors"]))
+ _ = int(import_result["data"][f"insert_{prefix}"]["affected_rows"]) + int(
+ import_result["data"][f"insert_{prefix}_flat"]["affected_rows"]
+ )
+ except Exception:
+ FWOLogger.exception(f"failed to write new objects: {traceback.format_exc()!s}")
+ raise
+
+ def lookup_obj_type(self, obj_type_str: str) -> int:
+ # TODO: might check for miss here as this is a mandatory field!
+ return self.network_object_type_map.get(obj_type_str, -1)
+
+ def lookup_svc_type(self, svc_type_str: str) -> int:
+ # TODO: might check for miss here as this is a mandatory field!
+ return self.service_object_type_map.get(svc_type_str, -1)
+
+ def lookup_user_type(self, user_type_str: str) -> int:
+ return self.user_object_type_map.get(user_type_str, -1)
+
+ def lookup_obj_id_to_uid_and_policy_name(self, obj_id: int) -> str:
+ return str(obj_id) # mock
+ # CAST((COALESCE (rule.rule_ruleid, rule.rule_uid) || ', Rulebase: ' || device.local_rulebase_name) AS VARCHAR) AS unique_name,
+ # return self.NetworkObjectIdMap.get(objId, None) # noqa: ERA001
+
+ def lookup_svc_id_to_uid_and_policy_name(self, svc_id: int):
+ return str(svc_id) # mock
+
+ def lookup_proto_name_to_id(self, proto_str: str | int) -> int | None:
+ if isinstance(proto_str, int):
+ return proto_str # already an int, do nothing
+ return self.protocol_map.get(proto_str.lower(), None)
+
+ def prepare_changelog_objects(
+ self,
+ nw_obj_ids_added: list[dict[str, int]],
+ svc_obj_ids_added: list[dict[str, int]],
+ nw_obj_ids_removed: list[dict[str, int]],
+ svc_obj_ids_removed: list[dict[str, int]],
+ ) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]:
+ """
+ Insert into stm_change_type (change_type_id,change_type_name) VALUES (1,'factory settings');
+ insert into stm_change_type (change_type_id,change_type_name) VALUES (2,'initial import');
+ insert into stm_change_type (change_type_id,change_type_name) VALUES (3,'in operation');
+ """
+ # TODO: deal with object changes where we need old and new obj id
+
+ nw_objs: list[dict[str, Any]] = []
+ svc_objs: list[dict[str, Any]] = []
+ import_time = datetime.datetime.now().isoformat()
+ change_typ = 3 # standard
+ change_logger = ChangeLogger()
+
+ if self.import_state.state.is_full_import or self.import_state.state.is_clearing_import:
+ change_typ = 2 # to be ignored in change reports
+
+ # Write changelog for network objects.
+
+ nw_objs = [
+ change_logger.create_changelog_import_object(
+ "obj", self.import_state.state, "I", change_typ, import_time, nw_obj_id
+ )
+ for nw_obj_id in [nw_obj_ids_added_item["obj_id"] for nw_obj_ids_added_item in nw_obj_ids_added]
+ ]
+
+ nw_objs.extend(
+ [
+ change_logger.create_changelog_import_object(
+ "obj",
+ self.import_state.state,
+ "D",
+ change_typ,
+ import_time,
+ nw_obj_id,
+ )
+ for nw_obj_id in [nw_obj_ids_removed_item["obj_id"] for nw_obj_ids_removed_item in nw_obj_ids_removed]
+ ]
+ )
+
+ for old_nw_obj_id, new_nw_obj_id in change_logger.changed_object_id_map.items():
+ nw_objs.append(
+ change_logger.create_changelog_import_object(
+ "obj",
+ self.import_state.state,
+ "C",
+ change_typ,
+ import_time,
+ new_nw_obj_id,
+ old_nw_obj_id,
+ )
+ )
+
+ # Write changelog for Services.
+
+ svc_objs.extend(
+ [
+ change_logger.create_changelog_import_object(
+ "svc", self.import_state.state, "I", change_typ, import_time, svc_id
+ )
+ for svc_id in [svc_ids_added_item["svc_id"] for svc_ids_added_item in svc_obj_ids_added]
+ ]
+ )
+
+ svc_objs.extend(
+ [
+ change_logger.create_changelog_import_object(
+ "svc", self.import_state.state, "D", change_typ, import_time, svc_id
+ )
+ for svc_id in [svc_ids_removed_item["svc_id"] for svc_ids_removed_item in svc_obj_ids_removed]
+ ]
+ )
+
+ for old_svc_id, new_svc_id in change_logger.changed_service_id_map.items():
+ svc_objs.append(
+ change_logger.create_changelog_import_object(
+ "svc",
+ self.import_state.state,
+ "C",
+ change_typ,
+ import_time,
+ new_svc_id,
+ old_svc_id,
+ )
+ )
+
+ return nw_objs, svc_objs
+
+ def add_changelog_objs(
+ self,
+ nwobj_ids_added: list[dict[str, int]],
+ svc_obj_ids_added: list[dict[str, int]],
+ nw_obj_ids_removed: list[dict[str, int]],
+ svc_obj_ids_removed: list[dict[str, int]],
+ ):
+ nwobjs_changed, svcobjs_changed = self.prepare_changelog_objects(
+ nwobj_ids_added, svc_obj_ids_added, nw_obj_ids_removed, svc_obj_ids_removed
+ )
+ changelog_mutation = """
+ mutation updateObjChangelogs($nwObjChanges: [changelog_object_insert_input!]!, $svcObjChanges: [changelog_service_insert_input!]!) {
+ insert_changelog_object(objects: $nwObjChanges) {
+ affected_rows
+ }
+ insert_changelog_service(objects: $svcObjChanges) {
+ affected_rows
+ }
+ }
+ """
+
+ query_variables = {
+ "nwObjChanges": nwobjs_changed,
+ "svcObjChanges": svcobjs_changed,
+ }
+
+ if len(nwobjs_changed) + len(svcobjs_changed) > 0:
+ try:
+ changelog_result = self.import_state.api_call.call(
+ changelog_mutation,
+ query_variables=query_variables,
+ analyze_payload=True,
+ )
+ if "errors" in changelog_result:
+ FWOLogger.exception(
+ f"error while adding changelog entries for objects: {changelog_result['errors']!s}"
+ )
+ except Exception:
+ FWOLogger.exception(
+ f"fatal error while adding changelog entries for objects: {traceback.format_exc()!s}"
+ )
diff --git a/roles/importer/files/importer/model_controllers/fwconfig_import_rollback.py b/roles/importer/files/importer/model_controllers/fwconfig_import_rollback.py
new file mode 100644
index 0000000000..9806a33f62
--- /dev/null
+++ b/roles/importer/files/importer/model_controllers/fwconfig_import_rollback.py
@@ -0,0 +1,35 @@
+import traceback
+
+import fwo_const
+from fwo_api import FwoApi
+from fwo_api_call import FwoApiCall
+from fwo_log import FWOLogger
+from models.import_state import ImportState
+
+
+# this class is used for rolling back an import
+class FwConfigImportRollback:
+ # this function deletes all new entries added in this import
+ # also resets all entries that have been marked removed
+ # also deletes latest_config for this management
+ # TODO: also take super management id into account as second option
+
+ def rollback_current_import(self, import_state: ImportState, fwo_api_call: FwoApiCall):
+ rollback_mutation = FwoApi.get_graphql_code([f"{fwo_const.GRAPHQL_QUERY_PATH}import/rollbackImport.graphql"])
+ try:
+ query_variables = {"importId": import_state.import_id}
+ rollback_result = fwo_api_call.call(rollback_mutation, query_variables=query_variables)
+ if "errors" in rollback_result:
+ FWOLogger.exception(
+ "error while trying to roll back current import for mgm id "
+ + str(import_state.mgm_details.mgm_id)
+ + ": "
+ + str(rollback_result["errors"])
+ )
+ else:
+ FWOLogger.info("import " + str(import_state.import_id) + " has been rolled back successfully")
+
+ except Exception:
+ FWOLogger.exception(
+ f"failed to rollback current import for mgm id {import_state.mgm_details.mgm_id!s}: {traceback.format_exc()!s}"
+ )
diff --git a/roles/importer/files/importer/model_controllers/fwconfig_import_rule.py b/roles/importer/files/importer/model_controllers/fwconfig_import_rule.py
new file mode 100644
index 0000000000..4d0b3fb716
--- /dev/null
+++ b/roles/importer/files/importer/model_controllers/fwconfig_import_rule.py
@@ -0,0 +1,1400 @@
+import json
+import traceback
+from collections.abc import Callable, Generator
+from datetime import datetime
+from difflib import ndiff
+from enum import Enum
+from typing import Any, TypeVar
+
+import fwo_const
+from fwo_api import FwoApi
+from fwo_exceptions import FwoApiWriteError, FwoImporterError
+from fwo_log import ChangeLogger, FWOLogger
+from model_controllers.import_state_controller import ImportStateController
+from models.fwconfig_normalized import FwConfigNormalized
+from models.networkobject import NetworkObject
+from models.rule import Rule, RuleNormalized
+from models.rule_from import RuleFrom
+from models.rule_metadatum import RuleMetadatum
+from models.rule_service import RuleService
+from models.rule_to import RuleTo
+from models.rulebase import Rulebase, RulebaseForImport
+from models.serviceobject import ServiceObject
+from services.global_state import GlobalState
+from services.group_flats_mapper import GroupFlatsMapper
+from services.service_provider import ServiceProvider
+from services.uid2id_mapper import Uid2IdMapper
+
+
+class RefType(Enum):
+ SRC = "rule_from"
+ DST = "rule_to"
+ SVC = "rule_service"
+ NWOBJ_RESOLVED = "rule_nwobj_resolved"
+ SVC_RESOLVED = "rule_svc_resolved"
+ USER_RESOLVED = "rule_user_resolved"
+ 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:
+ _changed_rule_id_map: dict[int, int]
+ global_state: GlobalState
+ import_details: ImportStateController
+ normalized_config: FwConfigNormalized | None = None
+ uid2id_mapper: Uid2IdMapper
+ group_flats_mapper: GroupFlatsMapper
+ prev_group_flats_mapper: GroupFlatsMapper
+
+ def __init__(self):
+ self._changed_rule_id_map = {}
+
+ service_provider = ServiceProvider()
+ self.global_state = service_provider.get_global_state()
+ self.import_details = self.global_state.import_state
+ # TODO: why is there a state where this is initialized with normalized_config = None? - see #3154
+ self.normalized_config = self.global_state.normalized_config
+ self.uid2id_mapper = service_provider.get_uid2id_mapper(self.import_details.state.import_id)
+ self.group_flats_mapper = service_provider.get_group_flats_mapper(self.import_details.state.import_id)
+ self.prev_group_flats_mapper = service_provider.get_prev_group_flats_mapper(self.import_details.state.import_id)
+ self.rule_order_service = service_provider.get_rule_order_service(self.import_details.state.import_id)
+
+ def update_rulebase_diffs(self, prev_config: FwConfigNormalized) -> list[int]:
+ if self.normalized_config is None:
+ raise FwoImporterError("cannot update rulebase diffs: normalized_config is None")
+
+ # calculate rule diffs
+ changed_rule_uids: dict[str, list[str]] = {} # rulebase_id -> list of rule_uids
+ rule_uids_in_both: dict[str, list[str]] = {}
+ previous_rulebase_uids: list[str] = []
+ current_rulebase_uids: list[str] = []
+ new_hit_information: list[dict[str, Any]] = []
+
+ rule_order_diffs: dict[str, dict[str, list[str]]] = self.rule_order_service.update_rule_order_diffs()
+
+ # collect rulebase UIDs of previous config
+ previous_rulebase_uids = [rulebase.uid for rulebase in prev_config.rulebases]
+
+ # collect rulebase UIDs of current (just imported) config
+ current_rulebase_uids = [rulebase.uid for rulebase in self.normalized_config.rulebases]
+
+ for rulebase_uid in previous_rulebase_uids:
+ current_rulebase = self.normalized_config.get_rulebase_or_none(rulebase_uid)
+ if current_rulebase is None:
+ FWOLogger.info(f"current rulebase has been deleted: {rulebase_uid}")
+ continue
+ if rulebase_uid in current_rulebase_uids:
+ # deal with policies contained both in this and previous config
+ previous_rulebase = prev_config.get_rulebase(rulebase_uid)
+ rule_uids_in_both.update(
+ {rulebase_uid: list(current_rulebase.rules.keys() & previous_rulebase.rules.keys())}
+ )
+ else:
+ FWOLogger.info(f"previous rulebase has been deleted: {current_rulebase.name} (id:{rulebase_uid})")
+
+ # find changed rules
+ for rulebase_uid in rule_uids_in_both: # noqa: PLC0206
+ changed_rule_uids.update({rulebase_uid: []})
+ current_rulebase = self.normalized_config.get_rulebase(
+ rulebase_uid
+ ) # [pol for pol in self.NormalizedConfig.rulebases if pol.Uid == rulebaseId]
+ previous_rulebase = prev_config.get_rulebase(rulebase_uid)
+ for rule_uid in rule_uids_in_both[rulebase_uid]:
+ self.preserve_rule_num_numeric(current_rulebase, previous_rulebase, rule_uid)
+ self.collect_changed_rules(
+ rule_uid, current_rulebase, previous_rulebase, rulebase_uid, changed_rule_uids
+ )
+
+ # collect hit information for all rules with hit data
+ self.collect_all_hit_information(prev_config, new_hit_information)
+
+ # add moved rules that are not in changed rules (e.g. move across rulebases)
+ self._collect_uncaught_moves(rule_order_diffs["moved_rule_uids"], changed_rule_uids)
+
+ # add full rule details first
+ new_rulebases = self.get_rules(rule_order_diffs["new_rule_uids"])
+
+ # update rule_metadata before adding rules
+ _, _ = self.add_new_rule_metadata(new_rulebases)
+ self.update_rule_metadata_last_hit(new_hit_information)
+
+ # # now update the database with all rule diffs
+ self.uid2id_mapper.update_rule_mapping()
+
+ num_added_rules, new_rule_ids = self.add_new_rules(new_rulebases)
+ num_changed_rules, old_rule_ids, updated_rule_ids = self.create_new_rule_version(changed_rule_uids)
+
+ self.uid2id_mapper.add_rule_mappings(new_rule_ids + updated_rule_ids)
+ _ = self.add_new_refs(prev_config)
+
+ num_deleted_rules, removed_rule_ids = self.mark_rules_removed(rule_order_diffs["deleted_rule_uids"])
+ self.remove_outdated_refs(prev_config)
+
+ num_moved_rules, _ = self.verify_rules_moved(changed_rule_uids)
+
+ new_rule_ids = [rule["rule_id"] for rule in new_rule_ids] # extract rule_ids from the returned list of dicts
+ self.write_changelog_rules(new_rule_ids, removed_rule_ids)
+
+ self.import_details.state.stats.increment_rule_add_count(num_added_rules)
+ self.import_details.state.stats.increment_rule_delete_count(num_deleted_rules)
+ self.import_details.state.stats.increment_rule_move_count(num_moved_rules)
+ self.import_details.state.stats.increment_rule_change_count(num_changed_rules)
+
+ for removed_rules_by_rulebase in removed_rule_ids:
+ old_rule_ids.append(removed_rules_by_rulebase)
+
+ if len(old_rule_ids) > 0:
+ self._create_removed_rules_map(old_rule_ids)
+
+ # TODO: rule_nwobj_resolved fuellen (recert?)
+ return new_rule_ids
+
+ def _create_removed_rules_map(self, removed_rule_ids: list[int]):
+ removed_rule_ids_set = set(removed_rule_ids)
+ for rule_id in removed_rule_ids_set:
+ rule_uid = next((k for k, v in self.import_details.state.rule_map.items() if v == rule_id), None)
+ if rule_uid:
+ self.import_details.state.removed_rules_map[rule_uid] = rule_id
+
+ def _collect_uncaught_moves(self, moved_rule_uids: dict[str, list[str]], changed_rule_uids: dict[str, list[str]]):
+ for rulebase_id in moved_rule_uids: # noqa: PLC0206
+ for rule_uid in moved_rule_uids[rulebase_id]:
+ if rule_uid not in changed_rule_uids.get(rulebase_id, []):
+ if rulebase_id not in changed_rule_uids:
+ changed_rule_uids[rulebase_id] = []
+ changed_rule_uids[rulebase_id].append(rule_uid)
+
+ def collect_all_hit_information(self, prev_config: FwConfigNormalized, new_hit_information: list[dict[str, Any]]):
+ """
+ Consolidated hit information collection for ALL rules that need hit updates.
+
+ Args:
+ prev_config: Previous configuration for comparison
+ new_hit_information: List to append hit update information to
+
+ """
+ processed_rules: set[str] = set()
+
+ def add_hit_update(new_hit_information: list[dict[str, Any]], rule: RuleNormalized):
+ """Add a hit information update entry for a rule."""
+ new_hit_information.append(
+ {
+ "where": {
+ "rule_uid": {"_eq": rule.rule_uid},
+ "mgm_id": {"_eq": self.import_details.state.mgm_details.current_mgm_id},
+ },
+ "_set": {"rule_last_hit": rule.last_hit},
+ }
+ )
+
+ # check all rulebases in current config
+ if self.normalized_config is None:
+ raise FwoImporterError("cannot collect hit information: normalized_config is None")
+
+ for current_rulebase in self.normalized_config.rulebases:
+ previous_rulebase = prev_config.get_rulebase_or_none(current_rulebase.uid)
+
+ for rule_uid in current_rulebase.rules:
+ current_rule = current_rulebase.rules[rule_uid]
+ previous_rule = previous_rulebase.rules.get(rule_uid) if previous_rulebase else None
+
+ if current_rule.last_hit is None:
+ continue # No hit information to update
+
+ if previous_rule is None or (current_rule.last_hit != previous_rule.last_hit):
+ # rulebase or rule is new or hit information changed
+ add_hit_update(new_hit_information, current_rule)
+ processed_rules.add(rule_uid)
+
+ def update_rule_metadata_last_hit(self, new_hit_information: list[dict[str, Any]]):
+ """
+ Updates rule_metadata.rule_last_hit for all rules with hit information changes.
+ This method executes the actual database updates for hit information.
+
+ Args:
+ new_hit_information (list[dict]): The hit information to update.
+
+ """
+ if len(new_hit_information) > 0:
+ update_last_hit_mutation = FwoApi.get_graphql_code(
+ [fwo_const.GRAPHQL_QUERY_PATH + "rule_metadata/updateLastHits.graphql"]
+ )
+ query_variables = {"hit_info": new_hit_information}
+
+ try:
+ import_result = self.import_details.api_call.call(
+ update_last_hit_mutation, query_variables=query_variables, analyze_payload=True
+ )
+ if "errors" in import_result:
+ FWOLogger.exception(
+ f"fwo_api:importNwObject - error in addNewRuleMetadata: {import_result['errors']!s}"
+ )
+ # do not count last hit changes as changes here
+ except Exception:
+ raise FwoApiWriteError(f"failed to update RuleMetadata last hit info: {traceback.format_exc()!s}")
+
+ @staticmethod
+ def collect_changed_rules(
+ rule_uid: str,
+ current_rulebase: Rulebase,
+ previous_rulebase: Rulebase,
+ rulebase_id: str,
+ changed_rule_uids: dict[str, list[str]],
+ ):
+ if current_rulebase.rules[rule_uid] != previous_rulebase.rules[rule_uid]:
+ changed_rule_uids[rulebase_id].append(rule_uid)
+
+ @staticmethod
+ def preserve_rule_num_numeric(current_rulebase: Rulebase, previous_rulebase: Rulebase, rule_uid: str):
+ if current_rulebase.rules[rule_uid].rule_num_numeric == 0:
+ current_rulebase.rules[rule_uid].rule_num_numeric = previous_rulebase.rules[rule_uid].rule_num_numeric
+
+ def get_rule_refs(
+ self, rule: RuleNormalized, is_prev: bool = False
+ ) -> dict[RefType, list[tuple[str, str | None]] | list[str]]:
+ froms: list[tuple[str, str | None]] = []
+ tos: list[tuple[str, str | None]] = []
+ users: list[str] = []
+ nwobj_resolveds = []
+ svc_resolveds = []
+ user_resolveds = []
+ from_zones = []
+ to_zones = []
+ for refs in rule.rule_src_refs.split(fwo_const.LIST_DELIMITER):
+ user_ref = None
+ if fwo_const.USER_DELIMITER in refs:
+ src_ref, user_ref = refs.split(fwo_const.USER_DELIMITER)
+ users.append(user_ref)
+ else:
+ src_ref = refs
+ froms.append((src_ref, user_ref))
+ for refs in rule.rule_dst_refs.split(fwo_const.LIST_DELIMITER):
+ user_ref = None
+ if fwo_const.USER_DELIMITER in refs:
+ dst_ref, user_ref = refs.split(fwo_const.USER_DELIMITER)
+ users.append(user_ref)
+ else:
+ dst_ref = refs
+ tos.append((dst_ref, user_ref))
+ svcs = rule.rule_svc_refs.split(fwo_const.LIST_DELIMITER)
+ if is_prev:
+ nwobj_resolveds = self.prev_group_flats_mapper.get_network_object_flats([ref[0] for ref in froms + tos])
+ svc_resolveds = self.prev_group_flats_mapper.get_service_object_flats(svcs)
+ user_resolveds = self.prev_group_flats_mapper.get_user_flats(users)
+ else:
+ 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.SRC_ZONE: from_zones,
+ RefType.DST_ZONE: to_zones,
+ }
+
+ T = TypeVar("T")
+
+ def _lookup_object(
+ self,
+ uid: str,
+ previous: bool,
+ config_accessor: Callable[[FwConfigNormalized], dict[str, T]],
+ object_type_name: str,
+ ) -> T:
+ """Generic object lookup from config with fallback to global config."""
+ config = self.global_state.previous_config if previous else self.normalized_config
+ global_config = (
+ self.global_state.previous_global_config if previous else self.global_state.global_normalized_config
+ )
+ config_type = "previous" if previous else "current"
+
+ if config is None:
+ raise FwoImporterError(f"cannot lookup {object_type_name}: {config_type} config is None")
+
+ obj = config_accessor(config).get(uid, None)
+ if obj is None:
+ # try lookup in global config
+ if global_config is None:
+ raise FwoImporterError(f"{object_type_name} not found in {config_type} config: {uid}")
+ obj = config_accessor(global_config).get(uid, None)
+ if obj is None:
+ raise FwoImporterError(
+ f"{object_type_name} not found in {config_type} config and {config_type} global config: {uid}"
+ )
+ return obj
+
+ def lookup_network_object(self, uid: str, previous: bool = False) -> NetworkObject:
+ return self._lookup_object(uid, previous, lambda cfg: cfg.network_objects, "network object")
+
+ def lookup_service_object(self, uid: str, previous: bool = False) -> ServiceObject:
+ return self._lookup_object(uid, previous, lambda cfg: cfg.service_objects, "service object")
+
+ def lookup_user(self, uid: str, previous: bool = False) -> dict[str, Any]:
+ return self._lookup_object(uid, previous, lambda cfg: cfg.users, "user")
+
+ def lookup_zone(self, uid: str, previous: bool = False) -> dict[str, Any]:
+ return self._lookup_object(uid, previous, lambda cfg: cfg.zone_objects, "zone")
+
+ def is_ref_unchanged(self, ref_type: RefType, ref_uid: tuple[str, str | None] | str) -> bool:
+ """
+ Check if a reference object is unchanged between previous and current config.
+
+ Returns True if the object is the same in both configs, False if it changed.
+ """
+ if ref_type in (RefType.SRC, RefType.DST):
+ if not isinstance(ref_uid, tuple) or len(ref_uid) != 2: # noqa: PLR2004
+ raise TypeError(
+ f"ref_uid for {ref_type.name} must be a tuple of length 2, not {type(ref_uid).__name__}"
+ )
+ nwobj_uid, user_uid = ref_uid
+ prev_nwobj = self.lookup_network_object(nwobj_uid, previous=True)
+ curr_nwobj = self.lookup_network_object(nwobj_uid, previous=False)
+ prev_user = self.lookup_user(user_uid, previous=True) if user_uid else None
+ curr_user = self.lookup_user(user_uid, previous=False) if user_uid else None
+ return (prev_nwobj, prev_user) == (curr_nwobj, curr_user)
+
+ if not isinstance(ref_uid, str):
+ raise TypeError(f"ref_uid must be str, not {type(ref_uid).__name__}")
+
+ if ref_type == RefType.NWOBJ_RESOLVED:
+ return self.lookup_network_object(ref_uid, previous=True) == self.lookup_network_object(
+ ref_uid, previous=False
+ )
+ if ref_type in (RefType.SVC, RefType.SVC_RESOLVED):
+ return self.lookup_service_object(ref_uid, previous=True) == self.lookup_service_object(
+ ref_uid, previous=False
+ )
+ if ref_type == RefType.USER_RESOLVED:
+ return self.lookup_user(ref_uid, previous=True) == self.lookup_user(ref_uid, previous=False)
+ if ref_type in (RefType.SRC_ZONE, RefType.DST_ZONE):
+ return self.lookup_zone(ref_uid, previous=True) == self.lookup_zone(ref_uid, previous=False)
+
+ raise FwoImporterError(f"unknown ref type: {ref_type}")
+
+ def get_ref_remove_statement(
+ self, ref_type: RefType, rule_uid: str, ref_uid: tuple[str, str | None] | str
+ ) -> dict[str, Any]:
+ if ref_type in (RefType.SRC, RefType.DST):
+ nwobj_uid, user_uid = ref_uid
+ statement = {
+ "_and": [
+ {"rule_id": {"_eq": self.uid2id_mapper.get_rule_id(rule_uid, before_update=True)}},
+ {"obj_id": {"_eq": self.uid2id_mapper.get_network_object_id(nwobj_uid, before_update=True)}},
+ ]
+ }
+ if user_uid:
+ statement["_and"].append(
+ {"user_id": {"_eq": self.uid2id_mapper.get_user_id(user_uid, before_update=True)}}
+ )
+ else:
+ statement["_and"].append({"user_id": {"_is_null": True}})
+ return statement
+ if ref_type in (RefType.SVC, RefType.SVC_RESOLVED):
+ return {
+ "_and": [
+ {"rule_id": {"_eq": self.uid2id_mapper.get_rule_id(rule_uid, before_update=True)}},
+ {"svc_id": {"_eq": self.uid2id_mapper.get_service_object_id(ref_uid, before_update=True)}}, # type: ignore # ref_uid is str here # noqa: PGH003
+ ]
+ }
+ if ref_type == RefType.NWOBJ_RESOLVED:
+ return {
+ "_and": [
+ {"rule_id": {"_eq": self.uid2id_mapper.get_rule_id(rule_uid, before_update=True)}},
+ {"obj_id": {"_eq": self.uid2id_mapper.get_network_object_id(ref_uid, before_update=True)}}, # type: ignore # ref_uid is str here # noqa: PGH003
+ ]
+ }
+ if ref_type == RefType.USER_RESOLVED:
+ return {
+ "_and": [
+ {"rule_id": {"_eq": self.uid2id_mapper.get_rule_id(rule_uid, before_update=True)}},
+ {"user_id": {"_eq": self.uid2id_mapper.get_user_id(ref_uid, before_update=True)}}, # type: ignore # ref_uid is str here # noqa: PGH003
+ ]
+ }
+ if ref_type in (RefType.SRC_ZONE, 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)}}, # type: ignore # ref_uid is str here TODO: Cleanup ref_uid dict # noqa: PGH003
+ ]
+ }
+ raise FwoImporterError(f"unknown ref type: {ref_type}")
+
+ def get_outdated_refs_to_remove(
+ self, prev_rule: RuleNormalized, rule: RuleNormalized | None, remove_all: bool
+ ) -> dict[RefType, list[dict[str, Any]]]:
+ """
+ Get the references that need to be removed for a rule based on comparison with the previous rule.
+
+ Args:
+ prev_rule (RuleNormalized): The previous version of the rule.
+ rule (RuleNormalized): The current version of the rule.
+ prev_config (FwConfigNormalized): The previous configuration containing the rules.
+ remove_all (bool): If True, all references will be removed. If False, it will check for changes in references that need to be removed.
+
+ """
+ ref_uids: dict[RefType, list[tuple[str, str | None]] | list[str]] = {ref_type: [] for ref_type in RefType}
+
+ if rule is None:
+ return {}
+
+ if not remove_all:
+ ref_uids = self.get_rule_refs(rule)
+ prev_ref_uids = self.get_rule_refs(prev_rule, is_prev=True)
+ refs_to_remove: dict[RefType, list[dict[str, Any]]] = {}
+ for ref_type in RefType:
+ refs_to_remove[ref_type] = []
+ for prev_ref_uid in prev_ref_uids[ref_type]:
+ if prev_ref_uid in ref_uids[ref_type] and self.is_ref_unchanged(ref_type, prev_ref_uid):
+ continue # ref not removed or changed
+ # ref removed or changed
+ if prev_rule.rule_uid is None:
+ raise FwoImporterError(
+ f"previous reference UID is None: {prev_ref_uid} in rule {prev_rule.rule_uid}"
+ )
+ refs_to_remove[ref_type].append(
+ self.get_ref_remove_statement(ref_type, prev_rule.rule_uid, prev_ref_uid)
+ )
+ return refs_to_remove
+
+ def get_refs_to_remove(self, prev_config: FwConfigNormalized) -> dict[RefType, list[dict[str, Any]]]:
+ all_refs_to_remove: dict[RefType, list[dict[str, Any]]] = {ref_type: [] for ref_type in RefType}
+ for prev_rulebase in prev_config.rulebases:
+ if self.normalized_config is None:
+ raise FwoImporterError("cannot remove outdated refs: normalized_config is None")
+ rules = next((rb.rules for rb in self.normalized_config.rulebases if rb.uid == prev_rulebase.uid), None)
+ if rules is None:
+ continue
+ for prev_rule in prev_rulebase.rules.values():
+ uid = prev_rule.rule_uid
+ if uid is None:
+ raise FwoImporterError(f"rule UID is None: {prev_rule} in rulebase {prev_rulebase.name}")
+ rule_removed_or_changed = (
+ uid not in rules or prev_rule != rules[uid]
+ ) # rule removed or changed -> all refs need to be removed
+ rule_refs_to_remove = self.get_outdated_refs_to_remove(
+ prev_rule, rules.get(uid, None), rule_removed_or_changed
+ )
+ for ref_type, ref_statements in rule_refs_to_remove.items():
+ all_refs_to_remove[ref_type].extend(ref_statements)
+ return all_refs_to_remove
+
+ def remove_outdated_refs(self, prev_config: FwConfigNormalized):
+ all_refs_to_remove = self.get_refs_to_remove(prev_config)
+
+ if not any(all_refs_to_remove.values()):
+ return
+
+ import_mutation = FwoApi.get_graphql_code([fwo_const.GRAPHQL_QUERY_PATH + "rule/updateRuleRefs.graphql"])
+
+ query_variables: dict[str, Any] = {
+ "importId": self.import_details.state.import_id,
+ "ruleFroms": all_refs_to_remove[RefType.SRC],
+ "ruleTos": all_refs_to_remove[RefType.DST],
+ "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],
+ "ruleFromZones": all_refs_to_remove[RefType.SRC_ZONE],
+ "ruleToZones": all_refs_to_remove[RefType.DST_ZONE],
+ }
+
+ try:
+ import_result = self.import_details.api_call.call(
+ import_mutation, query_variables=query_variables, analyze_payload=True
+ )
+ if "errors" in import_result:
+ FWOLogger.error(f"failed to remove outdated rule references: {import_result['errors']!s}")
+ raise FwoApiWriteError(f"failed to remove outdated rule references: {import_result['errors']!s}")
+ _ = sum(import_result["data"][f"update_{ref_type.value}"].get("affected_rows", 0) for ref_type in RefType)
+ except Exception:
+ raise FwoApiWriteError(f"failed to remove outdated rule references: {traceback.format_exc()!s}")
+
+ def get_ref_add_statement(
+ self, ref_type: RefType, rule: RuleNormalized, ref_uid: tuple[str, str | None] | str
+ ) -> dict[str, Any]:
+ if rule.rule_uid is None:
+ raise FwoImporterError(
+ f"rule UID is None: {rule} in rulebase during get_ref_add_statement"
+ ) # should not happen
+
+ import_id = self.import_details.state.import_id
+ mgm_id = self.import_details.state.mgm_details.current_mgm_id
+
+ if ref_type == RefType.SRC:
+ nwobj_uid, user_uid = ref_uid
+ _ = self.uid2id_mapper.get_network_object_id(nwobj_uid) # check if nwobj exists
+ return RuleFrom(
+ rule_id=self.uid2id_mapper.get_rule_id(rule.rule_uid),
+ obj_id=self.uid2id_mapper.get_network_object_id(nwobj_uid),
+ user_id=self.uid2id_mapper.get_user_id(user_uid) if user_uid else None,
+ rf_create=import_id,
+ rf_last_seen=import_id, # TODO: to be removed in the future
+ negated=rule.rule_src_neg,
+ ).model_dump()
+ if ref_type == RefType.DST:
+ nwobj_uid, user_uid = ref_uid
+ return RuleTo(
+ rule_id=self.uid2id_mapper.get_rule_id(rule.rule_uid),
+ obj_id=self.uid2id_mapper.get_network_object_id(nwobj_uid),
+ user_id=self.uid2id_mapper.get_user_id(user_uid) if user_uid else None,
+ rt_create=import_id,
+ rt_last_seen=import_id, # TODO: to be removed in the future
+ negated=rule.rule_dst_neg,
+ ).model_dump()
+ if ref_type == RefType.SVC:
+ return RuleService(
+ rule_id=self.uid2id_mapper.get_rule_id(rule.rule_uid),
+ svc_id=self.uid2id_mapper.get_service_object_id(ref_uid), # type: ignore # ref_uid is str here TODO: Cleanup ref_uid dict # noqa: PGH003
+ rs_create=import_id,
+ rs_last_seen=import_id, # TODO: to be removed in the future
+ ).model_dump()
+ if ref_type == RefType.NWOBJ_RESOLVED:
+ return {
+ "mgm_id": mgm_id,
+ "rule_id": self.uid2id_mapper.get_rule_id(rule.rule_uid),
+ "obj_id": self.uid2id_mapper.get_network_object_id(ref_uid), # type: ignore # ref_uid is str here TODO: Cleanup ref_uid dict # noqa: PGH003
+ "created": import_id,
+ }
+ if ref_type == RefType.SVC_RESOLVED:
+ return {
+ "mgm_id": mgm_id,
+ "rule_id": self.uid2id_mapper.get_rule_id(rule.rule_uid),
+ "svc_id": self.uid2id_mapper.get_service_object_id(ref_uid), # type: ignore # ref_uid is str here TODO: Cleanup ref_uid dict # noqa: PGH003
+ "created": import_id,
+ }
+ if ref_type == RefType.USER_RESOLVED:
+ return {
+ "mgm_id": mgm_id,
+ "rule_id": self.uid2id_mapper.get_rule_id(rule.rule_uid),
+ "user_id": self.uid2id_mapper.get_user_id(ref_uid), # type: ignore # ref_uid is str here TODO: Cleanup ref_uid dict # noqa: PGH003
+ "created": import_id,
+ }
+ if ref_type in (RefType.SRC_ZONE, 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), # type: ignore # ref_uid is str here TODO: Cleanup ref_uid dict # noqa: PGH003
+ "created": import_id,
+ }
+ return None
+
+ def get_new_refs_to_add(
+ self, rule: RuleNormalized, prev_rule: RuleNormalized | None, add_all: bool
+ ) -> dict[RefType, list[dict[str, Any]]]:
+ """
+ Get the references that need to be added for a rule based on comparison with the previous rule.
+
+ Args:
+ rule (RuleNormalized): The current version of the rule.
+ prev_rule (RuleNormalized): The previous version of the rule.
+ prev_config (FwConfigNormalized): The previous configuration containing the rules.
+ add_all (bool): If True, all references will be added. If False, it will check for changes in references that need to be added.
+
+ """
+ prev_ref_uids: dict[RefType, list[tuple[str, str | None]] | list[str]] = {ref_type: [] for ref_type in RefType}
+ if not add_all and prev_rule is not None:
+ prev_ref_uids = self.get_rule_refs(prev_rule, is_prev=True)
+ ref_uids = self.get_rule_refs(rule)
+ refs_to_add: dict[RefType, list[dict[str, Any]]] = {}
+ for ref_type in RefType:
+ refs_to_add[ref_type] = []
+ for ref_uid in ref_uids[ref_type]:
+ if ref_uid in prev_ref_uids[ref_type] and self.is_ref_unchanged(ref_type, ref_uid):
+ continue # ref not added or changed
+ # ref added or changed
+ refs_to_add[ref_type].append(self.get_ref_add_statement(ref_type, rule, ref_uid))
+ return refs_to_add
+
+ def add_new_refs(self, prev_config: FwConfigNormalized):
+ all_refs_to_add: dict[RefType, list[dict[str, Any]]] = {ref_type: [] for ref_type in RefType}
+ if self.normalized_config is None:
+ raise FwoImporterError("cannot add new refs: normalized_config is None")
+ for rulebase in self.normalized_config.rulebases:
+ prev_rules: dict[str, RuleNormalized] = {}
+ prev_rules = next((rb.rules for rb in prev_config.rulebases if rb.uid == rulebase.uid), prev_rules)
+ for rule in rulebase.rules.values():
+ uid = rule.rule_uid
+ if uid is None:
+ raise FwoImporterError(f"rule UID is None: {rule} in rulebase {rulebase.name}")
+ rule_added_or_changed = (
+ uid not in prev_rules or rule != prev_rules[uid]
+ ) # rule added or changed -> all refs need to be added
+ rule_refs_to_add = self.get_new_refs_to_add(rule, prev_rules.get(uid, None), rule_added_or_changed)
+ for ref_type, ref_statements in rule_refs_to_add.items():
+ all_refs_to_add[ref_type].extend(ref_statements)
+
+ if not any(all_refs_to_add.values()):
+ return 0
+
+ import_mutation = FwoApi.get_graphql_code([fwo_const.GRAPHQL_QUERY_PATH + "rule/insertRuleRefs.graphql"])
+ query_variables = {
+ "ruleFroms": all_refs_to_add[RefType.SRC],
+ "ruleTos": all_refs_to_add[RefType.DST],
+ "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],
+ "ruleFromZones": all_refs_to_add[RefType.SRC_ZONE],
+ "ruleToZones": all_refs_to_add[RefType.DST_ZONE],
+ }
+
+ try:
+ import_result = self.import_details.api_call.call(import_mutation, query_variables=query_variables)
+ except Exception:
+ raise FwoApiWriteError(f"failed to add new rule references: {traceback.format_exc()!s}")
+ if "errors" in import_result:
+ raise FwoApiWriteError(f"failed to add new rule references: {import_result['errors']!s}")
+ return sum(import_result["data"][f"insert_{ref_type.value}"].get("affected_rows", 0) for ref_type in RefType)
+
+ def get_rules_by_id_with_ref_uids(
+ self, rule_ids: list[int]
+ ) -> list[dict[str, Any]]: # TODO: change return type to list[Rule] and cast
+ get_rule_uid_refs_query = FwoApi.get_graphql_code(
+ [fwo_const.GRAPHQL_QUERY_PATH + "rule/getRulesByIdWithRefUids.graphql"]
+ )
+ query_variables = {"ruleIds": rule_ids}
+
+ try:
+ import_result = self.import_details.api_call.call(get_rule_uid_refs_query, query_variables=query_variables)
+ if "errors" in import_result:
+ FWOLogger.exception(
+ f"fwconfig_import_rule:getRulesByIdWithRefUids - error in addNewRules: {import_result['errors']!s}"
+ )
+ return []
+ return import_result["data"]["rule"]
+ except Exception:
+ FWOLogger.exception(f"failed to get rules from API: {traceback.format_exc()!s}")
+ raise
+
+ def get_rules(self, rule_uids: dict[str, list[str]]) -> list[Rulebase]:
+ # TODO: seems unnecessary, as the rulebases should already have been created this way in the normalized config
+ rulebases: list[Rulebase] = []
+
+ if self.normalized_config is None:
+ raise FwoImporterError("cannot get rules: normalized_config is None")
+
+ for rb in self.normalized_config.rulebases:
+ if rb.uid in rule_uids:
+ filtered_rules = {uid: rule for uid, rule in rb.rules.items() if uid in rule_uids[rb.uid]}
+ rulebase = Rulebase(
+ name=rb.name, uid=rb.uid, mgm_uid=rb.mgm_uid, is_global=rb.is_global, rules=filtered_rules
+ )
+ rulebases.append(rulebase)
+ return rulebases
+
+ # assuming input of form:
+ # {'rule-uid1': {'rule_num': 17', ... }, 'rule-uid2': {'rule_num': 8, ...}, ... }
+ @staticmethod
+ def rule_dict_to_ordered_list_of_rule_uids(rules: dict[str, dict[str, Any]]) -> list[str]:
+ return sorted(rules, key=lambda x: rules[x]["rule_num"])
+
+ @staticmethod
+ def list_diff(old_rules: list[str], new_rules: list[str]) -> list[tuple[str, str]]:
+ diff = list(ndiff(old_rules, new_rules))
+ changes: list[tuple[str, str]] = []
+
+ for change in diff:
+ if change.startswith("- "):
+ changes.append(("delete", change[2:]))
+ elif change.startswith("+ "):
+ changes.append(("insert", change[2:]))
+ elif change.startswith(" "):
+ changes.append(("unchanged", change[2:]))
+
+ return changes
+
+ def _find_following_rules(
+ self, rule_uid: str, previous_rulebase: dict[str, int], rulebase_id: str
+ ) -> Generator[str]:
+ """
+ Helper method to find the next rule in self that has an existing rule number.
+
+ :param ruleUid: The ID of the current rule being processed.
+ :param previousRulebase: Dictionary of existing rule IDs and their rule_number values.
+ :return: Generator yielding rule IDs that appear after `current_rule_id` in self.new_rules.
+ """
+ found = False
+ if self.normalized_config is None:
+ raise FwoImporterError("cannot find following rules: normalized_config is None")
+ current_rulebase = self.normalized_config.get_rulebase(rulebase_id)
+ for current_uid in current_rulebase.rules:
+ if current_uid == rule_uid:
+ found = True
+ elif found and rule_uid in previous_rulebase:
+ yield current_uid
+
+ # adds new rule_metadatum to the database
+ def add_new_rule_metadata(self, new_rules: list[Rulebase]) -> tuple[int, list[int]]:
+ changes: int = 0
+ new_rule_ids: list[int] = []
+
+ add_new_rule_metadata_mutation = """mutation upsertRuleMetadata($ruleMetadata: [rule_metadata_insert_input!]!) {
+ insert_rule_metadata(objects: $ruleMetadata, on_conflict: {constraint: rule_metadata_rule_uid_unique, update_columns: [rule_last_modified]}) {
+ affected_rows
+ returning {
+ rule_metadata_id
+ }
+ }
+ }
+ """
+
+ add_new_rule_metadata: list[dict[str, Any]] = self.prepare_new_rule_metadata(new_rules)
+ query_variables = {"ruleMetadata": add_new_rule_metadata}
+
+ FWOLogger.debug(json.dumps(query_variables), 10) # just for debugging purposes
+
+ try:
+ import_result = self.import_details.api_call.call(
+ add_new_rule_metadata_mutation, query_variables=query_variables, analyze_payload=True
+ )
+ except Exception:
+ raise FwoApiWriteError(f"failed to write new RulesMetadata: {traceback.format_exc()!s}")
+ if "errors" in import_result:
+ raise FwoApiWriteError(f"failed to write new RulesMetadata: {import_result['errors']!s}")
+ # reduce change number by number of rulebases
+ changes = import_result["data"]["insert_rule_metadata"]["affected_rows"]
+
+ return changes, new_rule_ids
+
+ def add_rulebases_without_rules(self, new_rules: list[Rulebase]):
+ changes: int = 0
+
+ add_rulebases_without_rules_mutation = """mutation upsertRulebaseWithoutRules($rulebases: [rulebase_insert_input!]!) {
+ insert_rulebase(
+ objects: $rulebases,
+ on_conflict: {
+ constraint: unique_rulebase_mgm_id_uid,
+ update_columns: []
+ }
+ ) {
+ affected_rows
+ returning {
+ id
+ name
+ uid
+ }
+ }
+ }
+ """
+
+ new_rulebases_for_import: list[RulebaseForImport] = self.prepare_new_rulebases(new_rules)
+ query_variables = {
+ "rulebases": [rb.model_dump(by_alias=True, exclude_unset=True) for rb in new_rulebases_for_import]
+ }
+
+ try:
+ import_result = self.import_details.api_call.call(
+ add_rulebases_without_rules_mutation, query_variables=query_variables
+ )
+ except Exception:
+ FWOLogger.exception(f"fwo_api:importRules - error in addNewRules: {traceback.format_exc()!s}")
+ raise FwoApiWriteError(f"failed to write new rulebases: {traceback.format_exc()!s}")
+ if "errors" in import_result:
+ FWOLogger.exception(f"fwo_api:importRules - error in addNewRules: {import_result['errors']!s}")
+ raise FwoApiWriteError(f"failed to write new rulebases: {import_result['errors']!s}")
+ # reduce change number by number of rulebases
+ changes = import_result["data"]["insert_rulebase"]["affected_rows"]
+ new_rulebase_ids = (
+ [rulebase["id"] for rulebase in import_result["data"]["insert_rulebase"]["returning"]]
+ if changes > 0
+ else []
+ )
+ # finally, add the new rulebases to the map for next step (adding rulebase with rules)
+ self.import_details.set_rulebase_map()
+ return changes, new_rulebase_ids
+
+ # as we cannot add the rules for all rulebases in one go (using a constraint from the rule table),
+ # we need to add them per rulebase separately
+ # TODO: separation because of constraint still needed?
+ def add_rules_within_rulebases(self, rulebases: list[Rulebase]) -> tuple[int, list[dict[str, Any]]]:
+ """
+ Adds rules within the given rulebases to the database.
+
+ Args:
+ rulebases (list[Rulebase]): List of Rulebase objects containing rules to be added
+
+ Returns:
+ tuple[int, list[dict]]: A tuple containing the number of changes made and a list of dictionaries,
+ each with 'rule_id' and 'rule_uid' for each newly added rule.
+
+ """
+ changes: int = 0
+ new_rule_ids: list[dict[str, Any]] = []
+
+ upsert_rulebase_with_rules = """mutation upsertRules($rules: [rule_insert_input!]!) {
+ insert_rule(
+ objects: $rules,
+ on_conflict: { constraint: rule_unique_mgm_id_rule_uid_rule_create_xlate_rule, update_columns: [] }
+ ) { affected_rows, returning { rule_id, rule_uid } }
+ }
+ """
+ for rulebase in rulebases:
+ new_rules = self.prepare_rules_for_import(list(rulebase.rules.values()), rulebase.uid)
+ if len(new_rules) > 0:
+ query_variables = {"rules": [rule.model_dump() for rule in new_rules]}
+ try:
+ import_result = self.import_details.api_call.call(
+ upsert_rulebase_with_rules, query_variables=query_variables, analyze_payload=True
+ )
+ except Exception:
+ FWOLogger.exception(
+ f"fwo_api:addRulesWithinRulebases - error in addRulesWithinRulebases: {traceback.format_exc()!s}"
+ )
+ raise FwoApiWriteError(
+ f"failed to write rules of rulebase {rulebase.uid}: {traceback.format_exc()!s}"
+ )
+ if "errors" in import_result:
+ FWOLogger.exception(
+ f"fwo_api:addRulesWithinRulebases - error in addRulesWithinRulebases: {import_result['errors']!s}"
+ )
+ raise FwoApiWriteError(
+ f"failed to write rules of rulebase {rulebase.uid}: {import_result['errors']!s}"
+ )
+ changes += import_result["data"]["insert_rule"]["affected_rows"]
+ new_rule_ids += import_result["data"]["insert_rule"]["returning"]
+ return changes, new_rule_ids
+
+ # adds only new rules to the database
+ # unchanged or deleted rules are not touched here
+ def add_new_rules(self, rulebases: list[Rulebase]) -> tuple[int, list[dict[str, Any]]]:
+ # TODO: currently brute-forcing all rulebases and rules and depending on constraints to avoid duplicates. seems inefficient.
+ changes1, _ = self.add_rulebases_without_rules(rulebases)
+ changes2, new_rule_ids = self.add_rules_within_rulebases(rulebases)
+
+ return changes1 + changes2, new_rule_ids
+
+ def prepare_new_rule_metadata(self, new_rules: list[Rulebase]) -> list[dict[str, Any]]:
+ new_rule_metadata: list[dict[str, Any]] = []
+
+ now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+ for rulebase in new_rules:
+ for rule_uid, rule in rulebase.rules.items():
+ rm4import = RuleMetadatum(
+ rule_uid=rule_uid,
+ mgm_id=self.import_details.state.mgm_details.current_mgm_id,
+ rule_last_modified=now,
+ rule_created=now,
+ rule_last_hit=rule.last_hit,
+ )
+ new_rule_metadata.append(rm4import.model_dump())
+ # TODO: add other fields
+ return new_rule_metadata
+
+ # creates a structure of rulebases optinally including rules for import
+ def prepare_new_rulebases(self, new_rulebases: list[Rulebase]) -> list[RulebaseForImport]:
+ new_rules_for_import: list[RulebaseForImport] = []
+
+ for rulebase in new_rulebases:
+ rb4import = RulebaseForImport(
+ name=rulebase.name,
+ mgm_id=self.import_details.state.mgm_details.current_mgm_id,
+ uid=rulebase.uid,
+ is_global=self.import_details.state.mgm_details.current_mgm_is_super_manager,
+ created=self.import_details.state.import_id,
+ )
+ new_rules_for_import.append(rb4import)
+ # TODO: see where to get real UIDs (both for rulebase and manager)
+ # add rules for each rulebase
+ return new_rules_for_import
+
+ def mark_rules_removed(self, removed_rule_uids: dict[str, list[str]]) -> tuple[int, list[int]]:
+ changes = 0
+ collected_removed_rule_ids: list[int] = []
+
+ # TODO: make sure not to mark new (changed) rules as removed (order of calls!)
+
+ for rule_uids in removed_rule_uids.values():
+ removed_rule_ids = []
+ if len(rule_uids) > 0: # if nothing to remove, skip this
+ remove_mutation = """
+ mutation markRulesRemoved($importId: bigint!, $mgmId: Int!, $uids: [String!]!) {
+ update_rule(where: {removed: { _is_null: true }, rule_uid: {_in: $uids}, mgm_id: {_eq: $mgmId}}, _set: {removed: $importId, active:false}) {
+ affected_rows
+ returning { rule_id }
+ }
+ }
+ """
+ query_variables: dict[str, Any] = {
+ "importId": self.import_details.state.import_id,
+ "mgmId": self.import_details.state.mgm_details.current_mgm_id,
+ "uids": list(rule_uids),
+ }
+
+ try:
+ remove_result = self.import_details.api_call.call(remove_mutation, query_variables=query_variables)
+ except Exception:
+ raise FwoApiWriteError(f"failed to remove rules: {traceback.format_exc()!s}")
+ if "errors" in remove_result:
+ raise FwoApiWriteError(f"failed to remove rules: {remove_result['errors']!s}")
+ changes = int(remove_result["data"]["update_rule"]["affected_rows"])
+ removed_rule_ids = remove_result["data"]["update_rule"]["returning"]
+ collected_removed_rule_ids += [item["rule_id"] for item in removed_rule_ids]
+
+ return changes, collected_removed_rule_ids
+
+ def create_new_rule_version(self, rule_uids: dict[str, list[str]]) -> tuple[int, list[int], list[dict[str, Any]]]:
+ """
+ Creates new versions of rules specified in rule_uids by inserting new rule entries and marking the old ones as removed.
+
+ Args:
+ rule_uids (dict[str, list[str]]): A dictionary where keys are rulebase UIDs and values are lists of rule UIDs to be updated.
+
+ Returns:
+ tuple[int, list[int], list[dict]]: A tuple containing the number of changes made, a list of old rule IDs that were changed, and a list of newly inserted rule entries.
+
+ """
+ self._changed_rule_id_map = {}
+
+ if len(rule_uids) == 0:
+ return 0, [], []
+
+ create_new_rule_versions_mutation = """mutation createNewRuleVersions($objects: [rule_insert_input!]!, $uids: [String!], $mgmId: Int!, $importId: bigint) {
+ insert_rule(objects: $objects) {
+ affected_rows
+ returning {
+ rule_id
+ rule_src_refs
+ rule_dst_refs
+ rule_svc_refs
+ rule_to_zone
+ rule_from_zone
+ rule_src_neg
+ rule_dst_neg
+ rule_svc_neg
+ rulebase_id
+ rule_installon
+ rule_uid
+ }
+ }
+
+ update_rule(
+ where: {
+ removed: { _is_null: true },
+ rule_uid: { _in: $uids },
+ mgm_id: { _eq: $mgmId },
+ rule_last_seen: { _neq: $importId }
+ },
+ _set: {
+ removed: $importId,
+ active: false
+ }
+ ) {
+ affected_rows
+ returning {
+ rule_id
+ rule_uid
+ }
+ }
+ }
+ """
+
+ import_rules: list[Rule] = []
+
+ for rulebase_uid, rulebase_rules in rule_uids.items():
+ changed_rule_of_rulebase: list[RuleNormalized] = [
+ rule_with_changes
+ for rule_with_changes in self.rule_order_service.target_rules_flat
+ if rule_with_changes.rule_uid in rulebase_rules
+ ]
+
+ import_rules_of_rulebase = self.prepare_rules_for_import(changed_rule_of_rulebase, rulebase_uid)
+
+ import_rules.extend(import_rules_of_rulebase)
+
+ create_new_rule_version_variables: dict[str, Any] = {
+ "objects": [rule.model_dump() for rule in import_rules],
+ "uids": [rule.rule_uid for rule in import_rules],
+ "mgmId": self.import_details.state.mgm_details.current_mgm_id,
+ "importId": self.import_details.state.import_id,
+ }
+
+ try:
+ create_new_rule_version_result = self.import_details.api_call.call(
+ create_new_rule_versions_mutation, query_variables=create_new_rule_version_variables
+ )
+ except Exception:
+ raise FwoApiWriteError(f"failed to move rules: {traceback.format_exc()!s}")
+ if "errors" in create_new_rule_version_result:
+ raise FwoApiWriteError(f"failed to create new rule versions: {create_new_rule_version_result['errors']!s}")
+ changes = int(create_new_rule_version_result["data"]["update_rule"]["affected_rows"])
+ update_rules_return: list[dict[str, Any]] = create_new_rule_version_result["data"]["update_rule"]["returning"]
+ insert_rules_return: list[dict[str, Any]] = create_new_rule_version_result["data"]["insert_rule"]["returning"]
+
+ self._changed_rule_id_map = {
+ update_item["rule_id"]: next(
+ insert_item["rule_id"]
+ for insert_item in insert_rules_return
+ if insert_item["rule_uid"] == update_item["rule_uid"]
+ )
+ for update_item in update_rules_return
+ }
+
+ collected_changed_rule_ids: list[int] = list(self._changed_rule_id_map.keys()) or []
+
+ return changes, collected_changed_rule_ids, insert_rules_return
+
+ def update_rule_enforced_on_gateway_after_move(
+ self, insert_rules_return: list[dict[str, Any]], update_rules_return: list[dict[str, Any]]
+ ) -> tuple[int, int, list[str]]:
+ """
+ Updates the db table rule_enforced_on_gateway by creating new entries for a list of rule_ids and setting the old versions of said rules removed.
+ """
+ id_map: dict[int, int] = {}
+
+ for insert_rules_return_entry in insert_rules_return:
+ id_map[insert_rules_return_entry["rule_id"]] = next(
+ update_rules_return_entry["rule_id"]
+ for update_rules_return_entry in update_rules_return
+ if update_rules_return_entry["rule_uid"] == insert_rules_return_entry["rule_uid"]
+ )
+
+ set_rule_enforced_on_gateway_entries_removed_mutation = """mutation set_rule_enforced_on_gateway_entries_removed($rule_ids: [Int!], $importId: bigint) {
+ update_rule_enforced_on_gateway(
+ where: {
+ rule_id: { _in: $rule_ids },
+ },
+ _set: {
+ removed: $importId,
+ }
+ ) {
+ affected_rows
+ returning {
+ rule_id
+ dev_id
+ }
+ }
+ }
+ """
+
+ set_rule_enforced_on_gateway_entries_removed_variables: dict[str, Any] = {
+ "rule_ids": list(id_map.values()),
+ "importId": self.import_details.state.import_id,
+ }
+
+ insert_rule_enforced_on_gateway_entries_mutation = """
+ mutation insert_rule_enforced_on_gateway_entries($new_entries: [rule_enforced_on_gateway_insert_input!]!) {
+ insert_rule_enforced_on_gateway(
+ objects: $new_entries
+ ) {
+ affected_rows
+ }
+ }
+ """
+
+ try:
+ set_rule_enforced_on_gateway_entries_removed_result = self.import_details.api_call.call(
+ set_rule_enforced_on_gateway_entries_removed_mutation,
+ set_rule_enforced_on_gateway_entries_removed_variables,
+ )
+
+ if "errors" in set_rule_enforced_on_gateway_entries_removed_result:
+ FWOLogger.exception(
+ f"fwo_api:update_rule_enforced_on_gateway_after_move - error while updating moved rules refs: {set_rule_enforced_on_gateway_entries_removed_result['errors']!s}"
+ )
+ return 1, 0, []
+
+ insert_rule_enforced_on_gateway_entries_variables: dict[str, Any] = {
+ "new_entries": [
+ {
+ "rule_id": new_id,
+ "dev_id": next(
+ entry
+ for entry in set_rule_enforced_on_gateway_entries_removed_result["data"][
+ "update_rule_enforced_on_gateway"
+ ]["returning"]
+ if entry["rule_id"] == id_map[new_id]
+ )["dev_id"],
+ "created": self.import_details.state.import_id,
+ }
+ for new_id in id_map
+ ]
+ }
+
+ insert_rule_enforced_on_gateway_entries_result = self.import_details.api_call.call(
+ insert_rule_enforced_on_gateway_entries_mutation, insert_rule_enforced_on_gateway_entries_variables
+ )
+
+ if "errors" in insert_rule_enforced_on_gateway_entries_result:
+ FWOLogger.exception(
+ f"fwo_api:update_rule_enforced_on_gateway_after_move - error while updating moved rules refs: {insert_rule_enforced_on_gateway_entries_result['errors']!s}"
+ )
+ return 1, 0, []
+
+ return 0, 0, []
+
+ except Exception:
+ FWOLogger.exception(f"failed to move rules: {traceback.format_exc()!s}")
+ return 1, 0, []
+
+ def verify_rules_moved(self, changed_rule_uids: dict[str, list[str]]) -> tuple[int, list[str]]:
+ number_of_moved_rules = 0
+
+ moved_rule_uids: list[str] = []
+
+ changed_rule_uids_flat = [uid for uids in changed_rule_uids.values() for uid in uids]
+
+ rule_order_service_moved_rule_uids_flat = [
+ rule_uid
+ for rule_uids in self.rule_order_service._moved_rule_uids.values() # type: ignore #TODO: access to protected member # noqa: SLF001, PGH003
+ for rule_uid in rule_uids
+ ]
+
+ for rule_uid in rule_order_service_moved_rule_uids_flat:
+ if rule_uid in changed_rule_uids_flat:
+ moved_rule_uids.append(rule_uid)
+ number_of_moved_rules += 1
+
+ return number_of_moved_rules, moved_rule_uids
+
+ # TODO: limit query to a single rulebase
+ def get_rule_num_map(self) -> dict[str, dict[str, float]]:
+ query = "query getRuleNumMap($mgmId: Int) { rule(where:{mgm_id:{_eq:$mgmId}}) { rule_uid rulebase_id rule_num_numeric } }"
+ try:
+ result = self.import_details.api_call.call(
+ query=query, query_variables={"mgmId": self.import_details.state.mgm_details.current_mgm_id}
+ )
+ except Exception:
+ FWOLogger.error("Error while getting rule number map")
+ return {}
+
+ rule_num_map: dict[str, dict[str, float]] = {}
+ for rule_num in result["data"]["rule"]:
+ if rule_num["rulebase_id"] not in rule_num_map:
+ rule_num_map.update({rule_num["rulebase_id"]: {}}) # initialize rulebase
+ rule_num_map[rule_num["rulebase_id"]].update({rule_num["rule_uid"]: rule_num["rule_num_numeric"]})
+ return rule_num_map
+
+ def get_next_rule_num_map(self) -> dict[str, float]: # TODO: implement!
+ query = "query getRuleNumMap { rule { rule_uid rule_num_numeric } }"
+ try:
+ _ = self.import_details.api_call.call(query=query, query_variables={})
+ except Exception:
+ FWOLogger.error("Error while getting rule number")
+ return {}
+
+ rule_num_map: dict[str, float] = {}
+ return rule_num_map
+
+ def get_rule_type_map(self) -> dict[str, int]:
+ query = "query getTrackMap { stm_track { track_name track_id } }"
+ try:
+ result = self.import_details.api_call.call(query=query, query_variables={})
+ except Exception:
+ FWOLogger.error("Error while getting stm_track")
+ return {}
+
+ rule_type_map: dict[str, int] = {}
+ for track in result["data"]["stm_track"]:
+ rule_type_map.update({track["track_name"]: track["track_id"]})
+ return rule_type_map
+
+ def get_current_rules(self, import_id: int, mgm_id: int, rulebase_name: str) -> list[list[Any]] | None:
+ query_variables: dict[str, Any] = {"importId": import_id, "mgmId": mgm_id, "rulebaseName": rulebase_name}
+ query = """
+ query get_rulebase($importId: bigint!, $mgmId: Int!, $rulebaseName: String!) {
+ rulebase(where: {mgm_id: {_eq: $mgmId}, name: {_eq: $rulebaseName}}) {
+ id
+ rules(where: {rule: {rule_create: {_lt: $importId}, removed: {_is_null: true}}}, order_by: {rule: {rule_num_numeric: asc}}) {
+ rule_num
+ rule_num_numeric
+ rule_uid
+ }
+ }
+ }
+ """
+
+ try:
+ query_result = self.import_details.api_call.call(query, query_variables=query_variables)
+ except Exception:
+ FWOLogger.error(f"error while getting current rulebase: {traceback.format_exc()!s}")
+ return None
+
+ try:
+ rule_list = query_result["data"]["rulebase"][0]["rules"]
+ except Exception:
+ FWOLogger.error(f"could not find rules in query result: {query_result}")
+ return None
+
+ rules: list[list[Any]] = [
+ [rule["rule"]["rule_num"], rule["rule"]["rule_num_numeric"], rule["rule"]["rule_uid"]] for rule in rule_list
+ ]
+ return rules
+
+ def insert_rulebase(self, rulebase_name: str, is_global: bool = False):
+ # call for each rulebase to add
+ query_variables: dict[str, Any] = {
+ "rulebase": {
+ "is_global": is_global,
+ "mgm_id": self.import_details.state.mgm_details.current_mgm_id,
+ "name": rulebase_name,
+ "created": self.import_details.state.import_id,
+ }
+ }
+
+ mutation = """
+ mutation upsertRulebaseWithRules($rulebases: [rulebase_insert_input!]!) {
+ insert_rulebase(
+ objects: $rulebases,
+ on_conflict: {
+ constraint: unique_rulebase_mgm_id_uid,
+ update_columns: [created, is_global]
+ }
+ ) {
+ returning {
+ id
+ name
+ rule_id
+ rulebase_id
+ }
+ }
+ }
+ """
+ return self.import_details.api_call.call(mutation, query_variables=query_variables)
+
+ def import_insert_rulebase_on_gateway(self, rulebase_id: int, dev_id: int, order_num: int = 0):
+ query_variables: dict[str, Any] = {
+ "rulebase2gateway": [{"dev_id": dev_id, "rulebase_id": rulebase_id, "order_no": order_num}]
+ }
+ mutation = """
+ mutation importInsertRulebaseOnGateway($rulebase2gateway: [rulebase_on_gateway_insert_input!]!) {
+ insert_rulebase_on_gateway(objects: $rulebase2gateway) {
+ affected_rows
+ }
+ }"""
+
+ return self.import_details.api_call.call(mutation, query_variables=query_variables)
+
+ def _get_list_of_enforced_gateways(
+ self, rule: RuleNormalized, import_details: ImportStateController
+ ) -> list[int] | None:
+ if rule.rule_installon is None:
+ return None
+ enforced_gw_ids: list[int] = []
+ for gw_uid in rule.rule_installon.split(fwo_const.LIST_DELIMITER):
+ gw_id = import_details.state.lookup_gateway_id(gw_uid)
+ if gw_id is None:
+ FWOLogger.warning(f"could not find gateway id for gateway uid {gw_uid} during rule import preparation")
+ continue
+ enforced_gw_ids.append(gw_id)
+ if len(enforced_gw_ids) == 0:
+ return None
+
+ return enforced_gw_ids
+
+ def prepare_rules_for_import(self, rules: list[RuleNormalized], rulebase_uid: str) -> list[Rule]:
+ # get rulebase_id for rulebaseUid
+ rulebase_id = self.import_details.state.lookup_rulebase_id(rulebase_uid)
+
+ return [self.prepare_single_rule_for_import(rule, self.import_details, rulebase_id) for rule in rules]
+
+ def prepare_single_rule_for_import(
+ self, rule: RuleNormalized, import_details: ImportStateController, rulebase_id: int
+ ) -> Rule:
+ return Rule(
+ mgm_id=import_details.state.mgm_details.current_mgm_id,
+ rule_num=rule.rule_num,
+ rule_disabled=rule.rule_disabled,
+ rule_src_neg=rule.rule_src_neg,
+ rule_src=rule.rule_src,
+ rule_src_refs=rule.rule_src_refs,
+ rule_dst_neg=rule.rule_dst_neg,
+ rule_dst=rule.rule_dst,
+ rule_dst_refs=rule.rule_dst_refs,
+ rule_svc_neg=rule.rule_svc_neg,
+ rule_svc=rule.rule_svc,
+ rule_svc_refs=rule.rule_svc_refs,
+ rule_action=rule.rule_action,
+ rule_track=rule.rule_track,
+ rule_time=rule.rule_time,
+ rule_name=rule.rule_name,
+ rule_uid=rule.rule_uid,
+ rule_custom_fields=rule.rule_custom_fields,
+ rule_implied=rule.rule_implied,
+ rule_comment=rule.rule_comment,
+ 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,
+ rulebase_id=rulebase_id,
+ rule_create=import_details.state.import_id,
+ rule_last_seen=import_details.state.import_id,
+ rule_num_numeric=rule.rule_num_numeric,
+ action_id=import_details.state.lookup_action(rule.rule_action),
+ track_id=import_details.state.lookup_track(rule.rule_track),
+ rule_head_text=rule.rule_head_text,
+ rule_installon=rule.rule_installon,
+ last_change_admin=None, # TODO: get id from rule.last_change_admin
+ )
+
+ def write_changelog_rules(self, added_rules_ids: list[int], removed_rules_ids: list[int]):
+ changelog_rule_insert_objects = self.prepare_changelog_rules_insert_objects(added_rules_ids, removed_rules_ids)
+
+ update_changelog_rules = FwoApi.get_graphql_code(
+ [fwo_const.GRAPHQL_QUERY_PATH + "rule/updateChanglogRules.graphql"]
+ )
+
+ query_variables = {"rule_changes": changelog_rule_insert_objects}
+
+ if len(changelog_rule_insert_objects) > 0:
+ try:
+ update_changelog_rules_result = self.import_details.api_call.call(
+ update_changelog_rules, query_variables=query_variables, analyze_payload=True
+ )
+ if "errors" in update_changelog_rules_result:
+ FWOLogger.exception(
+ f"error while adding changelog entries for objects: {update_changelog_rules_result['errors']!s}"
+ )
+ except Exception:
+ FWOLogger.exception(
+ f"fatal error while adding changelog entries for objects: {traceback.format_exc()!s}"
+ )
+
+ def prepare_changelog_rules_insert_objects(
+ self, added_rules_ids: list[int], removed_rules_ids: list[int]
+ ) -> list[dict[str, Any]]:
+ """
+ Creates two lists of insert arguments for the changelog_rules db table, one for new rules, one for deleted.
+ """
+ change_logger = ChangeLogger()
+ changelog_rule_insert_objects: list[dict[str, Any]] = []
+ import_time = datetime.now().isoformat()
+ change_typ = 3
+
+ if self.import_details.state.is_full_import or self.import_details.state.is_clearing_import:
+ change_typ = 2 # TODO: Somehow all imports are treated as im operation.
+
+ changelog_rule_insert_objects.extend(
+ [
+ change_logger.create_changelog_import_object(
+ "rule", self.import_details.state, "I", change_typ, import_time, rule_id
+ )
+ for rule_id in added_rules_ids
+ ]
+ )
+
+ changelog_rule_insert_objects.extend(
+ [
+ change_logger.create_changelog_import_object(
+ "rule", self.import_details.state, "D", change_typ, import_time, rule_id
+ )
+ for rule_id in removed_rules_ids
+ ]
+ )
+
+ changelog_rule_insert_objects.extend(
+ [
+ change_logger.create_changelog_import_object(
+ "rule", self.import_details.state, "C", change_typ, import_time, new_rule_id, old_rule_id
+ )
+ for old_rule_id, new_rule_id in self._changed_rule_id_map.items()
+ ]
+ )
+
+ return changelog_rule_insert_objects
diff --git a/roles/importer/files/importer/model_controllers/fwconfig_import_ruleorder.py b/roles/importer/files/importer/model_controllers/fwconfig_import_ruleorder.py
new file mode 100644
index 0000000000..7135097b23
--- /dev/null
+++ b/roles/importer/files/importer/model_controllers/fwconfig_import_ruleorder.py
@@ -0,0 +1,455 @@
+from typing import TYPE_CHECKING, Any
+
+from fwo_const import RULE_NUM_NUMERIC_STEPS
+from fwo_exceptions import FwoApiFailureError
+from models.rule import RuleNormalized
+from models.rulebase import Rulebase
+from services.enums import Services
+from services.global_state import GlobalState
+from services.service_provider import ServiceProvider
+
+if TYPE_CHECKING:
+ from models.fwconfig_normalized import FwConfigNormalized
+
+# Constants
+CONFIG_OBJECTS_NOT_INITIALIZED_ERROR = (
+ "Config objects in global state not correctly initialized — expected non-None values."
+)
+
+
+class RuleOrderService:
+ """
+ A singleton service that holds data and provides logic to compute rule order values.
+ """
+
+ _service_provider: ServiceProvider
+ _global_state: GlobalState
+ _normalized_config: "FwConfigNormalized | None"
+ _previous_config: "FwConfigNormalized | None"
+
+ _target_rule_uids: list[str]
+ _target_rules_flat: list[RuleNormalized]
+ _source_rule_uids: list[str]
+ _source_rules_flat: list[RuleNormalized]
+
+ _min_moves: dict[str, Any]
+
+ _deleted_rule_uids: dict[str, list[str]]
+ _new_rule_uids: dict[str, list[str]]
+ _moved_rule_uids: dict[str, list[str]]
+
+ _inserts_and_moves: dict[str, list[str]]
+ _updated_rules: list[str]
+
+ def __init__(self):
+ self._initialize(set_configs=False)
+
+ @property
+ def target_rules_flat(self) -> list[RuleNormalized]:
+ return self._target_rules_flat
+
+ def update_rule_order_diffs(self) -> dict[str, dict[str, list[str]]]:
+ """
+ Determines diffs that are relevant for the rule order and updates rule_num_numeric in the corresponding rules in normalized config.
+ """
+ self._initialize()
+ self._calculate_necessary_transformations()
+ self._update_rule_num_numerics()
+
+ return {
+ "deleted_rule_uids": self._deleted_rule_uids,
+ "new_rule_uids": self._new_rule_uids,
+ "moved_rule_uids": self._moved_rule_uids,
+ }
+
+ def _initialize(self, set_configs: bool = True) -> None:
+ """
+ Prepares rule order service to calculate rule order diffs and update rule num numerics.
+ Expects previous config object and normalized config object in global state.
+ """
+ # Get logger and global state.
+
+ self._service_provider = ServiceProvider()
+ self._global_state = self._service_provider.get_service(Services.GLOBAL_STATE)
+
+ # Verifies config objects in global state.
+
+ if set_configs: # make premature initialization via __init__ possible
+ if not self._global_state.normalized_config or not self._global_state.previous_config:
+ raise ValueError(CONFIG_OBJECTS_NOT_INITIALIZED_ERROR)
+
+ # Instantiate objects for next steps.
+
+ self._normalized_config = self._global_state.normalized_config
+ self._previous_config = self._global_state.previous_config
+
+ self._deleted_rule_uids = {}
+ self._new_rule_uids = {}
+ self._moved_rule_uids = {}
+
+ self._inserts_and_moves = {}
+ self._updated_rules = []
+
+ def _calculate_necessary_transformations(self):
+ if self._normalized_config is None or self._previous_config is None:
+ raise ValueError(CONFIG_OBJECTS_NOT_INITIALIZED_ERROR)
+
+ # Parse rules from config to flat lists.
+
+ self._target_rule_uids, self._target_rules_flat = self._parse_rule_uids_and_objects_from_config(
+ self._normalized_config
+ )
+ self._source_rule_uids, self._source_rules_flat = self._parse_rule_uids_and_objects_from_config(
+ self._previous_config
+ )
+
+ # Compute necessary mutations.
+
+ from fwo_base import compute_min_moves # lazy import to avoid circular import conflict # noqa: PLC0415
+
+ self._min_moves = compute_min_moves(
+ self._source_rule_uids, self._target_rule_uids
+ ) # use flat lists to get moves across rulebases right
+
+ # Translate _min_moves to rulebase uid keyed dictionaries of rule uid lists.
+
+ for rulebase in self._normalized_config.rulebases:
+ previous_rulebase_uids: list[str] = []
+ previous_configs_rulebase = next(
+ (rb for rb in self._previous_config.rulebases if rb.uid == rulebase.uid), None
+ )
+ if previous_configs_rulebase:
+ previous_rulebase_uids.extend(list(previous_configs_rulebase.rules.keys()))
+
+ rule_uids = list(rulebase.rules.keys())
+
+ self._new_rule_uids[rulebase.uid] = [
+ insertion_uid for _, insertion_uid in self._min_moves["insertions"] if insertion_uid in rule_uids
+ ]
+
+ self._moved_rule_uids[rulebase.uid] = [
+ move_uid for _, move_uid, _ in self._min_moves["reposition_moves"] if move_uid in rule_uids
+ ]
+
+ # Add undetected moves (i.e. across rulebases).
+ for rule_uid in rule_uids:
+ if (
+ rule_uid not in self._new_rule_uids[rulebase.uid]
+ and rule_uid not in self._moved_rule_uids[rulebase.uid]
+ and rule_uid not in previous_rulebase_uids
+ ):
+ self._moved_rule_uids[rulebase.uid].append(rule_uid)
+
+ self._inserts_and_moves[rulebase.uid] = []
+ self._inserts_and_moves[rulebase.uid].extend(self._new_rule_uids[rulebase.uid])
+ self._inserts_and_moves[rulebase.uid].extend(self._moved_rule_uids[rulebase.uid])
+
+ for rulebase in self._previous_config.rulebases:
+ rule_uids = list(rulebase.rules.keys())
+
+ self._deleted_rule_uids[rulebase.uid] = [
+ deletion_uid for _, deletion_uid in self._min_moves["deletions"] if deletion_uid in rule_uids
+ ]
+
+ def _update_rule_num_numerics(self):
+ # Set initial rule_num_numerics if it is the first import.
+
+ if len(self._source_rules_flat) == 0:
+ self._set_initial_rule_num_numerics()
+ return
+
+ for rulebase_uid, rule_uids in self._inserts_and_moves.items():
+ for rule_uid in rule_uids:
+ # Compute value if it is a consecutive insert.
+
+ if self._is_part_of_consecutive_insert(rule_uid):
+ self._update_rule_on_consecutive_insert(rule_uid, rulebase_uid)
+ self._updated_rules.append(rule_uid)
+
+ # Handle singular inserts and moves.
+
+ elif self._is_rule_uid_in_return_object(
+ rule_uid, self._new_rule_uids
+ ) or self._is_rule_uid_in_return_object(rule_uid, self._moved_rule_uids):
+ self._update_rule_on_move_or_insert(rule_uid, rulebase_uid)
+ self._updated_rules.append(rule_uid)
+
+ # Raise if unexpected rule uid.
+
+ else:
+ raise FwoApiFailureError(message="RuleOrderService: Unexpected rule_uid.")
+
+ def _set_initial_rule_num_numerics(self):
+ for rule_uids in self._inserts_and_moves.values():
+ current_rule_num_numeric = 0
+ for rule_uid in rule_uids:
+ _, changed_rule = self._get_index_and_rule_object_from_flat_list(self._target_rules_flat, rule_uid)
+ current_rule_num_numeric += RULE_NUM_NUMERIC_STEPS
+ changed_rule.rule_num_numeric = current_rule_num_numeric
+
+ def _update_rule_on_move_or_insert(self, rule_uid: str, target_rulebase_uid: str) -> None:
+ next_rules_rule_num_numeric = 0.0
+ previous_rule_num_numeric = 0.0
+
+ if self._normalized_config is None or self._previous_config is None:
+ raise ValueError(CONFIG_OBJECTS_NOT_INITIALIZED_ERROR)
+ target_rulebase = next(
+ (rulebase for rulebase in self._normalized_config.rulebases if rulebase.uid == target_rulebase_uid), None
+ )
+ unchanged_target_rulebase = next(
+ (rulebase for rulebase in self._previous_config.rulebases if rulebase.uid == target_rulebase_uid), None
+ )
+
+ if target_rulebase is None:
+ return
+ changed_and_unchanged_rules = list(target_rulebase.rules.values())
+
+ if unchanged_target_rulebase:
+ changed_and_unchanged_rules.extend(list(unchanged_target_rulebase.rules.values()))
+
+ index, changed_rule = self._get_index_and_rule_object_from_flat_list(
+ list(target_rulebase.rules.values()), rule_uid
+ )
+ prev_rule_uid, next_rule_uid = self._get_adjacent_list_element(list(target_rulebase.rules.keys()), index)
+
+ if not prev_rule_uid:
+ min_num_numeric_rule = min(
+ (r for r in changed_and_unchanged_rules if r.rule_num_numeric != 0),
+ key=lambda x: x.rule_num_numeric,
+ default=None,
+ )
+
+ if min_num_numeric_rule:
+ changed_rule.rule_num_numeric = max(min_num_numeric_rule.rule_num_numeric / 2, 1)
+ else:
+ changed_rule.rule_num_numeric = RULE_NUM_NUMERIC_STEPS
+
+ elif not next_rule_uid:
+ changed_rule.rule_num_numeric = RULE_NUM_NUMERIC_STEPS
+
+ max_num_numeric_rule = max(changed_and_unchanged_rules, key=lambda x: x.rule_num_numeric, default=None)
+
+ if max_num_numeric_rule:
+ changed_rule.rule_num_numeric += max_num_numeric_rule.rule_num_numeric
+
+ else:
+ previous_rule_num_numeric = self._get_relevant_rule_num_numeric(
+ prev_rule_uid, ascending=False, target_rulebase=target_rulebase
+ )
+ next_rules_rule_num_numeric = self._get_relevant_rule_num_numeric(
+ next_rule_uid, ascending=True, target_rulebase=target_rulebase
+ )
+ if next_rules_rule_num_numeric > 0:
+ changed_rule.rule_num_numeric = (previous_rule_num_numeric + next_rules_rule_num_numeric) / 2
+ else:
+ changed_rule.rule_num_numeric = previous_rule_num_numeric + RULE_NUM_NUMERIC_STEPS
+
+ def _update_rule_on_consecutive_insert(self, rule_uid: str, rulebase_uid: str) -> None:
+ index, rule = self._get_index_and_rule_object_from_flat_list(self._target_rules_flat, rule_uid)
+ _index = index
+ prev_rule_num_numeric = 0
+ next_rule_num_numeric = 0
+
+ if self._normalized_config is None:
+ raise ValueError(CONFIG_OBJECTS_NOT_INITIALIZED_ERROR)
+ target_rulebase = next(
+ rulebase for rulebase in self._normalized_config.rulebases if rulebase.uid == rulebase_uid
+ )
+
+ while prev_rule_num_numeric == 0:
+ prev_rule_uid, _ = self._get_adjacent_list_element(self._target_rule_uids, _index)
+
+ if prev_rule_uid and prev_rule_uid in list(
+ next(
+ rulebase for rulebase in self._normalized_config.rulebases if rulebase.uid == rulebase_uid
+ ).rules.keys()
+ ):
+ prev_rule_num_numeric = self._get_relevant_rule_num_numeric(
+ prev_rule_uid, ascending=False, target_rulebase=target_rulebase
+ )
+ _index -= 1
+ else:
+ break
+
+ _index = index
+
+ while next_rule_num_numeric == 0:
+ _, next_rule_uid = self._get_adjacent_list_element(self._target_rule_uids, _index)
+
+ if next_rule_uid and next_rule_uid in list(
+ next(
+ rulebase for rulebase in self._normalized_config.rulebases if rulebase.uid == rulebase_uid
+ ).rules.keys()
+ ):
+ next_rule_num_numeric = self._get_relevant_rule_num_numeric(
+ next_rule_uid, ascending=True, target_rulebase=target_rulebase
+ )
+ _index += 1
+ else:
+ break
+
+ if next_rule_num_numeric == 0:
+ next_rule_num_numeric = prev_rule_num_numeric + RULE_NUM_NUMERIC_STEPS
+ rule.rule_num_numeric = next_rule_num_numeric
+ return
+
+ rule.rule_num_numeric = (prev_rule_num_numeric + next_rule_num_numeric) / 2
+
+ def _parse_rule_uids_and_objects_from_config(
+ self, config: "FwConfigNormalized"
+ ) -> tuple[list[str], list[RuleNormalized]]:
+ uids_and_rules = [
+ (rule_uid, rule) for rulebase in config.rulebases for rule_uid, rule in rulebase.rules.items()
+ ]
+
+ if not uids_and_rules:
+ return ([], [])
+
+ uids, rules = zip(*uids_and_rules, strict=False)
+ return (list(uids), list(rules))
+
+ def _is_part_of_consecutive_insert(self, rule_uid: str):
+ # Only inserts.
+
+ if not self._is_rule_uid_in_return_object(rule_uid, self._new_rule_uids):
+ return False
+
+ # Cant be consecutive, if there is only one insert
+
+ number_of_rule_uids = 0
+
+ for rulebase in self._new_rule_uids.values():
+ number_of_rule_uids += len(rulebase)
+
+ if number_of_rule_uids < 2: # noqa: PLR2004
+ return False
+
+ # Evaluate adjacent rule_uids
+
+ index, _ = self._get_index_and_rule_object_from_flat_list(self._target_rules_flat, rule_uid)
+
+ prev_rule_uid, next_rule_uid = self._get_adjacent_list_element(self._target_rule_uids, index)
+
+ if prev_rule_uid and self._is_rule_uid_in_return_object(prev_rule_uid, self._new_rule_uids):
+ return True
+
+ if next_rule_uid and self._is_rule_uid_in_return_object(next_rule_uid, self._new_rule_uids):
+ return True
+ return None
+
+ def _get_adjacent_list_element(self, lst: list[str], index: int) -> tuple[str | None, str | None]:
+ if not lst or index < 0 or index >= len(lst):
+ return None, None
+
+ prev_item = lst[index - 1] if index - 1 >= 0 else None
+ next_item = lst[index + 1] if index + 1 < len(lst) else None
+ return prev_item, next_item
+
+ def _get_index_and_rule_object_from_flat_list(self, flat_list: list[RuleNormalized], rule_uid: str):
+ return next((i, rule) for i, rule in enumerate(flat_list) if rule.rule_uid == rule_uid)
+
+ def _get_relevant_rule_num_numeric(
+ self,
+ rule_uid: str,
+ ascending: bool,
+ target_rulebase: Rulebase,
+ ) -> float:
+ """
+ Returns the relevant rule_num_numeric for rule_uid.
+ - Prefers already updated rules
+ - Handles consecutive inserts
+ - Handles new/moved rules relative to neighbors in the target
+ - Falls back to the source rules
+ Always returns a numeric value.
+ """
+ # 1) Already updated rule? -> simple return
+ if rule_uid in self._updated_rules:
+ _, rule = self._get_index_and_rule_object_from_flat_list(self._target_rules_flat, rule_uid)
+ return float(rule.rule_num_numeric)
+
+ # 2) Part of a consecutive insert? -> defined value (0)
+ if self._is_part_of_consecutive_insert(rule_uid):
+ return 0.0
+
+ # 3) New or moved rule? -> determine neighbors in the target
+ if self._is_rule_uid_in_return_object(rule_uid, self._new_rule_uids) or self._is_rule_uid_in_return_object(
+ rule_uid, self._moved_rule_uids
+ ):
+ return self._compute_num_for_changed_rule(rule_uid, ascending, target_rulebase)
+
+ # 4) Fallback: value from the source rules
+ _, rule = self._get_index_and_rule_object_from_flat_list(self._source_rules_flat, rule_uid)
+ return float(rule.rule_num_numeric)
+
+ def _compute_num_for_changed_rule(self, rule_uid: str, ascending: bool, target_rulebase: Rulebase) -> float:
+ """Calculates rule_num_numeric for a new/moved rule relative to its neighbors in the target."""
+ # Get rule & neighbors in the target
+ index, changed_rule = self._get_index_and_rule_object_from_flat_list(
+ list(target_rulebase.rules.values()), rule_uid
+ )
+ prev_uid, next_uid = self._get_adjacent_list_element(list(target_rulebase.rules.keys()), index)
+
+ if ascending:
+ return self._num_for_ascending_case(changed_rule, next_uid, target_rulebase)
+ return self._num_for_descending_case(changed_rule, prev_uid, target_rulebase)
+
+ def _num_for_ascending_case(
+ self, changed_rule: RuleNormalized, next_uid: str | None, target_rulebase: Rulebase
+ ) -> float:
+ """
+ Ascending:
+ - If a next neighbor exists, recursively use its relevant value
+ - Otherwise, align with the maximum rule in the target (and update changed_rule)
+ """
+ if next_uid:
+ return float(self._get_relevant_rule_num_numeric(next_uid, ascending=True, target_rulebase=target_rulebase))
+
+ max_rule = self._max_num_numeric_rule(target_rulebase)
+ if max_rule:
+ changed_rule.rule_num_numeric = max_rule.rule_num_numeric
+ return float(max_rule.rule_num_numeric)
+
+ # If no max exists, set to 0
+ changed_rule.rule_num_numeric = 0
+ return 0.0
+
+ def _num_for_descending_case(
+ self, changed_rule: RuleNormalized, prev_uid: str | None, target_rulebase: Rulebase
+ ) -> float:
+ """
+ Descending:
+ - If a previous neighbor exists, recursively use its relevant value
+ - Otherwise, halve the minimum > 0 (or fall back to a step value)
+ """
+ if prev_uid:
+ return float(
+ self._get_relevant_rule_num_numeric(prev_uid, ascending=False, target_rulebase=target_rulebase)
+ )
+
+ min_rule = self._min_nonzero_num_numeric_rule(target_rulebase)
+ if min_rule:
+ # Halve the min value or use 1 - whichever is larger (as intended in original)
+ half = min_rule.rule_num_numeric / 2.0
+ changed_rule.rule_num_numeric = max(half, 1)
+ return float(changed_rule.rule_num_numeric)
+ return 0
+
+ def _max_num_numeric_rule(self, target_rulebase: Rulebase):
+ """Return the rule with the maximum rule_num_numeric, or None if empty."""
+ return max(target_rulebase.rules.values(), key=lambda x: x.rule_num_numeric, default=None)
+
+ def _min_nonzero_num_numeric_rule(self, target_rulebase: Rulebase):
+ """Return the rule with the minimum non-zero rule_num_numeric, or None if none exist."""
+ return min(
+ (r for r in target_rulebase.rules.values() if getattr(r, "rule_num_numeric", 0) != 0),
+ key=lambda x: x.rule_num_numeric,
+ default=None,
+ )
+
+ def _is_rule_uid_in_return_object(self, rule_uid: str, return_object: Any) -> bool:
+ for rule_uids in return_object.values():
+ for _rule_uid in rule_uids:
+ if rule_uid == _rule_uid:
+ return True
+
+ return False
diff --git a/roles/importer/files/importer/model_controllers/fwconfigmanager_controller.py b/roles/importer/files/importer/model_controllers/fwconfigmanager_controller.py
new file mode 100644
index 0000000000..8e6817f6d1
--- /dev/null
+++ b/roles/importer/files/importer/model_controllers/fwconfigmanager_controller.py
@@ -0,0 +1,39 @@
+from typing import Any
+
+from models.fwconfig_normalized import FwConfigNormalized
+from models.fwconfigmanager import FwConfigManager
+
+
+class FwConfigManagerController(FwConfigManager):
+ manager_uid: str
+ manager_name: str
+ is_global: bool = False
+ dependant_manager_uids: list[str]
+ configs: list[FwConfigNormalized]
+ model_config = {"arbitrary_types_allowed": True} # noqa: RUF012
+
+ def __init__(
+ self,
+ manager_uid: str,
+ manager_name: str,
+ is_global: bool,
+ dependant_manager_uids: list[str],
+ configs: list[FwConfigNormalized],
+ ):
+ self.manager_uid = manager_uid
+ self.manager_name = manager_name
+ self.is_global = is_global
+ self.dependant_manager_uids = dependant_manager_uids
+ self.configs = configs
+
+ @classmethod
+ def from_json(cls, json_dict: dict[str, Any]) -> "FwConfigManagerController":
+ manager_uid: str = json_dict["manager_uid"]
+ manager_name: str = json_dict["mgm_name"]
+ is_global: bool = json_dict["is_global"]
+ dependant_manager_uids: list[str] = json_dict["dependant_manager_uids"]
+ configs: list[FwConfigNormalized] = json_dict["configs"]
+ return cls(manager_uid, manager_name, is_global, dependant_manager_uids, configs)
+
+ def __str__(self):
+ return f"{self.manager_uid}({self.configs!s})"
diff --git a/roles/importer/files/importer/model_controllers/fwconfigmanagerlist_controller.py b/roles/importer/files/importer/model_controllers/fwconfigmanagerlist_controller.py
new file mode 100644
index 0000000000..47f78bc032
--- /dev/null
+++ b/roles/importer/files/importer/model_controllers/fwconfigmanagerlist_controller.py
@@ -0,0 +1,165 @@
+import json
+import time
+import traceback
+from copy import deepcopy
+
+from fwo_base import ConfFormat
+from fwo_const import IMPORT_TMP_PATH
+from fwo_log import FWOLogger
+from models.fwconfigmanager import FwConfigManager
+from models.fwconfigmanagerlist import FwConfigManagerList
+from models.import_state import ImportState
+from utils.fwconfig_json_encoder import FwConfigJsonEncoder
+
+"""
+ a list of normalized configuratons of a firewall management to import
+ FwConfigManagerList: [ FwConfigManager ]
+"""
+
+
+class FwConfigManagerListController(FwConfigManagerList):
+ def __str__(self):
+ return f"{self.ManagerSet!s})"
+
+ def to_json_string(self, pretty_print: bool = False):
+ json_dict = self.model_dump(by_alias=True)
+ if pretty_print:
+ return json.dumps(json_dict, indent=2, cls=FwConfigJsonEncoder)
+ return json.dumps(json_dict)
+
+ def merge_configs(self, conf2: "FwConfigManagerListController"):
+ if self.ConfigFormat == conf2.ConfigFormat:
+ self.ManagerSet.extend(conf2.ManagerSet)
+
+ @staticmethod
+ def generate_empty_config(is_super_manager: bool = False) -> "FwConfigManagerListController":
+ """
+ Generates an empty FwConfigManagerListController with a single empty FwConfigManager.
+ """
+ empty_config = FwConfigManagerListController()
+ empty_config.ConfigFormat = ConfFormat.NORMALIZED
+ empty_manager = FwConfigManager(
+ manager_uid="",
+ is_super_manager=is_super_manager,
+ sub_manager_ids=[],
+ configs=[],
+ domain_name="",
+ domain_uid="",
+ manager_name="",
+ )
+ empty_config.add_manager(empty_manager)
+ empty_config.native_config = {}
+ return empty_config
+
+ def get_all_zone_names(self, mgr_uid: str) -> set[str]:
+ """
+ Returns a list of all zone UIDs in the configuration.
+ """
+ all_zone_names: list[str] = []
+ for mgr in self.ManagerSet:
+ if mgr.is_super_manager or mgr.manager_uid == mgr_uid:
+ for single_config in mgr.configs:
+ all_zone_names.extend(single_config.zone_objects.keys())
+ return set(all_zone_names)
+
+ def get_all_network_object_uids(self, mgr_uid: str) -> set[str]:
+ """
+ Returns a list of all network objects in the configuration.
+ """
+ all_network_objects: list[str] = []
+ for mgr in self.ManagerSet:
+ if mgr.is_super_manager or mgr.manager_uid == mgr_uid:
+ for single_config in mgr.configs:
+ all_network_objects.extend(single_config.network_objects.keys())
+ return set(all_network_objects)
+
+ def get_all_service_object_uids(self, mgr_uid: str) -> set[str]:
+ """
+ Returns a list of all service objects in the configuration.
+ """
+ all_service_objects: list[str] = []
+ for mgr in self.ManagerSet:
+ if mgr.is_super_manager or mgr.manager_uid == mgr_uid:
+ for single_config in mgr.configs:
+ all_service_objects.extend(single_config.service_objects.keys())
+ return set(all_service_objects)
+
+ def get_all_user_object_uids(self, mgr_uid: str) -> set[str]:
+ """
+ Returns a list of all user objects in the configuration.
+ """
+ all_user_objects: list[str] = []
+ for mgr in self.ManagerSet:
+ if mgr.is_super_manager or mgr.manager_uid == mgr_uid:
+ for single_config in mgr.configs:
+ all_user_objects.extend(single_config.users.keys())
+ return set(all_user_objects)
+
+ def add_manager(self, manager: FwConfigManager):
+ self.ManagerSet.append(manager)
+
+ def get_first_manager(self):
+ if len(self.ManagerSet) > 0:
+ return self.ManagerSet[0]
+ return None
+
+ @staticmethod
+ def get_device_uid_from_rulebase_name(rb_name: str) -> str:
+ return rb_name
+
+ @staticmethod
+ def get_policy_uid_from_rulebase_name(rb_name: str) -> str:
+ return rb_name
+
+ def store_full_normalized_config_to_file(self, import_state: ImportState):
+ if FWOLogger.is_debug_level(6):
+ debug_start_time = int(time.time())
+ try:
+ normalized_config_filename = (
+ f"{IMPORT_TMP_PATH}/mgm_id_{import_state.mgm_details.mgm_id!s}_config_normalized.json"
+ )
+
+ config_copy_without_native = deepcopy(self)
+ config_copy_without_native.native_config = {}
+
+ with open(normalized_config_filename, "w") as json_data:
+ json_data.write(config_copy_without_native.to_json_string(pretty_print=True))
+ time_write_debug_json = int(time.time()) - debug_start_time
+ FWOLogger.debug(
+ f"storeFullNormalizedConfigToFile - writing normalized config json files duration {time_write_debug_json!s}s"
+ )
+
+ return normalized_config_filename
+
+ except Exception:
+ FWOLogger.error(
+ f"import_management - unspecified error while dumping normalized config to json file: {traceback.format_exc()!s}"
+ )
+ raise
+ return None
+
+ def is_native(self) -> bool:
+ return self.native_config is not None
+
+ def is_native_non_empty(self) -> bool:
+ return self.native_config is not None and self.native_config != {}
+
+ def contains_only_native(self) -> bool:
+ return self.is_native() and (
+ len(self.ManagerSet) == 0 or (len(self.ManagerSet) == 1 and len(self.ManagerSet[0].configs) == 0)
+ )
+
+ def native_config_is_empty(self) -> bool:
+ return self.native_config is None or self.native_config == {}
+
+ def normalized_config_is_empty(self) -> bool:
+ return len(self.ManagerSet) == 1 and len(self.ManagerSet[0].configs) == 0
+
+ def is_normalized(self) -> bool:
+ return not self.is_native()
+
+ def is_legacy(self) -> bool:
+ return self.ConfigFormat == ConfFormat.is_legacy_config_format
+
+ def has_empty_config(self) -> bool:
+ return self.native_config_is_empty() and self.normalized_config_is_empty()
diff --git a/roles/importer/files/importer/model_controllers/fworch_config_controller.py b/roles/importer/files/importer/model_controllers/fworch_config_controller.py
new file mode 100644
index 0000000000..663fe37b4c
--- /dev/null
+++ b/roles/importer/files/importer/model_controllers/fworch_config_controller.py
@@ -0,0 +1,42 @@
+from typing import Any
+
+from models.fworch_config import FworchConfig
+
+"""
+ the configuraton of a firewall orchestrator itself
+ as read from the global config file including FWO URI
+"""
+
+
+class FworchConfigController(FworchConfig):
+ def __init__(
+ self,
+ fwo_api_url: str | None,
+ fwo_user_mgmt_api_uri: str | None,
+ importer_pwd: str | None,
+ api_fetch_size: int = 500,
+ ):
+ if fwo_api_url is not None:
+ self.fwo_api_url = fwo_api_url
+ else:
+ self.fwo_api_fwo_user_mgmt_api_uri = None
+ if fwo_user_mgmt_api_uri is not None:
+ self.fwo_user_mgmt_api_uri = fwo_user_mgmt_api_uri
+ else:
+ self.fwo_user_mgmt_api_uri = None
+ self.importer_password = importer_pwd
+ self.api_fetch_size = api_fetch_size
+
+ @classmethod
+ def from_json(cls, json_dict: dict[str, Any]) -> "FworchConfigController":
+ fwo_api_uri = json_dict["fwo_api_base_url"]
+ fwo_user_mgmt_api_uri = json_dict["user_management_api_base_url"]
+ fwo_importer_pwd = json_dict.get("importerPassword")
+
+ return cls(fwo_api_uri, fwo_user_mgmt_api_uri, fwo_importer_pwd)
+
+ def __str__(self):
+ return f"{self.fwo_api_url}, {self.fwo_user_mgmt_api_uri}, {self.api_fetch_size}"
+
+ def set_importer_pwd(self, importer_password: str | None):
+ self.importer_password = importer_password
diff --git a/roles/importer/files/importer/model_controllers/gateway_controller.py b/roles/importer/files/importer/model_controllers/gateway_controller.py
new file mode 100644
index 0000000000..eb208bd12f
--- /dev/null
+++ b/roles/importer/files/importer/model_controllers/gateway_controller.py
@@ -0,0 +1,28 @@
+from models.gateway import Gateway
+
+
+class GatewayController(Gateway): # noqa: PLW1641
+ def __init__(self, gw: Gateway):
+ self.Gateway = gw
+
+ @staticmethod
+ def replace_none_with_empty(s: str | None) -> str:
+ if s is None or s == "":
+ return ""
+ return str(s)
+
+ def __eq__(self, other: object) -> bool:
+ if isinstance(other, Gateway):
+ return (
+ self.Name == other.Name
+ and self.Uid == other.Uid
+ and self.Routing == other.Routing
+ and self.Interfaces == other.Interfaces
+ and self.RulebaseLinks == other.RulebaseLinks
+ and self.EnforcedNatPolicyUids == other.EnforcedNatPolicyUids
+ and self.EnforcedPolicyUids == other.EnforcedPolicyUids
+ and self.GlobalPolicyUid == other.GlobalPolicyUid
+ and self.ImportDisabled == other.ImportDisabled
+ and self.ShowInUI == other.ShowInUI
+ )
+ return NotImplemented
diff --git a/roles/importer/files/importer/model_controllers/import_state_controller.py b/roles/importer/files/importer/model_controllers/import_state_controller.py
new file mode 100644
index 0000000000..e9ed8703aa
--- /dev/null
+++ b/roles/importer/files/importer/model_controllers/import_state_controller.py
@@ -0,0 +1,319 @@
+import traceback
+from datetime import UTC, datetime
+
+import fwo_globals
+import urllib3
+from dateutil import parser
+from fwo_api import FwoApi
+from fwo_api_call import FwoApiCall
+from fwo_config import read_config
+from fwo_const import FWO_CONFIG_FILENAME, GRAPHQL_QUERY_PATH
+from fwo_exceptions import FwoImporterError
+from fwo_log import FWOLogger
+from model_controllers.fworch_config_controller import FworchConfigController
+from model_controllers.management_controller import (
+ ConnectionInfo,
+ CredentialInfo,
+ DeviceInfo,
+ DomainInfo,
+ ManagementController,
+ ManagerInfo,
+)
+from models.import_state import ImportState
+
+"""Used for storing state during import process per management"""
+
+
+class ImportStateController:
+ state: ImportState
+ api_connection: FwoApi
+ api_call: FwoApiCall
+
+ def __init__(self, state: ImportState, fwo_api: FwoApi):
+ self.state = state
+ self.api_connection = fwo_api
+ self.api_call = FwoApiCall(self.api_connection)
+
+ def __str__(self):
+ return f"{self.state.mgm_details!s}(import_id={self.state.import_id})"
+
+ def set_import_file_name(self, import_file_name: str):
+ self.state.import_file_name = import_file_name
+
+ def set_import_id(self, import_id: int):
+ self.state.import_id = import_id
+
+ @classmethod
+ def initialize_import(
+ cls,
+ mgm_id: int,
+ jwt: str,
+ suppress_cert_warnings: bool,
+ ssl_verification: bool,
+ force: bool,
+ version: int,
+ is_clearing_import: bool,
+ is_full_import: bool,
+ ):
+ fwo_config = FworchConfigController.from_json(read_config(FWO_CONFIG_FILENAME))
+
+ api_conn = FwoApi(api_uri=fwo_config.fwo_api_url, jwt=jwt)
+ api_call = FwoApiCall(api_conn)
+ # set global https connection values
+ fwo_globals.set_global_values(
+ suppress_cert_warnings_in=suppress_cert_warnings, verify_certs_in=ssl_verification
+ )
+ if fwo_globals.suppress_cert_warnings:
+ urllib3.disable_warnings() # suppress ssl warnings only
+
+ try: # get mgm_details (fw-type, port, ip, user credentials):
+ mgm_controller = ManagementController(
+ mgm_id, "", [], DeviceInfo(), ConnectionInfo(), "", CredentialInfo(), ManagerInfo(), DomainInfo()
+ )
+ mgm_details = mgm_controller.get_mgm_details(api_conn, mgm_id)
+ except Exception as _:
+ FWOLogger.error(
+ f"import_management - error while getting fw management details for mgm={mgm_id}: {traceback.format_exc()!s}"
+ )
+ raise
+
+ try: # get last import data
+ _, last_import_date = api_call.get_last_complete_import({"mgmId": mgm_id})
+ except Exception:
+ FWOLogger.error(f"import_management - error while getting last import data for mgm={mgm_id}")
+ raise
+
+ state = ImportState()
+ state.config_changed_since_last_import = True
+ state.fwo_config = fwo_config
+ state.mgm_details = ManagementController.from_json(mgm_details)
+ state.force_import = force
+ state.import_version = version
+ state.is_clearing_import = is_clearing_import
+ state.is_full_import = is_full_import
+ state.is_initial_import = last_import_date == ""
+ state.verify_certs = ssl_verification
+ state.last_successful_import = last_import_date
+
+ fwo_api = FwoApi(fwo_config.fwo_api_url, jwt)
+
+ result = cls(state, fwo_api)
+ result.get_past_import_infos()
+ result.set_core_data()
+
+ if type(result) is str: # type: ignore # TODO: This should never happen # noqa: PGH003
+ FWOLogger.error("error while getting import state")
+ raise FwoImporterError("error while getting import state")
+
+ return result
+
+ def get_past_import_infos(self):
+ try: # get past import details (LastFullImport, ...):
+ day_string = self.api_call.get_config_value(key="dataRetentionTime")
+ if day_string:
+ self.state.data_retention_days = int(day_string)
+ self.state.last_full_import_id, self.state.last_full_import_date = self.api_call.get_last_complete_import(
+ {"mgmId": int(self.state.mgm_details.mgm_id)}
+ )
+ except Exception:
+ FWOLogger.error(
+ f"import_management - error while getting past import details for mgm={self.state.mgm_details.mgm_id!s}: {traceback.format_exc()!s}"
+ )
+ raise
+
+ if self.state.last_full_import_date != "":
+ self.state.last_successful_import = self.state.last_full_import_date
+
+ # Convert the string to a datetime object
+ past_date = parser.parse(self.state.last_full_import_date)
+
+ # Ensure "now" is timezone-aware (UTC here)
+ now = datetime.now(UTC)
+
+ # Normalize pastDate too (convert to UTC if it had a tz)
+ past_date = past_date.replace(tzinfo=UTC) if past_date.tzinfo is None else past_date.astimezone(UTC)
+
+ difference = now - past_date
+
+ self.state.days_since_last_full_import = difference.days
+ else:
+ self.state.days_since_last_full_import = 0
+
+ def set_core_data(self):
+ self.set_track_map()
+ self.set_action_map()
+ self.set_link_type_map()
+ self.set_gateway_map()
+ self.set_management_map()
+ self.set_color_ref_map()
+
+ # the following maps will be empty when starting first import of a management
+ self.set_rulebase_map()
+ self.set_rule_map()
+
+ def set_action_map(self):
+ query = "query getActionMap { stm_action { action_name action_id allowed } }"
+ try:
+ result = self.api_call.call(query=query, query_variables={})
+ except Exception:
+ FWOLogger.error("Error while getting stm_action")
+ raise
+
+ action_map: dict[str, int] = {}
+ for action in result["data"]["stm_action"]:
+ action_map.update({action["action_name"]: action["action_id"]})
+ self.state.actions = action_map
+
+ def set_track_map(self):
+ query = "query getTrackMap { stm_track { track_name track_id } }"
+ try:
+ result = self.api_call.call(query=query, query_variables={})
+ except Exception:
+ FWOLogger.error("Error while getting stm_track")
+ raise
+
+ track_map: dict[str, int] = {}
+ for track in result["data"]["stm_track"]:
+ track_map.update({track["track_name"]: track["track_id"]})
+ self.state.tracks = track_map
+
+ def set_link_type_map(self):
+ query = "query getLinkType { stm_link_type { id name } }"
+ try:
+ result = self.api_call.call(query=query, query_variables={})
+ except Exception:
+ FWOLogger.error("Error while getting stm_link_type")
+ raise
+
+ link_map: dict[str, int] = {}
+ for track in result["data"]["stm_link_type"]:
+ link_map.update({track["name"]: track["id"]})
+ self.state.link_types = link_map
+
+ def set_color_ref_map(self):
+ get_colors_query = FwoApi.get_graphql_code([GRAPHQL_QUERY_PATH + "stmTables/getColors.graphql"])
+
+ try:
+ result = self.api_call.call(query=get_colors_query, query_variables={})
+ except Exception:
+ FWOLogger.error("Error while getting stm_color")
+ raise
+
+ color_map: dict[str, int] = {}
+ for color in result["data"]["stm_color"]:
+ color_map.update({color["color_name"]: color["color_id"]})
+ self.state.color_map = color_map
+
+ # limited to the current mgm_id
+ # creates a dict with key = rulebase.uid and value = rulebase.id
+ # TODO: map update inconsistencies: import_state is global over all sub managers, so map needs to be updated for each sub manager
+ # currently, this is done in fwconfig_import_rule. But what about other maps? - see #3646
+ # TODO: global rulebases not yet included
+ def set_rulebase_map(self) -> None:
+ # TODO: maps need to be updated directly after data changes
+ query = """query getRulebaseMap($mgmId: Int) { rulebase(where:{mgm_id: {_eq: $mgmId}, removed:{_is_null:true }}) { id uid } }"""
+ try:
+ result = self.api_call.call(query=query, query_variables={"mgmId": self.state.mgm_details.current_mgm_id})
+ except Exception:
+ FWOLogger.error("Error while getting rulebases")
+ self.state.rulebase_map = {}
+ raise
+
+ m: dict[str, int] = {}
+ for rulebase in result["data"]["rulebase"]:
+ rbid = rulebase["id"]
+ m.update({rulebase["uid"]: rbid})
+ self.state.rulebase_map = m
+
+ FWOLogger.debug(
+ f"updated rulebase map for mgm_id {self.state.mgm_details.current_mgm_id} with {len(self.state.rulebase_map)} entries"
+ )
+
+ # limited to the current mgm_id
+ # creats a dict with key = rule.uid and value = rule.id
+ # should be called sparsely, as there might be a lot of rules for a mgmt
+ def set_rule_map(self) -> None:
+ query = """query getRuleMap($mgmId: Int) { rule(where:{mgm_id: {_eq: $mgmId}, removed:{_is_null:true }}) { rule_id rule_uid } }"""
+ try:
+ result = self.api_call.call(query=query, query_variables={"mgmId": self.state.mgm_details.mgm_id})
+ except Exception:
+ FWOLogger.error("Error while getting rules")
+ self.state.rule_map = {}
+ raise
+
+ m: dict[str, int] = {}
+ for rule in result["data"]["rule"]:
+ m.update({rule["rule_uid"]: rule["rule_id"]})
+ self.state.rule_map = m
+
+ # getting all gateways (not limitited to the current mgm_id) to support super managements
+ # creates a dict with key = gateway.uid and value = gateway.id
+ # and also key = gateway.name and value = gateway.id
+ def set_gateway_map(self):
+ query = """
+ query getGatewayMap {
+ device {
+ mgm_id
+ dev_id
+ dev_uid
+ }
+ }
+ """
+ try:
+ result = self.api_call.call(query=query, query_variables={})
+ except Exception:
+ FWOLogger.error("Error while getting gateways")
+ self.state.gateway_map = {}
+ raise
+
+ m = {}
+ for gw in result["data"]["device"]:
+ if gw["mgm_id"] not in m:
+ m[gw["mgm_id"]] = {}
+ m[gw["mgm_id"]][gw["dev_uid"]] = gw["dev_id"]
+ self.state.gateway_map = m
+
+ # getting all managements (not limitited to the current mgm_id) to support super managements
+ # creates a dict with key = management.uid and value = management.id
+ def set_management_map(self):
+ query = """
+ query getManagementMap($mgmId: Int!) {
+ management(where: {mgm_id: {_eq: $mgmId}}) {
+ mgm_id
+ mgm_uid
+ sub_managers: managementByMultiDeviceManagerId {
+ mgm_id
+ mgm_uid
+ }
+ }
+ }
+ """
+ try:
+ result = self.api_call.call(query=query, query_variables={"mgmId": self.state.mgm_details.mgm_id})
+ except Exception:
+ FWOLogger.error("Error while getting managements")
+ self.state.management_map = {}
+ raise
+
+ m: dict[str, int] = {}
+ mgm = result["data"]["management"][0]
+ m.update({mgm["mgm_uid"]: mgm["mgm_id"]})
+ for sub_mgr in mgm["sub_managers"]:
+ m.update({sub_mgr["mgm_uid"]: sub_mgr["mgm_id"]})
+
+ self.state.management_map = m
+
+ def delete_import(self):
+ delete_import_mutation = """
+ mutation deleteImport($importId: bigint!) {
+ delete_import_control(where: {control_id: {_eq: $importId}}) { affected_rows }
+ }"""
+
+ try:
+ result = self.api_connection.call(
+ delete_import_mutation, query_variables={"importId": self.state.import_id}
+ )
+ _ = result["data"]["delete_import_control"]["affected_rows"]
+ FWOLogger.info(f"removed import with id {self.state.import_id!s} completely")
+ except Exception:
+ FWOLogger.exception("fwo_api: failed to unlock import for import id " + str(self.state.import_id))
diff --git a/roles/importer/files/importer/model_controllers/import_statistics_controller.py b/roles/importer/files/importer/model_controllers/import_statistics_controller.py
new file mode 100644
index 0000000000..3ff6d1afe9
--- /dev/null
+++ b/roles/importer/files/importer/model_controllers/import_statistics_controller.py
@@ -0,0 +1,141 @@
+from models.import_statistics import ImportStatistics
+
+
+class ImportStatisticsController:
+ def __init__(self, statistics: ImportStatistics | None = None):
+ self.statistics = statistics if statistics is not None else ImportStatistics()
+
+ def get_total_change_number(self):
+ return (
+ self.statistics.network_object_add_count
+ + self.statistics.network_object_delete_count
+ + self.statistics.network_object_change_count
+ + self.statistics.service_object_add_count
+ + self.statistics.service_object_delete_count
+ + self.statistics.service_object_change_count
+ + self.statistics.user_object_add_count
+ + self.statistics.user_object_delete_count
+ + self.statistics.user_object_change_count
+ + self.statistics.zone_object_add_count
+ + self.statistics.zone_object_delete_count
+ + self.statistics.zone_object_change_count
+ + self.statistics.rule_add_count
+ + self.statistics.rule_delete_count
+ + self.statistics.rule_change_count
+ + self.statistics.rule_enforce_change_count
+ + self.statistics.rulebase_add_count
+ + self.statistics.rulebase_change_count
+ + self.statistics.rulebase_delete_count
+ )
+
+ def get_rule_change_number(self):
+ return (
+ self.statistics.rule_add_count
+ + self.statistics.rule_delete_count
+ + self.statistics.rule_change_count
+ + self.statistics.rule_enforce_change_count
+ + self.statistics.rulebase_add_count
+ + self.statistics.rulebase_change_count
+ + self.statistics.rulebase_delete_count
+ )
+
+ def get_change_details(self):
+ result: dict[str, int] = {}
+ self.collect_nw_obj_change_details(result)
+ self.collect_svc_obj_change_details(result)
+ self.collect_usr_obj_change_details(result)
+ self.collect_zone_obj_change_details(result)
+ self.collect_rule_change_details(result)
+ return result
+
+ def collect_nw_obj_change_details(self, result: dict[str, int]):
+ if self.statistics.network_object_add_count > 0:
+ result["NetworkObjectAddCount"] = self.statistics.network_object_add_count
+ if self.statistics.network_object_delete_count > 0:
+ result["NetworkObjectDeleteCount"] = self.statistics.network_object_delete_count
+ if self.statistics.network_object_change_count > 0:
+ result["NetworkObjectChangeCount"] = self.statistics.network_object_change_count
+
+ def collect_svc_obj_change_details(self, result: dict[str, int]):
+ if self.statistics.service_object_add_count > 0:
+ result["ServiceObjectAddCount"] = self.statistics.service_object_add_count
+ if self.statistics.service_object_delete_count > 0:
+ result["ServiceObjectDeleteCount"] = self.statistics.service_object_delete_count
+ if self.statistics.service_object_change_count > 0:
+ result["ServiceObjectChangeCount"] = self.statistics.service_object_change_count
+
+ def collect_usr_obj_change_details(self, result: dict[str, int]):
+ if self.statistics.user_object_add_count > 0:
+ result["UserObjectAddCount"] = self.statistics.user_object_add_count
+ if self.statistics.user_object_delete_count > 0:
+ result["UserObjectDeleteCount"] = self.statistics.user_object_delete_count
+ if self.statistics.user_object_change_count > 0:
+ result["UserObjectChangeCount"] = self.statistics.user_object_change_count
+
+ def collect_zone_obj_change_details(self, result: dict[str, int]):
+ if self.statistics.zone_object_add_count > 0:
+ result["ZoneObjectAddCount"] = self.statistics.zone_object_add_count
+ if self.statistics.zone_object_delete_count > 0:
+ result["ZoneObjectDeleteCount"] = self.statistics.zone_object_delete_count
+ if self.statistics.zone_object_change_count > 0:
+ result["ZoneObjectChangeCount"] = self.statistics.zone_object_change_count
+
+ def collect_rule_change_details(self, result: dict[str, int]):
+ if self.statistics.rule_add_count > 0:
+ result["RuleAddCount"] = self.statistics.rule_add_count
+ if self.statistics.rule_delete_count > 0:
+ result["RuleDeleteCount"] = self.statistics.rule_delete_count
+ if self.statistics.rule_change_count > 0:
+ result["RuleChangeCount"] = self.statistics.rule_change_count
+ if self.statistics.rule_move_count > 0:
+ result["RuleMoveCount"] = self.statistics.rule_move_count
+ if self.statistics.rule_enforce_change_count > 0:
+ result["rule_enforce_change_count"] = self.statistics.rule_enforce_change_count
+ if self.statistics.rulebase_change_count > 0:
+ result["rulebase_change_count"] = self.statistics.rulebase_change_count
+ if self.statistics.rulebase_add_count > 0:
+ result["rulebase_add_count"] = self.statistics.rulebase_add_count
+ if self.statistics.rulebase_delete_count > 0:
+ result["rulebase_delete_count"] = self.statistics.rulebase_delete_count
+
+ def increment_network_object_add_count(self, increment: int = 1):
+ self.statistics.network_object_add_count += increment
+
+ def increment_network_object_delete_count(self, increment: int = 1):
+ self.statistics.network_object_delete_count += increment
+
+ def increment_network_object_change_count(self, increment: int = 1):
+ self.statistics.network_object_change_count += increment
+
+ def increment_service_object_add_count(self, increment: int = 1):
+ self.statistics.service_object_add_count += increment
+
+ def increment_service_object_delete_count(self, increment: int = 1):
+ self.statistics.service_object_delete_count += increment
+
+ def increment_service_object_change_count(self, increment: int = 1):
+ self.statistics.service_object_change_count += increment
+
+ def increment_rule_add_count(self, increment: int = 1):
+ self.statistics.rule_add_count += increment
+
+ def increment_rule_delete_count(self, increment: int = 1):
+ self.statistics.rule_delete_count += increment
+
+ def increment_rule_change_count(self, increment: int = 1):
+ self.statistics.rule_change_count += increment
+
+ def increment_rule_move_count(self, increment: int = 1):
+ self.statistics.rule_move_count += increment
+
+ def increment_rule_enforce_change_count(self, increment: int = 1):
+ self.statistics.rule_enforce_change_count += increment
+
+ def increment_rulebase_link_add_count(self, increment: int = 1):
+ self.statistics.rulebase_link_add_count += increment
+
+ def increment_rulebase_link_delete_count(self, increment: int = 1):
+ self.statistics.rulebase_link_delete_count += increment
+
+ def increment_rulebase_link_change_count(self, increment: int = 1):
+ self.statistics.rulebase_link_change_count += increment
diff --git a/roles/importer/files/importer/model_controllers/interface_controller.py b/roles/importer/files/importer/model_controllers/interface_controller.py
new file mode 100644
index 0000000000..3d67b9ef3c
--- /dev/null
+++ b/roles/importer/files/importer/model_controllers/interface_controller.py
@@ -0,0 +1,53 @@
+from typing import Any
+
+from fwo_log import FWOLogger
+from netaddr import IPAddress
+
+
+class Interface:
+ def __init__(
+ self, device_id: int, name: str, ip: IPAddress, netmask_bits: int, state_up: bool = True, ip_version: int = 4
+ ):
+ self.routing_device = int(device_id)
+ # check if routing device id exists?
+ self.name = str(name)
+ self.ip = IPAddress(ip)
+ netmask_bits = int(netmask_bits)
+ if netmask_bits < 0 or netmask_bits > 128: # noqa: PLR2004
+ FWOLogger.error("interface " + self.name + " with invalid bitmask: " + str(netmask_bits))
+ else:
+ self.netmask_bits = netmask_bits
+ self.state_up = bool(state_up)
+ ip_version = int(ip_version)
+ if ip_version not in {4, 6}:
+ FWOLogger.error("interface " + self.name + " with invalid ip protocal: " + str(ip_version))
+ else:
+ self.ip_version = ip_version
+
+ self.ip_version = ip_version
+
+
+class InterfaceSerializable(Interface):
+ name: str
+ routing_device: int
+ ip: str
+ netmask_bits: int
+ state_up: bool
+ ip_version: int
+
+ # TYPING: check if these types are correct
+ def __init__(self, iface_in: dict[Any, Any] | Interface):
+ if type(iface_in) is dict:
+ self.name = iface_in["name"]
+ self.routing_device = iface_in["routing_device"]
+ self.ip = str(iface_in["ip"])
+ self.netmask_bits = iface_in["netmask_bits"]
+ self.state_up = iface_in["state_up"]
+ self.ip_version = iface_in["ip_version"]
+ elif isinstance(iface_in, Interface):
+ self.name = iface_in.name
+ self.routing_device = iface_in.routing_device
+ self.ip = str(iface_in.ip)
+ self.netmask_bits = iface_in.netmask_bits
+ self.state_up = iface_in.state_up
+ self.ip_version = iface_in.ip_version
diff --git a/roles/importer/files/importer/model_controllers/management_controller.py b/roles/importer/files/importer/model_controllers/management_controller.py
new file mode 100644
index 0000000000..330c7ec8a3
--- /dev/null
+++ b/roles/importer/files/importer/model_controllers/management_controller.py
@@ -0,0 +1,202 @@
+import hashlib
+from dataclasses import dataclass
+from typing import Any
+
+from fwconfig_base import replace_none_with_empty
+from fwo_api import FwoApi
+from fwo_const import GRAPHQL_QUERY_PATH
+from fwo_encrypt import decrypt, read_main_key
+from fwo_exceptions import FwLoginFailedError, FwoApiFailureError, SecretDecryptionFailedError
+from models.gateway import Gateway
+from models.management import Management
+
+
+@dataclass
+class DeviceInfo:
+ name: str = ""
+ type_name: str = ""
+ type_version: str = ""
+
+
+@dataclass
+class ConnectionInfo:
+ hostname: str = ""
+ port: int = 443
+
+
+@dataclass
+class CredentialInfo:
+ secret: str = ""
+ import_user: str = ""
+ cloud_client_id: str = ""
+ cloud_client_secret: str = ""
+
+
+@dataclass
+class ManagerInfo:
+ is_super_manager: bool = False
+ sub_manager_ids: list[int] | None = None
+ sub_managers: list["Management"] | None = None
+
+
+@dataclass
+class DomainInfo:
+ domain_name: str = ""
+ domain_uid: str = ""
+
+
+class ManagementController(Management):
+ def __init__(
+ self,
+ mgm_id: int,
+ uid: str,
+ devices: list[dict[str, Any]],
+ device_info: DeviceInfo,
+ connection_info: ConnectionInfo,
+ importer_hostname: str,
+ credential_info: CredentialInfo,
+ manager_info: ManagerInfo,
+ domain_info: DomainInfo,
+ import_disabled: bool = False,
+ ):
+ super().__init__(
+ mgm_id=mgm_id,
+ uid=uid,
+ devices=devices,
+ import_disabled=import_disabled,
+ name=device_info.name,
+ device_type_name=device_info.type_name,
+ device_type_version=device_info.type_version,
+ hostname=connection_info.hostname,
+ port=connection_info.port,
+ importer_hostname=importer_hostname,
+ import_user=credential_info.import_user,
+ secret=credential_info.secret,
+ is_super_manager=manager_info.is_super_manager,
+ sub_manager_ids=manager_info.sub_manager_ids or [],
+ sub_managers=manager_info.sub_managers or [],
+ current_mgm_id=mgm_id,
+ current_mgm_is_super_manager=manager_info.is_super_manager,
+ domain_name=domain_info.domain_name,
+ domain_uid=domain_info.domain_uid,
+ cloud_client_id=credential_info.cloud_client_id,
+ cloud_client_secret=credential_info.cloud_client_secret,
+ )
+
+ @classmethod
+ def from_json(cls, json_dict: dict[str, Any]) -> "ManagementController":
+ device_info = DeviceInfo(
+ name=json_dict["name"],
+ type_name=json_dict["deviceType"]["name"],
+ type_version=json_dict["deviceType"]["version"],
+ )
+
+ connection_info = ConnectionInfo(
+ hostname=json_dict["hostname"],
+ port=json_dict["port"],
+ )
+
+ credential_info = CredentialInfo(
+ import_user=json_dict["import_credential"]["user"],
+ secret=json_dict["import_credential"]["secret"],
+ cloud_client_id=json_dict["import_credential"]["cloud_client_id"],
+ cloud_client_secret=json_dict["import_credential"]["cloud_client_secret"],
+ )
+
+ manager_info = ManagerInfo(
+ is_super_manager=json_dict["isSuperManager"],
+ sub_manager_ids=[subManager["id"] for subManager in json_dict["subManagers"]],
+ sub_managers=[cls.from_json(subManager) for subManager in json_dict["subManagers"]],
+ )
+
+ domain_info = DomainInfo(domain_name=json_dict["configPath"], domain_uid=json_dict["domainUid"])
+
+ return cls(
+ mgm_id=json_dict["id"],
+ uid=json_dict["uid"],
+ devices=json_dict["devices"],
+ device_info=device_info,
+ connection_info=connection_info,
+ importer_hostname=json_dict["importerHostname"],
+ credential_info=credential_info,
+ manager_info=manager_info,
+ domain_info=domain_info,
+ import_disabled=json_dict["importDisabled"],
+ )
+
+ def __str__(self):
+ return f"{self.hostname}({self.mgm_id})"
+
+ # TODO: fix device type URIs
+ def build_fw_api_string(self):
+ if self.device_type_name == "Check Point":
+ return f"https://{self.hostname}:{self.port!s}/web_api/"
+ if self.device_type_name == "CiscoFMC":
+ return f"https://{self.hostname}:{self.port!s}/api/fmc_platform/v1/"
+ if self.device_type_name == "Fortinet":
+ return f"https://{self.hostname}:{self.port!s}/api/v2/"
+ if self.device_type_name in {"FortiAdom", "FortiManager"}:
+ return f"https://{self.hostname}:{self.port!s}/jsonrpc"
+ if self.device_type_name in {"PaloAlto", "PaloAltoLegacy"}:
+ return f"https://{self.hostname}:{self.port!s}/restapi/v10.0/"
+ raise FwLoginFailedError(f"Unsupported device type: {self.device_type_name}")
+
+ def get_domain_string(self) -> str:
+ return self.domain_uid if self.domain_uid is not None else self.domain_name # type: ignore #TODO: check if None check is needed if yes, change type # noqa: PGH003
+
+ @classmethod
+ def build_gateway_list(cls, mgm_details: "ManagementController") -> list["Gateway"]:
+ devs: list[Gateway] = []
+ for dev in mgm_details.devices:
+ # check if gateway import is enabled
+ if dev.get("do_not_import"):
+ continue
+ devs.append(Gateway(Name=dev["name"], Uid=f"{dev['name']}/{mgm_details.calc_manager_uid_hash()}"))
+ return devs
+
+ def calc_manager_uid_hash(self):
+ combination = f"""
+ {replace_none_with_empty(self.hostname)}
+ {replace_none_with_empty(str(self.port))}
+ {replace_none_with_empty(self.domain_uid)}
+ {replace_none_with_empty(self.domain_name)}
+ """
+ return hashlib.sha256(combination.encode()).hexdigest()
+
+ def get_mgm_details(self, api_conn: FwoApi, mgm_id: int) -> dict[str, Any]:
+ get_mgm_details_query = FwoApi.get_graphql_code(
+ [
+ GRAPHQL_QUERY_PATH + "device/getSingleManagementDetails.graphql",
+ GRAPHQL_QUERY_PATH + "device/fragments/managementDetails.graphql",
+ GRAPHQL_QUERY_PATH + "device/fragments/subManagements.graphql",
+ GRAPHQL_QUERY_PATH + "device/fragments/deviceTypeDetails.graphql",
+ GRAPHQL_QUERY_PATH + "device/fragments/importCredentials.graphql",
+ ]
+ )
+
+ api_call_result = api_conn.call(get_mgm_details_query, query_variables={"mgmId": mgm_id})
+ if (
+ "data" not in api_call_result
+ or "management" not in api_call_result["data"]
+ or len(api_call_result["data"]["management"]) < 1
+ ):
+ raise FwoApiFailureError("did not succeed in getting management details from FWO API")
+
+ if "://" not in api_call_result["data"]["management"][0]["hostname"]:
+ # only decrypt if we have a real management and are not fetching the config from an URL
+ # decrypt secret read from API
+ try:
+ secret = api_call_result["data"]["management"][0]["import_credential"]["secret"]
+ decrypted_secret = decrypt(secret, read_main_key())
+ except Exception:
+ raise SecretDecryptionFailedError
+ api_call_result["data"]["management"][0]["import_credential"]["secret"] = decrypted_secret
+ if "subManagers" in api_call_result["data"]["management"][0]:
+ for sub_mgm in api_call_result["data"]["management"][0]["subManagers"]:
+ try:
+ secret = sub_mgm["import_credential"]["secret"]
+ decrypted_secret = decrypt(secret, read_main_key())
+ except Exception:
+ raise SecretDecryptionFailedError
+ sub_mgm["import_credential"]["secret"] = decrypted_secret
+ return api_call_result["data"]["management"][0]
diff --git a/roles/importer/files/importer/model_controllers/route_controller.py b/roles/importer/files/importer/model_controllers/route_controller.py
new file mode 100644
index 0000000000..ce957da0cd
--- /dev/null
+++ b/roles/importer/files/importer/model_controllers/route_controller.py
@@ -0,0 +1,120 @@
+from typing import Any
+
+from fwo_log import FWOLogger
+from model_controllers.interface_controller import InterfaceSerializable
+from netaddr import IPAddress, IPNetwork
+
+
+class Route:
+ def __init__(
+ self,
+ device_id: int,
+ target_gateway: str,
+ destination: str,
+ static: bool = True,
+ source: str | None = None,
+ interface: str | None = None,
+ metric: int | None = None,
+ distance: int | None = None,
+ ip_version: int = 4,
+ ):
+ self.routing_device = int(device_id)
+ if interface is not None:
+ self.interface = str(interface)
+ else:
+ self.interface = None
+ self.target_gateway = IPAddress(target_gateway)
+ self.destination = IPNetwork(destination)
+ if source is not None:
+ self.source = IPNetwork(source)
+ else:
+ self.source = None
+ self.static = bool(static)
+ if metric is not None:
+ self.metric = int(metric)
+ if distance is not None:
+ self.distance = int(distance)
+ ip_version = int(ip_version)
+ if ip_version not in {4, 6}:
+ FWOLogger.error(
+ "found route for destination " + str(self.destination) + " with invalid ip protocal: " + str(ip_version)
+ )
+ else:
+ self.ip_version = ip_version
+
+ def is_default_route(self):
+ return self.is_default_route_v4() or self.is_default_route_v6()
+
+ def is_default_route_v4(self):
+ return self.ip_version == 4 and self.destination == IPNetwork("0.0.0.0/0") # noqa: PLR2004
+
+ def is_default_route_v6(self):
+ return self.ip_version == 6 and self.destination == IPNetwork("::/0") # noqa: PLR2004
+
+ def route_matches(self, destination: str, dev_id: int) -> bool:
+ ip_n = IPNetwork(self.destination).cidr
+ dest_n = IPNetwork(destination).cidr
+ return dev_id == self.routing_device and (ip_n in dest_n or dest_n in ip_n)
+
+ def get_route_destination(self):
+ return self.destination
+
+
+class RouteSerializable(Route):
+ def __init__(self, route_in: dict[str, Any] | Route):
+ if type(route_in) is dict:
+ self.routing_device = route_in["routing_device"]
+ self.interface = route_in["interface"]
+ self.target_gateway = str(route_in["target_gateway"])
+ self.destination = str(route_in["destination"])
+ if route_in["source"] is None:
+ self.source = None
+ else:
+ self.source = str(route_in["source"])
+ self.static = route_in["static"]
+ self.metric = route_in["metric"]
+ self.distance = route_in["distance"]
+ self.ip_version = route_in["ip_version"]
+ elif isinstance(route_in, Route):
+ self.routing_device = route_in.routing_device
+ self.interface = route_in.interface
+ self.target_gateway = str(route_in.target_gateway)
+ self.destination = str(route_in.destination)
+ if route_in.source is None:
+ self.source = None
+ else:
+ self.source = str(route_in.source)
+ self.static = route_in.static
+ self.metric = route_in.metric
+ self.distance = route_in.distance
+ self.ip_version = route_in.ip_version
+
+
+def get_route_destination(obj: Route):
+ return obj.destination
+
+
+def get_matching_route_obj(destination_ip: str, routing_table: list[Route], dev_id: int) -> Route | None:
+ if len(routing_table) == 0:
+ FWOLogger.error("found empty routing table for device id " + str(dev_id))
+ return None
+
+ # assuiming routing table to be in sorted state already
+ for route in routing_table:
+ if route.route_matches(destination_ip, dev_id):
+ return route
+
+ FWOLogger.warning("src nat behind interface: found no matching route in routing table - no default route?!")
+ return None
+
+
+def get_ip_of_interface_obj(
+ interface_name: str | None, dev_id: int, interface_list: list[InterfaceSerializable]
+) -> str | None:
+ interface_details = next(
+ (sub for sub in interface_list if sub.name == interface_name and sub.routing_device == dev_id), None
+ )
+
+ if interface_details is not None:
+ return interface_details.ip
+ return None
diff --git a/roles/importer/files/importer/model_controllers/rule_enforced_on_gateway_controller.py b/roles/importer/files/importer/model_controllers/rule_enforced_on_gateway_controller.py
new file mode 100644
index 0000000000..6340e871c4
--- /dev/null
+++ b/roles/importer/files/importer/model_controllers/rule_enforced_on_gateway_controller.py
@@ -0,0 +1,149 @@
+from traceback import format_exc
+from typing import Any
+
+import fwo_const
+from fwo_api_call import FwoApiCall
+from fwo_log import FWOLogger
+from model_controllers.import_statistics_controller import ImportStatisticsController
+from model_controllers.rulebase_link_controller import RulebaseLinkController
+from models.import_state import ImportState
+
+
+class RuleEnforcedOnGatewayController:
+ def add_new_rule_enforced_on_gateway_refs(
+ self,
+ new_rules: list[dict[str, Any]],
+ import_state: ImportState,
+ fwo_api_call: FwoApiCall,
+ statistics_controller: ImportStatisticsController,
+ ):
+ """
+ Main function to add new rule-to-gateway references.
+ """
+ # Step 1: Initialize the RulebaseLinkController
+ rb_link_controller = self.initialize_rulebase_link_controller(import_state, fwo_api_call)
+
+ # Step 2: Prepare rule-to-gateway references
+ rule_to_gw_refs = self.prepare_rule_to_gateway_references(import_state, new_rules, rb_link_controller)
+
+ # Step 3: Check if there are any references to insert
+ if not rule_to_gw_refs:
+ FWOLogger.info("No rules to be enforced on gateways.")
+ return
+
+ # Step 4: Insert the references into the database
+ self.insert_rule_to_gateway_references(statistics_controller, fwo_api_call, rule_to_gw_refs)
+
+ def initialize_rulebase_link_controller(
+ self, import_state: ImportState, fwo_api_call: FwoApiCall
+ ) -> RulebaseLinkController:
+ """
+ Initialize the RulebaseLinkController and set the map of enforcing gateways.
+ """
+ rb_link_controller = RulebaseLinkController()
+ rb_link_controller.set_map_of_all_enforcing_gateway_ids_for_rulebase_id(import_state, fwo_api_call)
+ return rb_link_controller
+
+ def prepare_rule_to_gateway_references(
+ self,
+ import_state: ImportState,
+ new_rules: list[dict[str, Any]],
+ rb_link_controller: RulebaseLinkController,
+ ) -> list[dict[str, Any]]:
+ """
+ Prepare the list of rule-to-gateway references based on the rules and their 'install on' settings.
+ """
+ rule_to_gw_refs: list[dict[str, Any]] = []
+ for rule in new_rules:
+ if rule["rule_installon"] is None:
+ self.handle_rule_without_installon(import_state, rule, rb_link_controller, rule_to_gw_refs)
+ else:
+ self.handle_rule_with_installon(import_state, rule, rule_to_gw_refs)
+ return rule_to_gw_refs
+
+ def handle_rule_without_installon(
+ self,
+ import_state: ImportState,
+ rule: dict[str, Any],
+ rb_link_controller: RulebaseLinkController,
+ rule_to_gw_refs: list[dict[str, Any]],
+ ) -> None:
+ """
+ Handle rules with no 'install on' setting by linking them to all gateways for the rulebase.
+ """
+ rule_to_gw_refs.extend(
+ [
+ self.create_rule_to_gateway_reference(import_state, rule, gw_id)
+ for gw_id in rb_link_controller.get_gw_ids_for_rulebase_id(rule["rulebase_id"])
+ ]
+ )
+
+ def handle_rule_with_installon(
+ self,
+ import_state: ImportState,
+ rule: dict[str, Any],
+ rule_to_gw_refs: list[dict[str, Any]],
+ ) -> None:
+ """
+ Handle rules with 'install on' settings by linking them to specific gateways.
+ """
+ rule_installon: str | None = rule["rule_installon"]
+ if rule_installon is None:
+ return
+
+ for gw_uid in rule_installon.split(fwo_const.LIST_DELIMITER):
+ gw_id = import_state.lookup_gateway_id(gw_uid)
+ if gw_id is not None:
+ rule_to_gw_refs.append(self.create_rule_to_gateway_reference(import_state, rule, gw_id))
+ else:
+ FWOLogger.warning(f"Found a broken reference to a non-existing gateway (uid={gw_uid}). Ignoring.")
+
+ def create_rule_to_gateway_reference(
+ self, import_state: ImportState, rule: dict[str, Any], gw_id: int
+ ) -> dict[str, Any]:
+ """
+ Create a dictionary representing a rule-to-gateway reference.
+ """
+ return {
+ "rule_id": rule["rule_id"], # TODO: rule_id does not exist
+ "dev_id": gw_id,
+ "created": import_state.import_id,
+ "removed": None,
+ }
+
+ def insert_rule_to_gateway_references(
+ self,
+ statistics_controller: ImportStatisticsController,
+ fwo_api_call: FwoApiCall,
+ rule_to_gw_refs: list[dict[str, Any]],
+ ) -> None:
+ """
+ Insert the rule-to-gateway references into the database.
+ """
+ try:
+ import_results: dict[str, Any] = self.insert_rules_enforced_on_gateway(fwo_api_call, rule_to_gw_refs)
+ if "errors" in import_results:
+ FWOLogger.exception(f"Error in add_new_rule_enforced_on_gateway_refs: {import_results['errors']!s}")
+ else:
+ changes = import_results["data"]["insert_rule_enforced_on_gateway"].get("affected_rows", 1)
+ statistics_controller.increment_rule_enforce_change_count(changes)
+ except Exception:
+ FWOLogger.exception(f"Failed to write new rules: {format_exc()!s}")
+ raise
+
+ def insert_rules_enforced_on_gateway(
+ self, fwo_api_call: FwoApiCall, enforcements: list[dict[str, Any]]
+ ) -> dict[str, Any]:
+ """
+ Insert rules enforced on gateways into the database.
+ """
+ if len(enforcements) > 0:
+ query_variables = {"rulesEnforcedOnGateway": enforcements}
+ mutation = """
+ mutation importInsertRulesEnforcedOnGateway($rulesEnforcedOnGateway: [rule_enforced_on_gateway_insert_input!]!) {
+ insert_rule_enforced_on_gateway(objects: $rulesEnforcedOnGateway) {
+ affected_rows
+ }
+ }"""
+ return fwo_api_call.call(mutation, query_variables=query_variables)
+ return {}
diff --git a/roles/importer/files/importer/model_controllers/rulebase_link_controller.py b/roles/importer/files/importer/model_controllers/rulebase_link_controller.py
new file mode 100644
index 0000000000..1a768d28ca
--- /dev/null
+++ b/roles/importer/files/importer/model_controllers/rulebase_link_controller.py
@@ -0,0 +1,87 @@
+from typing import Any
+
+import fwo_const
+from fwo_api import FwoApi
+from fwo_api_call import FwoApiCall
+from fwo_log import FWOLogger
+from model_controllers.import_statistics_controller import ImportStatisticsController
+from models.import_state import ImportState
+from models.rulebase_link import RulebaseLink, parse_rulebase_links
+
+
+class RulebaseLinkController:
+ rulbase_to_gateway_map: dict[int, list[int]]
+ rb_links: list[RulebaseLink]
+
+ def __init__(self) -> None:
+ self.rulbase_to_gateway_map = {}
+
+ def insert_rulebase_links(
+ self, fwo_api_call: FwoApiCall, stats: ImportStatisticsController, rb_links: list[dict[str, Any]]
+ ) -> None:
+ query_variables = {"rulebaseLinks": rb_links}
+ if len(rb_links) == 0:
+ return
+ mutation = FwoApi.get_graphql_code([f"{fwo_const.GRAPHQL_QUERY_PATH}rule/insertRulebaseLinks.graphql"])
+ add_result = fwo_api_call.call(mutation, query_variables=query_variables)
+ if "errors" in add_result:
+ FWOLogger.exception(f"fwo_api:insertRulebaseLinks - error while inserting: {add_result['errors']!s}")
+ else:
+ changes = add_result["data"]["insert_rulebase_link"]["affected_rows"]
+ stats.increment_rulebase_link_add_count(changes)
+
+ def remove_rulebase_links(
+ self,
+ fwo_api_call: FwoApiCall,
+ stats: ImportStatisticsController,
+ import_id: int,
+ removed_rb_links_ids: list[int | None],
+ ) -> None:
+ query_variables: dict[str, Any] = {"removedRulebaseLinks": removed_rb_links_ids, "importId": import_id}
+ if len(removed_rb_links_ids) == 0:
+ return
+ mutation = FwoApi.get_graphql_code([f"{fwo_const.GRAPHQL_QUERY_PATH}rule/removeRulebaseLinks.graphql"])
+ add_result = fwo_api_call.call(mutation, query_variables=query_variables)
+ if "errors" in add_result:
+ FWOLogger.exception(f"fwo_api:removeRulebaseLinks - error while removing: {add_result['errors']!s}")
+ else:
+ changes = add_result["data"]["update_rulebase_link"]["affected_rows"]
+ stats.increment_rulebase_link_delete_count(changes)
+
+ def get_rulebase_links(self, import_state: ImportState, fwo_api_call: FwoApiCall) -> None:
+ gw_ids = import_state.lookup_all_gateway_ids()
+ if len(gw_ids) == 0:
+ FWOLogger.warning(
+ "RulebaseLinkController:get_rulebase_links - no gateway ids found for current management - skipping getting rulebase links"
+ )
+ self.rb_links = []
+ return
+ # we always need to provide gwIds since rulebase_links may be duplicate across different gateways
+ query_variables = {"gwIds": gw_ids}
+
+ query = FwoApi.get_graphql_code(file_list=[f"{fwo_const.GRAPHQL_QUERY_PATH}rule/getRulebaseLinks.graphql"])
+ links = fwo_api_call.call(query, query_variables=query_variables)
+ if "errors" in links:
+ FWOLogger.exception(f"fwo_api:getRulebaseLinks - error while getting rulebaseLinks: {links['errors']!s}")
+ else:
+ parsable_rulebase_links = [
+ link for link in links["data"]["rulebase_link"] if link.get("created") is not None
+ ] # TODO: is this necessary or was the bug some corrupted local db stuff? But why does integration test fail?
+ self.rb_links: list[RulebaseLink] = parse_rulebase_links(parsable_rulebase_links)
+
+ # add an entry for all rulebase to gateway pairs that are conained in the rulebase_links table
+ def set_map_of_all_enforcing_gateway_ids_for_rulebase_id(self, import_state: ImportState, fwo_api_call: FwoApiCall):
+ self.get_rulebase_links(import_state, fwo_api_call)
+
+ for link in self.rb_links:
+ rulebase_id = link.to_rulebase_id
+ gw_id = link.gw_id
+ if rulebase_id not in self.rulbase_to_gateway_map:
+ self.rulbase_to_gateway_map.update({rulebase_id: []})
+ if gw_id not in self.rulbase_to_gateway_map[rulebase_id]:
+ self.rulbase_to_gateway_map[rulebase_id].append(gw_id)
+
+ def get_gw_ids_for_rulebase_id(self, rulebase_id: int | None) -> list[int]:
+ if rulebase_id is None:
+ return []
+ return self.rulbase_to_gateway_map.get(rulebase_id, [])
diff --git a/roles/importer/files/importer/models/__init__.py b/roles/importer/files/importer/models/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/roles/importer/files/importer/models/action.py b/roles/importer/files/importer/models/action.py
new file mode 100644
index 0000000000..bf55cb7683
--- /dev/null
+++ b/roles/importer/files/importer/models/action.py
@@ -0,0 +1,7 @@
+from pydantic import BaseModel
+
+
+class Action(BaseModel):
+ action_id: int
+ action_name: str
+ allowed: bool = True
diff --git a/roles/importer/files/importer/models/caseinsensitiveenum.py b/roles/importer/files/importer/models/caseinsensitiveenum.py
new file mode 100644
index 0000000000..2da76f5d4b
--- /dev/null
+++ b/roles/importer/files/importer/models/caseinsensitiveenum.py
@@ -0,0 +1,14 @@
+from enum import Enum
+
+
+class CaseInsensitiveEnum(str, Enum):
+ @classmethod
+ def _missing_(cls, value: object) -> object | None:
+ if isinstance(value, str):
+ s = value.strip()
+ for member in cls:
+ # match either the value or the name, case-insensitive
+ if s.lower() in (member.value.lower(), member.name.lower()):
+ return member
+ # returning None -> Enum will raise the usual ValueError
+ return None
diff --git a/roles/importer/files/importer/models/fw_common.py b/roles/importer/files/importer/models/fw_common.py
new file mode 100644
index 0000000000..03e9766c9d
--- /dev/null
+++ b/roles/importer/files/importer/models/fw_common.py
@@ -0,0 +1,17 @@
+from abc import ABC, abstractmethod
+
+from model_controllers.fwconfigmanagerlist_controller import FwConfigManagerListController
+from model_controllers.import_state_controller import ImportStateController
+
+
+class FwCommon(ABC):
+ def has_config_changed(
+ self, _full_config: FwConfigManagerListController, _import_state: ImportStateController, _force: bool = False
+ ) -> bool:
+ return True
+
+ @abstractmethod
+ def get_config(
+ self, config_in: FwConfigManagerListController, import_state: ImportStateController
+ ) -> tuple[int, FwConfigManagerListController]:
+ raise NotImplementedError("Please Implement this method")
diff --git a/roles/importer/files/importer/models/fwconfig.py b/roles/importer/files/importer/models/fwconfig.py
new file mode 100644
index 0000000000..2de36298df
--- /dev/null
+++ b/roles/importer/files/importer/models/fwconfig.py
@@ -0,0 +1,15 @@
+from typing import Any
+
+from fwo_base import ConfFormat
+from pydantic import BaseModel
+
+"""
+ the configuraton of a firewall management to import
+ could be normalized or native config
+ management could be standard of super manager (MDS, fortimanager)
+"""
+
+
+class FwConfig(BaseModel):
+ ConfigFormat: ConfFormat
+ FwConf: dict[str, Any] = {}
diff --git a/roles/importer/files/importer/models/fwconfig_base.py b/roles/importer/files/importer/models/fwconfig_base.py
new file mode 100644
index 0000000000..384870ee3f
--- /dev/null
+++ b/roles/importer/files/importer/models/fwconfig_base.py
@@ -0,0 +1,15 @@
+import json
+
+from fwo_base import ConfFormat, ConfigAction
+from models.rulebase import Rulebase
+
+
+class FwoEncoder(json.JSONEncoder):
+ def default(self, o: object) -> object:
+ if isinstance(o, (ConfigAction, ConfFormat)):
+ return o.name
+
+ if isinstance(o, Rulebase):
+ return o.to_json()
+
+ return json.JSONEncoder.default(self, o)
diff --git a/roles/importer/files/importer/models/fwconfig_normalized.py b/roles/importer/files/importer/models/fwconfig_normalized.py
new file mode 100644
index 0000000000..bce6a05572
--- /dev/null
+++ b/roles/importer/files/importer/models/fwconfig_normalized.py
@@ -0,0 +1,96 @@
+from typing import Any
+
+from fwo_base import ConfFormat, ConfigAction
+from models.gateway import Gateway
+from models.networkobject import NetworkObject
+from models.rulebase import Rulebase
+from models.serviceobject import ServiceObject
+from pydantic import BaseModel
+
+
+class FwConfig(BaseModel):
+ ConfigFormat: ConfFormat = ConfFormat.NORMALIZED
+
+
+"""
+ the normalized configuraton of a firewall management to import
+ this applies to a single management which might be either a global or a stand-alone management
+
+ FwConfigNormalized:
+ {
+ 'action': 'INSERT|UPDATE|DELETE',
+ 'network_objects': [ ... ],
+ 'service_objects': [ ... ],
+ 'users': [...],
+ 'zone_objects': [ ... ],
+ 'policies': [
+ {
+ 'policy_name': 'pol1',
+ 'policy_uid': 'a32bc348234-23432a',
+ 'rules': [ { ... }, { ... }, ... ]
+ }
+ ],
+ 'gateways': # this is also a change, so these mappings are only listed once for insertion
+ {
+ 'gw-uid-1': {
+ 'name': 'gw1',
+ 'global_policy_uid': 'pol-global-1',
+ 'policies': ['policy_uid_1', 'policy_uid_2'] # here order is the order of policies on the gateway
+ }
+ }
+
+ }
+
+ write methods to
+ a) split a config into < X MB chunks
+ b) combine configs to a single config
+
+"""
+
+
+class FwConfigNormalized(FwConfig):
+ action: ConfigAction = ConfigAction.INSERT
+ network_objects: dict[str, NetworkObject] = {}
+ service_objects: dict[str, ServiceObject] = {}
+ users: dict[str, Any] = {}
+ zone_objects: dict[str, Any] = {}
+ rulebases: list[Rulebase] = []
+ gateways: list[Gateway] = []
+ ConfigFormat: ConfFormat = ConfFormat.NORMALIZED_LEGACY
+
+ model_config = {"arbitrary_types_allowed": True}
+
+ def get_rulebase(self, rulebase_uid: str) -> Rulebase:
+ """
+ Get the policy with a specific uid
+ :param policyUid: The UID of the relevant policy.
+ :return: Returns the policy with a specific uid, otherwise returns None.
+ """
+ rulebase = self.get_rulebase_or_none(rulebase_uid)
+ if rulebase is not None:
+ return rulebase
+
+ raise KeyError(f"Rulebase with UID {rulebase_uid} not found.")
+
+ def get_rulebase_or_none(self, rulebase_uid: str) -> Rulebase | None:
+ """
+ Get the policy with a specific uid
+ :param policyUid: The UID of the relevant policy.
+ :return: Returns the policy with a specific uid, otherwise returns None.
+ """
+ for rb in self.rulebases:
+ if rb.uid == rulebase_uid:
+ return rb
+ return None
+
+ def merge(self, other: "FwConfigNormalized"):
+ """
+ Merges the given config into this config.
+ """
+ self.action = other.action
+ self.network_objects.update(other.network_objects)
+ self.service_objects.update(other.service_objects)
+ self.users.update(other.users)
+ self.zone_objects.update(other.zone_objects)
+ self.rulebases.extend(other.rulebases)
+ self.gateways.extend(other.gateways)
diff --git a/roles/importer/files/importer/models/fwconfigmanager.py b/roles/importer/files/importer/models/fwconfigmanager.py
new file mode 100644
index 0000000000..a3c2655397
--- /dev/null
+++ b/roles/importer/files/importer/models/fwconfigmanager.py
@@ -0,0 +1,25 @@
+from typing import Any
+
+from models.fwconfig_normalized import FwConfigNormalized
+from pydantic import BaseModel, ConfigDict, Field
+from pydantic.alias_generators import to_pascal
+
+
+class FwConfigManager(BaseModel):
+ model_config = ConfigDict(alias_generator=to_pascal, validate_by_name=True)
+
+ manager_uid: str = Field(description="Unique identifier string of the management")
+ manager_name: str = Field(description="Name of the management")
+ is_super_manager: bool = Field(description="Indicates if the management is a super manager", default=False)
+ domain_uid: str = Field(description="Domain UID")
+ domain_name: str = Field(description="Domain name")
+ sub_manager_ids: list[int] = Field(default=[], description="List of sub-manager IDs")
+ configs: list[FwConfigNormalized] = Field(description="List of normalized firewall configurations")
+
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
+ kwargs.setdefault("by_alias", True)
+ return super().model_dump(**kwargs)
+
+ def model_dump_json(self, **kwargs: Any) -> str:
+ kwargs.setdefault("by_alias", True)
+ return super().model_dump_json(**kwargs)
diff --git a/roles/importer/files/importer/models/fwconfigmanagerlist.py b/roles/importer/files/importer/models/fwconfigmanagerlist.py
new file mode 100644
index 0000000000..248676315c
--- /dev/null
+++ b/roles/importer/files/importer/models/fwconfigmanagerlist.py
@@ -0,0 +1,23 @@
+from typing import Any
+
+from fwconfig_base import ConfFormat
+from models.fwconfigmanager import FwConfigManager
+from pydantic import BaseModel
+
+"""
+ a list of normalized configuratons of a firewall management to import
+ FwConfigManagerList: [ FwConfigManager ]
+"""
+
+
+class FwConfigManagerList(BaseModel):
+ ConfigFormat: ConfFormat = ConfFormat.NORMALIZED
+ ManagerSet: list[FwConfigManager] = []
+ native_config: (
+ dict[str, Any] | None
+ ) = {} # native config as dict, if available # TODO: change inital value to None?
+
+ model_config = {"arbitrary_types_allowed": True}
+
+ def __str__(self):
+ return f"{self.ManagerSet!s})"
diff --git a/roles/importer/files/importer/models/fworch_config.py b/roles/importer/files/importer/models/fworch_config.py
new file mode 100644
index 0000000000..5b3926207c
--- /dev/null
+++ b/roles/importer/files/importer/models/fworch_config.py
@@ -0,0 +1,11 @@
+"""
+the configuraton of a firewall orchestrator itself
+as read from the global config file including FWO URI
+"""
+
+
+class FworchConfig:
+ fwo_api_url: str
+ fwo_user_mgmt_api_uri: str | None
+ api_fetch_size: int
+ importer_password: str | None
diff --git a/roles/importer/files/importer/models/gateway.py b/roles/importer/files/importer/models/gateway.py
new file mode 100644
index 0000000000..1be452acb6
--- /dev/null
+++ b/roles/importer/files/importer/models/gateway.py
@@ -0,0 +1,29 @@
+from typing import Any
+
+from models.rulebase_link import RulebaseLinkUidBased
+from pydantic import BaseModel
+
+"""
+Gateway
+ {
+ 'gw-uid-1': {
+ 'name': 'gw1',
+ 'global_policy_uid': 'pol-global-1',
+ 'policies': ['policy_uid_1', 'policy_uid_2'] # here order is the order of policies on the gateway
+ 'nat-policies': ['nat_policy_uid_1'] # is always only a single policy?
+ }
+}
+"""
+
+
+class Gateway(BaseModel):
+ Uid: str | None = None
+ Name: str | None = None
+ Routing: list[dict[str, Any]] = []
+ Interfaces: list[dict[str, Any]] = []
+ RulebaseLinks: list[RulebaseLinkUidBased] = []
+ GlobalPolicyUid: str | None = None
+ EnforcedPolicyUids: list[str] | None = []
+ EnforcedNatPolicyUids: list[str] | None = []
+ ImportDisabled: bool = False
+ ShowInUI: bool = True
diff --git a/roles/importer/files/importer/models/import_state.py b/roles/importer/files/importer/models/import_state.py
new file mode 100644
index 0000000000..80445245d9
--- /dev/null
+++ b/roles/importer/files/importer/models/import_state.py
@@ -0,0 +1,102 @@
+import time
+
+from fwo_exceptions import FwoImporterError
+from fwo_log import FWOLogger
+from model_controllers.fworch_config_controller import FworchConfigController
+from model_controllers.import_statistics_controller import ImportStatisticsController
+from model_controllers.management_controller import ManagementController
+
+"""Used for storing state during import process per management"""
+
+
+class ImportState:
+ debug_level: int
+ verify_certs: bool = False
+ config_changed_since_last_import: bool
+ fwo_config: FworchConfigController
+ mgm_details: ManagementController
+ import_id: int = -1
+ import_file_name: str = ""
+ force_import: bool
+ import_version: int
+ data_retention_days: int
+ days_since_last_full_import: int
+ last_full_import_id: int
+ last_full_import_date: str | None = None
+ last_successful_import: str | None = None
+ is_full_import: bool
+ is_initial_import: bool = False
+ responsible_for_importing: bool = True
+ is_clearing_import: bool = False
+
+ def __init__(self) -> None:
+ self.stats: ImportStatisticsController = ImportStatisticsController()
+ self.start_time: int = int(time.time())
+ self.actions: dict[str, int] = {}
+ self.tracks: dict[str, int] = {}
+ self.link_types: dict[str, int] = {}
+ self.gateway_map: dict[int, dict[str, int]] = {}
+ self.rulebase_map: dict[str, int] = {}
+ self.rule_map: dict[str, int] = {}
+ self.management_map: dict[str, int] = {}
+ self.color_map: dict[str, int] = {}
+ self.rulebase_to_gateway_map: dict[int, list[int]] = {}
+ self.removed_rules_map: dict[str, int] = {}
+ self.data_retention_days: int = 30
+
+ def lookup_rule(self, rule_uid: str) -> int | None:
+ return self.rule_map.get(rule_uid, None)
+
+ def lookup_action(self, action_str: str) -> int:
+ action_id = self.actions.get(action_str.lower(), None)
+ if action_id is None:
+ FWOLogger.error(f"Action {action_str} not found")
+ raise FwoImporterError(f"Action {action_str} not found")
+ return action_id
+
+ def lookup_track(self, track_str: str) -> int:
+ track_id = self.tracks.get(track_str.lower(), None)
+ if track_id is None:
+ FWOLogger.error(f"Track {track_str} not found")
+ raise FwoImporterError(f"Track {track_str} not found")
+ return track_id
+
+ def lookup_rulebase_id(self, rulebase_uid: str) -> int:
+ rulebase_id = self.rulebase_map.get(rulebase_uid, None)
+ if rulebase_id is None:
+ FWOLogger.error(f"Rulebase {rulebase_uid} not found in {len(self.rulebase_map)} known rulebases")
+ raise FwoImporterError(f"Rulebase {rulebase_uid} not found in {len(self.rulebase_map)} known rulebases")
+ return rulebase_id
+
+ def lookup_link_type(self, link_uid: str) -> int:
+ link_type_id = self.link_types.get(link_uid, None)
+ if not link_type_id:
+ FWOLogger.error(f"Link type {link_uid} not found")
+ raise FwoImporterError(f"Link type {link_uid} not found")
+ return link_type_id
+
+ def lookup_gateway_id(self, gw_uid: str) -> int | None:
+ mgm_id = self.mgm_details.current_mgm_id
+ gws_for_mgm = self.gateway_map.get(mgm_id, {})
+ gw_id = gws_for_mgm.get(gw_uid, None)
+ if gw_id is None:
+ FWOLogger.error(
+ f"fwo_api:import_latest_config - no gateway id found for current mgm id '{mgm_id}' and gateway uid '{gw_uid}' in {len(gws_for_mgm)} known gateways for this mgm"
+ )
+ raise FwoImporterError(
+ f"fwo_api:import_latest_config - no gateway id found for current mgm id '{mgm_id}' and gateway uid '{gw_uid}' in {len(gws_for_mgm)} known gateways for this mgm"
+ )
+ return gw_id
+
+ def lookup_all_gateway_ids(self) -> list[int]:
+ mgm_id = self.mgm_details.current_mgm_id
+ gws_for_mgm = self.gateway_map.get(mgm_id, {})
+ return list(gws_for_mgm.values())
+
+ def lookup_management_id(self, mgm_uid: str) -> int | None:
+ if not self.management_map.get(mgm_uid, None):
+ FWOLogger.error(f"fwo_api:import_latest_config - no mgm id found for current manager uid '{mgm_uid}'")
+ return self.management_map.get(mgm_uid, None)
+
+ def lookup_color_id(self, color_str: str) -> int:
+ return self.color_map.get(color_str, 1) # 1 = forground color black
diff --git a/roles/importer/files/importer/models/import_statistics.py b/roles/importer/files/importer/models/import_statistics.py
new file mode 100644
index 0000000000..1fad9e0f26
--- /dev/null
+++ b/roles/importer/files/importer/models/import_statistics.py
@@ -0,0 +1,24 @@
+class ImportStatistics:
+ network_object_add_count: int = 0
+ network_object_delete_count: int = 0
+ network_object_change_count: int = 0
+ service_object_add_count: int = 0
+ service_object_delete_count: int = 0
+ service_object_change_count: int = 0
+ user_object_add_count: int = 0
+ user_object_delete_count: int = 0
+ user_object_change_count: int = 0
+ zone_object_add_count: int = 0
+ zone_object_delete_count: int = 0
+ zone_object_change_count: int = 0
+ rule_add_count: int = 0
+ rule_delete_count: int = 0
+ rule_change_count: int = 0
+ rule_move_count: int = 0 # when a rule is moved within the same rulebase
+ rulebase_add_count: int = 0
+ rulebase_change_count: int = 0
+ rulebase_delete_count: int = 0
+ rule_enforce_change_count: int = 0
+ rulebase_link_add_count: int = 0
+ rulebase_link_change_count: int = 0
+ rulebase_link_delete_count: int = 0
diff --git a/roles/importer/files/importer/models/management.py b/roles/importer/files/importer/models/management.py
new file mode 100644
index 0000000000..af4bc473dc
--- /dev/null
+++ b/roles/importer/files/importer/models/management.py
@@ -0,0 +1,27 @@
+from typing import Any
+
+from pydantic import BaseModel
+
+
+class Management(BaseModel):
+ mgm_id: int
+ name: str
+ uid: str
+ is_super_manager: bool
+ hostname: str
+ import_disabled: bool
+ devices: list[dict[str, Any]]
+ importer_hostname: str
+ device_type_name: str
+ device_type_version: str
+ port: int
+ import_user: str
+ secret: str
+ sub_manager_ids: list[int]
+ current_mgm_id: int
+ current_mgm_is_super_manager: bool
+ domain_name: str
+ domain_uid: str
+ sub_managers: list["Management"]
+ cloud_client_id: str | None = None
+ cloud_client_secret: str | None = None
diff --git a/roles/importer/files/importer/models/networkobject.py b/roles/importer/files/importer/models/networkobject.py
new file mode 100644
index 0000000000..ba2e8b3a30
--- /dev/null
+++ b/roles/importer/files/importer/models/networkobject.py
@@ -0,0 +1,94 @@
+from typing import Any
+
+from netaddr import AddrFormatError, IPNetwork
+from pydantic import BaseModel, field_serializer, field_validator
+
+
+class NetworkObject(BaseModel):
+ obj_uid: str
+ obj_name: str
+ obj_ip: IPNetwork | None = None
+ obj_ip_end: IPNetwork | None = None
+ obj_color: str
+ obj_typ: str
+ obj_member_refs: str | None = None
+ obj_member_names: str | None = None
+ obj_comment: str | None = None
+
+ @field_validator("obj_ip", "obj_ip_end", mode="before")
+ @classmethod
+ def convert_strings_to_ip_objects(cls, value: object, info: Any) -> IPNetwork | None:
+ """
+ Convert string values to IPNetwork objects, treating 'None' or empty as None.
+ """
+ if value is None:
+ return None
+ if isinstance(value, IPNetwork):
+ return value
+
+ s = str(value).strip()
+ if s.lower() == "none" or s == "":
+ return None
+
+ try:
+ return IPNetwork(s)
+ except AddrFormatError as e:
+ raise ValueError(f"Invalid {info.field_name} network format: {value}") from e
+
+ @field_serializer("obj_ip", "obj_ip_end")
+ def serialize_ipnetwork(self, value: IPNetwork | None, _info: Any) -> str | None:
+ """
+ Serialize IPNetwork objects to strings, keeping None as None.
+ """
+ return None if value is None else str(value)
+
+ model_config = {"arbitrary_types_allowed": True}
+
+
+class NetworkObjectForImport:
+ obj_uid: str
+ obj_name: str
+ obj_ip: str | None
+ obj_ip_end: str | None
+ obj_color_id: int | None
+ obj_member_refs: str | None
+ obj_member_names: str | None
+ obj_comment: str | None
+ mgm_id: int
+ obj_create: int
+ obj_last_seen: int
+ obj_typ_id: int
+
+ def __init__(self, nw_object: NetworkObject, mgm_id: int, import_id: int, color_id: int, typ_id: int):
+ self.obj_uid = nw_object.obj_uid
+ self.obj_name = nw_object.obj_name
+ self.obj_ip = str(nw_object.obj_ip)
+ self.obj_ip_end = str(nw_object.obj_ip_end)
+ self.obj_color_id = color_id
+ self.obj_member_refs = nw_object.obj_member_refs
+ self.obj_member_names = nw_object.obj_member_names
+ self.obj_comment = nw_object.obj_comment
+ self.mgm_id = mgm_id
+ self.obj_create = import_id
+ self.obj_last_seen = import_id
+ self.obj_typ_id = typ_id
+
+ def to_dict(self) -> dict[str, Any]:
+ result: dict[str, Any] = {
+ "obj_uid": self.obj_uid,
+ "obj_name": self.obj_name,
+ "obj_color_id": self.obj_color_id,
+ "obj_member_refs": self.obj_member_refs,
+ "obj_member_names": self.obj_member_names,
+ "obj_comment": self.obj_comment,
+ "mgm_id": self.mgm_id,
+ "obj_create": self.obj_create,
+ "obj_last_seen": self.obj_last_seen,
+ "obj_typ_id": self.obj_typ_id,
+ }
+
+ if self.obj_ip is not None and self.obj_ip != "None":
+ result.update({"obj_ip": self.obj_ip})
+ if self.obj_ip_end is not None and self.obj_ip_end != "None":
+ result.update({"obj_ip_end": self.obj_ip_end})
+ return result
diff --git a/roles/importer/files/importer/models/rule.py b/roles/importer/files/importer/models/rule.py
new file mode 100644
index 0000000000..a018c1a77e
--- /dev/null
+++ b/roles/importer/files/importer/models/rule.py
@@ -0,0 +1,166 @@
+from models.caseinsensitiveenum import CaseInsensitiveEnum
+from pydantic import BaseModel
+
+
+class RuleType(CaseInsensitiveEnum):
+ ACCESS = "access"
+ NAT = "nat"
+ ACCESSANDNAT = "accessandnat"
+ SECTIONHEADER = "sectionheader"
+
+
+class RuleAction(CaseInsensitiveEnum):
+ ACCEPT = "accept"
+ DROP = "drop"
+ REJECT = "reject"
+ CLIENTAUTH = "client auth"
+ INNERLAYER = "inner layer"
+ INFORM = "inform"
+ ASK = "ask"
+
+
+class RuleTrack(CaseInsensitiveEnum):
+ NONE = "none"
+ LOG = "log"
+ ALERT = "alert"
+ DETAILEDLOG = "detailed log"
+ EXTENDEDLOG = "extended log"
+ USERDEFINED = "userdefined"
+ MAIL = "mail"
+ ACCOUNT = "account"
+ USERDEFINED1 = "userdefined 1"
+ USERDEFINED2 = "userdefined 2"
+ USERDEFINED3 = "userdefined 3"
+ SNMPTRAP = "snmptrap"
+
+
+# RuleNormalized is the model for a normalized rule (containing no DB IDs)
+class RuleNormalized(BaseModel): # noqa: PLW1641
+ rule_num: int
+ rule_num_numeric: float
+ rule_disabled: bool
+ rule_src_neg: bool
+ rule_src: str
+ rule_src_refs: str
+ rule_dst_neg: bool
+ rule_dst: str
+ rule_dst_refs: str
+ rule_svc_neg: bool
+ rule_svc: str
+ rule_svc_refs: str
+ rule_action: RuleAction
+ rule_track: RuleTrack
+ rule_installon: str | None = None
+ rule_time: str | None = None
+ rule_name: str | None = None
+ rule_uid: str | None = None
+ rule_custom_fields: str | None = None
+ rule_implied: bool
+ rule_type: RuleType = RuleType.SECTIONHEADER
+ last_change_admin: str | None = None
+ parent_rule_uid: str | None = None
+ last_hit: str | None = None
+ rule_comment: str | None = None
+ rule_src_zone: str | None = None
+ rule_dst_zone: str | None = None
+ rule_head_text: str | None = None
+
+ def __eq__(self, other: object) -> bool:
+ if not isinstance(other, RuleNormalized):
+ return NotImplemented
+ # 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
+
+
+"""
+ based on public.rule:
+
+ "rule_id" BIGSERIAL,
+ "last_change_admin" Integer,
+ "rule_name" Varchar,
+ "mgm_id" Integer NOT NULL,
+ "parent_rule_id" BIGINT,
+ "parent_rule_type" smallint,
+ "active" Boolean NOT NULL Default TRUE,
+ "removed" BIGINT,
+ "rule_num" Integer NOT NULL,
+ "rule_num_numeric" NUMERIC(16, 8),
+ "rule_ruleid" Varchar,
+ "rule_uid" Text,
+ "rule_disabled" Boolean NOT NULL Default false,
+ "rule_src_neg" Boolean NOT NULL Default false,
+ "rule_dst_neg" Boolean NOT NULL Default false,
+ "rule_svc_neg" Boolean NOT NULL Default false,
+ "action_id" Integer NOT NULL,
+ "track_id" Integer NOT NULL,
+ "rule_src" Text NOT NULL,
+ "rule_dst" Text NOT NULL,
+ "rule_svc" Text NOT NULL,
+ "rule_src_refs" Text,
+ "rule_dst_refs" Text,
+ "rule_svc_refs" Text,
+ "rule_from_zone" Integer,
+ "rule_to_zone" Integer,
+ "rule_action" Text NOT NULL,
+ "rule_track" Text NOT NULL,
+ "rule_installon" Varchar,
+ "rule_time" Varchar,
+ "rule_comment" Text,
+ "rule_head_text" Text,
+ "rule_implied" Boolean NOT NULL Default FALSE,
+ "rule_create" BIGINT NOT NULL,
+ "rule_last_seen" BIGINT NOT NULL,
+ "dev_id" Integer,
+ "rule_custom_fields" jsonb,
+ "access_rule" BOOLEAN Default TRUE,
+ "nat_rule" BOOLEAN Default FALSE,
+ "xlate_rule" BIGINT,
+ "is_global" BOOLEAN DEFAULT FALSE NOT NULL,
+ "rulebase_id" Integer NOT NULL,
+"""
+
+
+# Rule is the model for a rule to be imported into the DB (containing IDs)
+class Rule(BaseModel):
+ access_rule: bool = True
+ action_id: int
+ is_global: bool = False
+ last_change_admin: int | None = None
+ mgm_id: int
+ nat_rule: bool = False
+ parent_rule_id: int | None = None
+ removed: int | None = None
+ rule_action: str
+ rule_comment: str | None = None
+ rule_create: int
+ rule_custom_fields: str | None = None
+ rule_disabled: bool
+ rule_dst: str
+ rule_dst_neg: bool
+ rule_dst_refs: str
+ rule_from_zone: int | None = None
+ rule_head_text: str | None = None
+ rule_implied: bool = False
+ rule_installon: str | None = None
+ rule_last_seen: int
+ rule_name: str | None = None
+ rule_num: int
+ rule_num_numeric: float
+ rule_src: str
+ rule_src_neg: bool
+ rule_src_refs: str
+ rule_svc: str
+ rule_svc_neg: bool
+ rule_svc_refs: str
+ rule_time: str | None = None
+ rule_to_zone: int | None = None
+ track_id: int
+ xlate_rule: int | None = None
+ rule_track: str
+ rule_uid: str | None = None
+ rulebase_id: int | None = None
diff --git a/roles/importer/files/importer/models/rule_enforced_on_gateway.py b/roles/importer/files/importer/models/rule_enforced_on_gateway.py
new file mode 100644
index 0000000000..0ef7b0c8fb
--- /dev/null
+++ b/roles/importer/files/importer/models/rule_enforced_on_gateway.py
@@ -0,0 +1,34 @@
+from typing import Any
+
+from pydantic import BaseModel
+
+
+# the model for a connection between a rule and a gateway
+# each rule has to be linked to the gateway the rule is enforced on
+# this is e.g. used for the "install on" feature in Check Point
+class RuleEnforcedOnGateway(BaseModel):
+ # "rule_id" Integer NOT NULL,
+ # "dev_id" Integer, -- NULL if rule is available for all gateways of its management
+ # "created" BIGINT,
+ # "removed" BIGINT
+ rule_id: int
+ dev_id: int
+ created: int | None
+ removed: int | None
+
+ def __init__(self, rule_id: int, dev_id: int, created: int | None = None, removed: int | None = None) -> None:
+ self.rule_id = rule_id
+ self.dev_id = dev_id
+ self.created = created
+ self.removed = removed
+
+ def to_dict(self) -> dict[str, Any]:
+ return {"rule_id": self.rule_id, "dev_id": self.dev_id, "created": self.created, "removed": self.removed}
+
+
+# normalized config without db ids
+class RuleEnforcedOnGatewayNormalized(BaseModel):
+ rule_uid: str
+ dev_uid: str
+
+ model_config = {"arbitrary_types_allowed": True}
diff --git a/roles/importer/files/importer/models/rule_from.py b/roles/importer/files/importer/models/rule_from.py
new file mode 100644
index 0000000000..354e2aae0c
--- /dev/null
+++ b/roles/importer/files/importer/models/rule_from.py
@@ -0,0 +1,14 @@
+from pydantic import BaseModel
+
+
+# RuleFrom is the model for a normalized rule (containing DB IDs)
+# does not contain the rule_from_id primary key as this one is set by the database
+class RuleFrom(BaseModel):
+ active: bool = True
+ rule_id: int
+ obj_id: int
+ rf_create: int
+ rf_last_seen: int
+ removed: int | None = None
+ user_id: int | None = None
+ negated: bool = False
diff --git a/roles/importer/files/importer/models/rule_metadatum.py b/roles/importer/files/importer/models/rule_metadatum.py
new file mode 100644
index 0000000000..8799644367
--- /dev/null
+++ b/roles/importer/files/importer/models/rule_metadatum.py
@@ -0,0 +1,26 @@
+from pydantic import BaseModel
+
+
+# Rule is the model for a normalized rule_metadata
+class RuleMetadatum(BaseModel):
+ rule_uid: str
+ mgm_id: int
+ rule_created: str | None = None
+ rule_last_modified: str | None = None
+ rule_first_hit: str | None = None
+ rule_last_hit: str | None = None
+ rule_hit_counter: int | None = None
+ rule_last_certified: str | None = None
+ rule_last_certifier: str | None = None
+ rule_last_certifier_dn: str | None = None
+ rule_owner: int | None = None
+ rule_owner_dn: str | None = None
+ rule_to_be_removed: bool = False
+ last_change_admin: str | None = None
+ rule_decert_date: int | None = None
+ rule_recertification_comment: str | None = None
+
+
+# RuleForImport is the model for a rule to be imported into the DB (containing IDs)
+class RuleMetadatumForImport(RuleMetadatum):
+ rule_metadata_id: int | None = None
diff --git a/roles/importer/files/importer/models/rule_service.py b/roles/importer/files/importer/models/rule_service.py
new file mode 100644
index 0000000000..ca59126b6f
--- /dev/null
+++ b/roles/importer/files/importer/models/rule_service.py
@@ -0,0 +1,11 @@
+from pydantic import BaseModel
+
+
+class RuleService(BaseModel):
+ active: bool = True
+ rule_id: int
+ svc_id: int
+ rs_create: int
+ rs_last_seen: int
+ removed: int | None = None
+ negated: bool = False
diff --git a/roles/importer/files/importer/models/rule_to.py b/roles/importer/files/importer/models/rule_to.py
new file mode 100644
index 0000000000..81079929f5
--- /dev/null
+++ b/roles/importer/files/importer/models/rule_to.py
@@ -0,0 +1,14 @@
+from pydantic import BaseModel
+
+
+# RuleTo is the model for a normalized rule (containing DB IDs)
+# does not contain the rule_to_id primary key as this one is set by the database
+class RuleTo(BaseModel):
+ active: bool = True
+ rule_id: int
+ obj_id: int
+ rt_create: int
+ rt_last_seen: int
+ removed: int | None = None
+ user_id: int | None = None
+ negated: bool = False
diff --git a/roles/importer/files/importer/models/rulebase.py b/roles/importer/files/importer/models/rulebase.py
new file mode 100644
index 0000000000..33f1d2466f
--- /dev/null
+++ b/roles/importer/files/importer/models/rulebase.py
@@ -0,0 +1,45 @@
+from models.rule import Rule, RuleNormalized
+from pydantic import BaseModel
+
+
+# Rulebase is the model for a rulebase (containing no DB IDs)
+class Rulebase(BaseModel):
+ uid: str
+ name: str
+ mgm_uid: str
+ is_global: bool = False
+ rules: dict[str, RuleNormalized] = {}
+
+ def to_json(self) -> dict[str, object]:
+ return {
+ "uid": self.uid,
+ "name": self.name,
+ "mgm_uid": self.mgm_uid,
+ "is_global": self.is_global,
+ "rules": {uid: rule.model_dump() for uid, rule in self.rules.items()},
+ }
+
+
+# RulebaseForImport is the model for a rule to be imported into the DB (containing IDs)
+"""
+ based on public.rulebase:
+
+ # "id" SERIAL primary key,
+ # "name" Varchar NOT NULL,
+ # "uid" Varchar NOT NULL,
+ # "mgm_id" Integer NOT NULL,
+ # "is_global" BOOLEAN DEFAULT FALSE NOT NULL,
+ # "created" BIGINT,
+ # "removed" BIGINT
+"""
+
+
+class RulebaseForImport(BaseModel):
+ id: int | None = None
+ name: str
+ uid: str
+ mgm_id: int
+ is_global: bool = False
+ created: int
+ removed: int | None = None
+ rules: list[Rule] = []
diff --git a/roles/importer/files/importer/models/rulebase_link.py b/roles/importer/files/importer/models/rulebase_link.py
new file mode 100644
index 0000000000..537ded6c9e
--- /dev/null
+++ b/roles/importer/files/importer/models/rulebase_link.py
@@ -0,0 +1,61 @@
+from typing import Any
+
+from pydantic import BaseModel, TypeAdapter
+
+
+# RulebaseLinkUidBased is the model for a rulebase_link (containing no DB IDs)
+class RulebaseLinkUidBased(BaseModel):
+ from_rulebase_uid: str | None = None
+ from_rule_uid: str | None = None
+ to_rulebase_uid: str
+ link_type: str = "section"
+ is_initial: bool
+ is_global: bool
+ is_section: bool
+
+ def to_dict(self) -> dict[str, object | str | bool | None]:
+ return {
+ "from_rule_uid": self.from_rule_uid,
+ "from_rulebase_uid": self.from_rulebase_uid,
+ "to_rulebase_uid": self.to_rulebase_uid,
+ "link_type": self.link_type,
+ "is_initial": self.is_initial,
+ "is_global": self.is_global,
+ "is_section": self.is_section,
+ }
+
+
+class RulebaseLink(BaseModel):
+ id: int | None = None # will be created during db import
+ gw_id: int
+ from_rule_id: int | None = None # null for initial rulebase
+ from_rulebase_id: int | None = None # either from_rule_id or from_rulebase_id must be set
+ to_rulebase_id: int
+ link_type: int = 1
+ is_initial: bool
+ is_global: bool
+ is_section: bool
+ created: int
+ removed: int | None = None
+
+ class Config:
+ populate_by_name = True
+
+ def to_dict(self) -> dict[str, Any]:
+ return {
+ "gw_id": self.gw_id,
+ "from_rule_id": self.from_rule_id,
+ "from_rulebase_id": self.from_rulebase_id,
+ "to_rulebase_id": self.to_rulebase_id,
+ "link_type": self.link_type,
+ "is_initial": self.is_initial,
+ "is_global": self.is_global,
+ "is_section": self.is_section,
+ "created": self.created,
+ "removed": self.removed,
+ }
+
+
+def parse_rulebase_links(data: list[dict[str, Any]]) -> list[RulebaseLink]:
+ adapter = TypeAdapter(list[RulebaseLink])
+ return adapter.validate_python(data)
diff --git a/roles/importer/files/importer/models/serviceobject.py b/roles/importer/files/importer/models/serviceobject.py
new file mode 100644
index 0000000000..522745bf21
--- /dev/null
+++ b/roles/importer/files/importer/models/serviceobject.py
@@ -0,0 +1,73 @@
+from typing import Any
+
+from pydantic import BaseModel
+
+
+class ServiceObject(BaseModel):
+ svc_uid: str
+ svc_name: str
+ svc_port: int | None = None
+ svc_port_end: int | None = None
+ svc_color: str
+ svc_typ: str # TODO: ENUM
+ ip_proto: int | None = None
+ svc_member_refs: str | None = None
+ svc_member_names: str | None = None
+ svc_comment: str | None = None
+ svc_timeout: int | None = None
+ rpc_nr: str | None = None
+
+
+class ServiceObjectForImport:
+ svc_uid: str
+ svc_name: str
+ svc_port: int | None
+ svc_port_end: int | None
+ svc_color_id: int
+ svc_typ: str # TODO: ENUM
+ ip_proto_id: int | None
+ svc_member_refs: str | None
+ svc_member_names: str | None
+ svc_comment: str | None
+ svc_timeout: int | None
+ svc_rpcnr: str | None
+ mgm_id: int
+ svc_create: int
+ svc_last_seen: int
+ svc_typ_id: int
+
+ def __init__(self, svc_object: ServiceObject, mgm_id: int, import_id: int, color_id: int, typ_id: int):
+ self.svc_uid = svc_object.svc_uid
+ self.svc_name = svc_object.svc_name
+ self.svc_port = svc_object.svc_port
+ self.svc_port_end = svc_object.svc_port_end
+ self.svc_color_id = color_id
+ self.svc_typ_id = typ_id
+ self.ip_proto_id = svc_object.ip_proto
+ self.svc_member_refs = svc_object.svc_member_refs
+ self.svc_member_names = svc_object.svc_member_names
+ self.svc_comment = svc_object.svc_comment
+ self.svc_timeout = svc_object.svc_timeout
+ self.svc_rpcnr = svc_object.rpc_nr
+ self.mgm_id = mgm_id
+ self.svc_create = import_id
+ self.svc_last_seen = import_id
+
+ def to_dict(self) -> dict[str, Any]:
+ return {
+ "svc_uid": self.svc_uid,
+ "svc_name": self.svc_name,
+ "svc_port": self.svc_port,
+ "svc_port_end": self.svc_port_end,
+ "ip_proto_id": self.ip_proto_id,
+ "svc_color_id": self.svc_color_id,
+ "svc_member_refs": self.svc_member_refs,
+ "svc_member_names": self.svc_member_names,
+ "svc_comment": self.svc_comment,
+ "svc_create": self.svc_create,
+ "svc_last_seen": self.svc_last_seen,
+ "svc_typ_id": self.svc_typ_id,
+ "svc_rpcnr": self.svc_rpcnr,
+ "svc_timeout": self.svc_timeout,
+ "mgm_id": self.mgm_id,
+ }
diff --git a/roles/importer/files/importer/models/track.py b/roles/importer/files/importer/models/track.py
new file mode 100644
index 0000000000..5816a9e559
--- /dev/null
+++ b/roles/importer/files/importer/models/track.py
@@ -0,0 +1,6 @@
+from pydantic import BaseModel
+
+
+class Track(BaseModel):
+ track_id: int
+ track_name: str
diff --git a/roles/importer/files/importer/nsx4ff/discovery_logging.conf b/roles/importer/files/importer/nsx4ff/discovery_logging.conf
deleted file mode 100644
index 139c55a9cb..0000000000
--- a/roles/importer/files/importer/nsx4ff/discovery_logging.conf
+++ /dev/null
@@ -1,41 +0,0 @@
-[loggers]
-keys=root,discoveryDebugLogger
-#keys=root,__main__
-
-[handlers]
-keys=consoleHandler,debugFileHandler
-
-[formatters]
-keys=defaultFormatter,debugFileFormatter
-
-[logger_root]
-level=DEBUG
-handlers=consoleHandler
-
-[logger_discoveryDebugLogger]
-#[logger___main__]
-level=DEBUG
-handlers=debugFileHandler
-qualname=discoveryDebugLogger
-#qualname=__main__
-propagate=0
-
-[handler_consoleHandler]
-class=StreamHandler
-level=DEBUG
-formatter=defaultFormatter
-args=(sys.stderr,)
-
-[handler_debugFileHandler]
-class=FileHandler
-level=DEBUG
-formatter=debugFileFormatter
-args=('/tmp/fworch_discovery.log',)
-# args=('/var/log/fworch/discovery.log',)
-
-[formatter_defaultFormatter]
-format=%(levelname)s:%(name)s:%(message)s
-
-[formatter_debugFileFormatter]
-format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
-
diff --git a/roles/importer/files/importer/nsx4ff/fwcommon.py b/roles/importer/files/importer/nsx4ff/fwcommon.py
deleted file mode 100644
index 5e5dea0537..0000000000
--- a/roles/importer/files/importer/nsx4ff/fwcommon.py
+++ /dev/null
@@ -1,105 +0,0 @@
-import sys
-import base64
-from common import importer_base_dir
-sys.path.append(importer_base_dir + "/nsx4ff")
-from nsx_service import normalize_svcobjects
-# from nsx_application import normalize_application_objects
-from nsx_rule import normalize_access_rules
-from nsx_network import normalize_nwobjects
-# from nsx_zone import normalize_zones
-from nsx_getter import update_config_with_nsxdcfw_api_call
-from fwo_log import getFwoLogger
-from nsx_base import api_version_str
-
-def has_config_changed(full_config, mgm_details, force=False):
- # dummy - may be filled with real check later on
- return True
-
-
-def get_config(config2import, full_config, current_import_id, mgm_details, limit=1000, force=False, jwt=''):
- logger = getFwoLogger()
- if full_config == {}: # no native config was passed in, so getting it from Azzure
- parsing_config_only = False
- else:
- parsing_config_only = True
-
- if not parsing_config_only: # no native config was passed in, so getting it from Palo Firewall
- apipwd = mgm_details["import_credential"]['secret']
- apiuser = mgm_details["import_credential"]['user']
- apihost = mgm_details["hostname"]
- domain = mgm_details["configPath"]
-
- vsys_base_objects = ["/infra/services"]
- vsys_object_groups = ["/infra/domains/{domain}/groups".format(domain=domain)]
- vsys_objects = vsys_object_groups + vsys_base_objects
-
- #predef_objects = ["/Objects/Applications"]
- rulebase_names = ["security-policies"] # , "/Policies/NATRules"]
-
- for obj_path in vsys_objects:
- full_config[obj_path] = []
-
- # for obj_path in predef_objects:
- # full_config[obj_path] = []
-
- credentials = base64.b64encode((apiuser + ":" + apipwd).encode())
-
- ## get objects:
- # base_url = "https://{apihost}/policy/api/v1/infra/domains/{domain}/security-policies/[policy name]".format(apihost=apihost, api_version_str=api_version_str)
-
- # vsys_name = "vsys1" # TODO - automate this hard-coded name
- # location = "vsys" # alternative: panorama-pushed
-
-
- for obj_path in vsys_objects:
- base_url = "https://{apihost}/policy/api/v1{path}".format(apihost=apihost, path=obj_path)
- update_config_with_nsxdcfw_api_call(base_url, full_config, obj_path, obj_type=obj_path, credentials=credentials)
-
- # for obj_path in predef_objects:
- # update_config_with_nsxdcfw_api_call(key, base_url, full_config, obj_path + "?location={location}".format(location="predefined"), obj_type=obj_path)
-
- # users
-
- # get rules
- full_config.update({'devices': {}})
- for device in mgm_details["devices"]:
- dev_id = device['id']
- dev_name = device['local_rulebase_name']
- full_config['devices'].update({ dev_id: {} })
-
- for obj_path in rulebase_names:
- base_url = "https://{apihost}/policy/api/v1/infra/domains/{domain}/{rulebase_name}/{policy_name}".format(apihost=apihost, domain=domain, policy_name=dev_name, rulebase_name=obj_path)
- update_config_with_nsxdcfw_api_call(
- base_url, full_config['devices'][device['id']],
- obj_path,
- obj_type=obj_path, credentials=credentials)
-
- ##################
- # now we normalize relevant parts of the raw config and write the results to config2import dict
-
- normalize_nwobjects(full_config, config2import, current_import_id, jwt=jwt, mgm_id=mgm_details['id'], domain=domain)
- normalize_svcobjects(full_config, config2import, current_import_id)
- # normalize_application_objects(full_config, config2import, current_import_id)
- # normalize_users(full_config, config2import, current_import_id, user_scope)
-
- # adding default any and predefined objects
- any_nw_svc = {"svc_uid": "any_svc_placeholder", "svc_name": "any", "svc_comment": "Placeholder service.",
- "svc_typ": "simple", "ip_proto": -1, "svc_port": 0, "svc_port_end": 65535, "control_id": current_import_id}
- http_svc = {"svc_uid": "http_predefined_svc", "svc_name": "service-http", "svc_comment": "Predefined service",
- "svc_typ": "simple", "ip_proto": 6, "svc_port": 80, "control_id": current_import_id}
- https_svc = {"svc_uid": "https_predefined_svc", "svc_name": "service-https", "svc_comment": "Predefined service",
- "svc_typ": "simple", "ip_proto": 6, "svc_port": 443, "control_id": current_import_id}
-
- config2import["service_objects"].append(any_nw_svc)
- config2import["service_objects"].append(http_svc)
- config2import["service_objects"].append(https_svc)
-
- any_nw_object = {"obj_uid": "any_obj_placeholder", "obj_name": "any", "obj_comment": "Placeholder object.",
- "obj_typ": "network", "obj_ip": "0.0.0.0/0", "control_id": current_import_id}
- config2import["network_objects"].append(any_nw_object)
-
- # normalize_zones(full_config, config2import, current_import_id)
- normalize_access_rules(full_config, config2import, current_import_id, mgm_details=mgm_details)
- # normalize_nat_rules(full_config, config2import, current_import_id, jwt=jwt)
-
- return 0
diff --git a/roles/importer/files/importer/nsx4ff/nsx_application.py b/roles/importer/files/importer/nsx4ff/nsx_application.py
deleted file mode 100644
index 6d132cbe82..0000000000
--- a/roles/importer/files/importer/nsx4ff/nsx_application.py
+++ /dev/null
@@ -1,37 +0,0 @@
-from fwo_const import list_delimiter
-from fwo_log import getFwoLogger
-
-
-# def normalize_application_objects(full_config, config2import, import_id):
-# app_objects = []
-# for app_orig in full_config["/Objects/Applications"]:
-# app_objects.append(parse_app(app_orig, import_id,config2import))
-# config2import['service_objects'] += app_objects
-
-
-def extract_base_app_infos(app_orig, import_id):
- app = {}
- if "@name" in app_orig:
- app["svc_uid"] = app_orig["@name"]
- app["svc_name"] = app_orig["@name"]
- if "comment" in app_orig:
- app["svc_comment"] = app_orig["comment"]
- app["control_id"] = import_id
- app["svc_typ"] = 'simple'
- return app
-
-
-def parse_app(app_orig, import_id,config2import):
- svc = extract_base_app_infos(app_orig, import_id)
- app_comment = ''
- if 'category' in app_orig:
- app_comment = "category: " + app_orig['category']
- if 'subcategory' in app_orig:
- app_comment += ", " + "subcategory: " + app_orig['subcategory']
- if 'technology' in app_orig:
- app_comment += ", " + "technology: " + app_orig['technology']
- if 'svc_comment' in svc:
- svc['svc_comment'] += "; " + app_comment
- else:
- svc['svc_comment'] = app_comment
- return svc
diff --git a/roles/importer/files/importer/nsx4ff/nsx_base.py b/roles/importer/files/importer/nsx4ff/nsx_base.py
deleted file mode 100644
index e4a69e545f..0000000000
--- a/roles/importer/files/importer/nsx4ff/nsx_base.py
+++ /dev/null
@@ -1,2 +0,0 @@
-
-api_version_str="9.1"
diff --git a/roles/importer/files/importer/nsx4ff/nsx_getter.py b/roles/importer/files/importer/nsx4ff/nsx_getter.py
deleted file mode 100644
index 155f85b293..0000000000
--- a/roles/importer/files/importer/nsx4ff/nsx_getter.py
+++ /dev/null
@@ -1,95 +0,0 @@
-# library for API get functions
-import base64
-from typing import Dict
-from fwo_log import getFwoLogger
-import requests.packages
-import requests
-import xmltodict, json
-import fwo_globals
-from fwo_exception import FwLoginFailed
-
-
-def api_call(url, params = {}, headers = {}, data = {}, credentials = '', show_progress=False, method='get'):
- logger = getFwoLogger()
- result_type='json'
- request_headers = {'Content-Type': 'application/json'}
- for header_key in headers:
- request_headers[header_key] = headers[header_key]
- if credentials != '':
- request_headers["Authorization"] = 'Basic {credentials}'.format(credentials=credentials.decode("utf-8"))
- result_type='json'
-
- if method == "post":
- response = requests.post(url, params=params, data=data, headers=request_headers, verify=fwo_globals.verify_certs)
- elif method == "get":
- response = requests.get(url, params=params, headers=request_headers, verify=fwo_globals.verify_certs)
- else:
- raise Exception("unknown HTTP method found in nsx_getter")
-
- # error handling:
- exception_text = ''
- if response is None:
- if 'password' in json.dumps(data):
- exception_text = "error while sending api_call containing credential information to url '" + \
- str(url)
- else:
- exception_text = "error while sending api_call to url '" + str(url) + "' with payload '" + json.dumps(
- data, indent=2) + "' and headers: '" + json.dumps(request_headers, indent=2)
- if not response.ok:
- exception_text = 'error code: {error_code}, error={error}'.format(error_code=response.status_code, error=response.content)
- #logger.error(response.content)
- if (len(response.content) == 0):
- exception_text = 'empty response content'
-
- if exception_text != '':
- raise Exception(exception_text)
-
- # no errors found
- if result_type=='xml':
- r = xmltodict.parse(response.content)
- body_json = json.loads(json.dumps(r))
- elif result_type=='json':
- body_json = json.loads(response.content)
- if 'result' in body_json:
- body_json = body_json['result']
-
- else:
- body_json = None
-
- return body_json
-
-
-# def login(apiuser, apipwd, apihost):
-# base_url = "https://{apihost}/api/?type=keygen&user={apiuser}&password={apipwd}".format(apihost=apihost, apiuser=apiuser, apipwd=apipwd)
-# try:
-# body = api_call(base_url, method="get", headers={}, data={})
-# except Exception as e:
-# raise FwLoginFailed("Palo FW login to firewall=" + str(apihost) + " failed; Message: " + str(e)) from None
-
-# if 'response' in body and 'result' in body['response'] and 'key' in body['response']['result'] and not body['response']['result']['key'] == None:
-# key = body['response']['result']['key']
-# else:
-# raise FwLoginFailed("Palo FW login to firewall=" + str(apihost) + " failed") from None
-
-# if fwo_globals.debug_level > 2:
-# logger = getFwoLogger()
-# logger.debug("Login successful. Received key: " + key)
-
-# return key
-
-
-def update_config_with_nsxdcfw_api_call(api_base_url, config, api_path, credentials='', obj_type='generic', parameters={}, payload={}, show_progress=False, limit: int=1000, method="get"):
- returned_new_data = True
-
- full_result = []
- result = api_call(api_base_url, credentials=credentials, params=parameters, data=payload, show_progress=show_progress, method=method)
- # if "entry" in result:
- # returned_new_data = len(result['entry'])>0
- # else:
- # returned_new_data = False
- if returned_new_data:
- if 'results' in result:
- config.update({obj_type: result['results']})
- else:
- # full_result.extend(result)
- config.update({obj_type: result})
diff --git a/roles/importer/files/importer/nsx4ff/nsx_network.py b/roles/importer/files/importer/nsx4ff/nsx_network.py
deleted file mode 100644
index 894406e8b6..0000000000
--- a/roles/importer/files/importer/nsx4ff/nsx_network.py
+++ /dev/null
@@ -1,197 +0,0 @@
-from asyncio.log import logger
-from fwo_log import getFwoLogger
-from fwo_const import list_delimiter
-import ipaddress
-import os.path
-
-
-def normalize_nwobjects(full_config, config2import, import_id, jwt=None, mgm_id=None, domain="default"):
- logger = getFwoLogger()
- nw_objects = []
- nw_tagged_groups = {}
- # for obj_orig in full_config["/Objects/Addresses"]:
- # nw_objects.append(parse_object(obj_orig, import_id, config2import, nw_objects))
- # if 'tag' in obj_orig and 'member' in obj_orig['tag']:
- # logger.info("found simple network object with tags: " + obj_orig['@name'])
- # for t in obj_orig['tag']['member']:
- # collect_tag_information(nw_tagged_groups, "#"+t, obj_orig['@name'])
-
- # for tag in nw_tagged_groups:
- # logger.info("handling nw_tagged_group: " + tag + " with members: " + list_delimiter.join(nw_tagged_groups[tag]))
- # obj = {}
- # obj["obj_name"] = tag
- # obj["obj_uid"] = tag
- # obj["obj_comment"] = 'dynamic group defined by tagging'
- # obj['control_id'] = import_id
- # obj['obj_typ'] = 'group'
- # members = nw_tagged_groups[tag] # parse_dynamic_object_group(obj_grp_orig, nw_tagged_groups)
- # obj['obj_members'] = list_delimiter.join(members)
- # obj['obj_member_refs'] = list_delimiter.join(members)
- # nw_objects.append(obj)
-
- for obj_grp_orig in full_config["/infra/domains/{domain}/groups".format(domain=domain)]:
- # logger.info("found network group: " + obj_grp_orig['name'])
- obj_grp = extract_base_object_infos(obj_grp_orig, import_id, config2import, nw_objects)
-
- if 'resource_type' in obj_grp_orig:
- obj_grp["obj_typ"] = obj_grp_orig["resource_type"].lower()
-
- if 'static' in obj_grp_orig and 'filter' in obj_grp_orig['static']:
- obj_grp["obj_member_refs"], obj_grp["obj_member_names"] = parse_static_obj_group(obj_grp_orig, import_id, nw_objects, config2import)
- if 'dynamic' in obj_grp_orig and 'filter' in obj_grp_orig['dynamic']:
- members = parse_dynamic_object_group(obj_grp_orig, nw_tagged_groups)
- obj_grp["obj_member_refs"] = list_delimiter.join(members)
- obj_grp["obj_member_names"] = list_delimiter.join(members)
- nw_objects.append(obj_grp)
- if 'tag' in obj_grp_orig and 'member' in obj_grp_orig['tag']:
- logger.info("found network group with tags: " + obj_grp_orig['@name'])
- for t in obj_grp_orig['tag']['member']:
- logger.info(" found tag " + t)
- collect_tag_information(nw_tagged_groups, t, obj_grp_orig['@name'])
-
- config2import['network_objects'] = nw_objects
-
-
-def parse_object(obj_orig, import_id, config2import, nw_objects):
- obj = extract_base_object_infos(obj_orig, import_id, config2import, nw_objects)
- obj['obj_ip'] = obj_orig['ip-netmask']
- if '/' in obj['obj_ip'] and not '/32' in obj['obj_ip']:
- obj['obj_typ'] = 'network'
- else:
- obj['obj_typ'] = 'host'
- return obj
-
-
-def extract_base_object_infos(obj_orig, import_id, config2import, nw_objects):
- obj = {}
- if 'display_name' in obj_orig:
- obj["obj_name"] = obj_orig["display_name"]
- obj["obj_uid"] = obj_orig["path"]
- if 'description' in obj_orig:
- obj["obj_comment"] = obj_orig["description"]
- obj["control_id"] = import_id
- return obj
-
-
-def parse_dynamic_object_group(orig_grp, nw_tagged_groups):
- if "dynamic" in orig_grp:
- if 'filter' in orig_grp['dynamic']:
- if ' ' not in orig_grp['dynamic']['filter']:
- # just a single tag
- # add all nw objects with the tag to this group
- tag = orig_grp['dynamic']['filter'][1:-1]
- if tag in nw_tagged_groups:
- return nw_tagged_groups[tag]
- else:
- # later: deal with more complex tagging (and/or)
- return []
- return []
-
-
-def parse_static_obj_group(orig_grp, import_id, nw_objects, config2import, id = None):
- refs = []
- names = []
-
- if "static" in orig_grp and "member" in orig_grp["static"]:
- for m in orig_grp['static']['member']:
- names.append(m)
- refs.append(m)
- return list_delimiter.join(refs), list_delimiter.join(names)
-
-
-def parse_obj_list(nw_obj_list, import_id, obj_list, id, type='network'):
- refs = []
- names = []
- for obj_uid in nw_obj_list:
- refs.append(obj_uid)
- names.append(lookup_obj_uid(obj_uid, obj_list, import_id, type=type))
- return list_delimiter.join(refs), list_delimiter.join(names)
-
-
-
-def lookup_obj_uid(obj_uid, obj_list, import_id, type='network'):
- for o in obj_list:
- if type=='network' and 'obj_uid' in o:
- if o['obj_uid']==obj_uid:
- return o['obj_name']
- elif type=='service' and 'svc_name' in o:
- if o['svc_uid']==obj_uid:
- return o['svc_name']
- else:
- logger.warning("could not find object uid in object " + str(o))
-
- # could not find existing obj in obj list, so creating new one
- if type=='network':
- refs, names = add_ip_obj([obj_uid], obj_list, import_id)
- return refs ## assuming only one object here
- elif type=='service':
- logger.warning("could not find service object " + str(obj_uid))
- else:
- logger.warning("unknown object type '" + type + "' for object " + str(obj_uid))
- return None
-
-
-def lookup_obj_name(obj_name, obj_list, import_id, type='network'):
- for o in obj_list:
- if type=='network' and 'obj_name' in o:
- if o['obj_name']==obj_name:
- return o['obj_name']
- elif type=='service' and 'svc_name' in o:
- if o['svc_uid']==obj_name:
- return o['svc_name']
- else:
- logger.warning("could not find object name in object " + str(o))
-
- # could not find existing obj in obj list, so creating new one
- if type=='network':
- refs, names = add_ip_obj([obj_name], obj_list, import_id)
- return refs ## assuming only one object here
- elif type=='service':
- logger.warning("could not find service object " + str(obj_name))
- else:
- logger.warning("unknown object type '" + type + "' for object " + str(obj_name))
- return None
-
-
-def add_ip_obj(ip_list, obj_list, import_id):
- refs = []
- names = []
- for ip in ip_list:
- # TODO: lookup ip in network_objects and re-use
- ip_obj = {}
- ip_obj['obj_name'] = ip
- ip_obj['obj_uid'] = ip_obj['obj_name']
- try:
- ipaddress.ip_network(ip)
- # valid ip
- ip_obj['obj_ip'] = ip
- except:
- # no valid ip - asusming Tag
- ip_obj['obj_ip'] = '0.0.0.0/0'
- ip = '0.0.0.0/0'
- ip_obj['obj_name'] = ip_obj['obj_name']
- ip_obj['obj_uid'] = ip_obj['obj_name']
- ip_obj['obj_type'] = 'simple'
- ip_obj['obj_typ'] = 'host'
- if "/" in ip:
- ip_obj['obj_typ'] = 'network'
-
- if "-" in ip: # ip range
- ip_obj['obj_typ'] = 'ip_range'
- ip_range = ip.split("-")
- ip_obj['obj_ip'] = ip_range[0]
- ip_obj['obj_ip_end'] = ip_range[1]
-
- ip_obj['control_id'] = import_id
-
- obj_list.append(ip_obj)
- refs.append(ip_obj['obj_uid'])
- names.append(ip_obj['obj_name'])
- return list_delimiter.join(refs), list_delimiter.join(names)
-
-
-def collect_tag_information(tagged_groups, tag, obj_name):
- if tag in tagged_groups.keys():
- tagged_groups[tag].append(obj_name)
- else:
- tagged_groups.update({tag: [obj_name]})
diff --git a/roles/importer/files/importer/nsx4ff/nsx_rule.py b/roles/importer/files/importer/nsx4ff/nsx_rule.py
deleted file mode 100644
index cdbc0e4292..0000000000
--- a/roles/importer/files/importer/nsx4ff/nsx_rule.py
+++ /dev/null
@@ -1,109 +0,0 @@
-from nsx_service import parse_svc_list
-from nsx_network import parse_obj_list
-from fwo_log import getFwoLogger
-from fwo_const import list_delimiter
-import hashlib
-import base64
-import os.path
-
-
-def make_hash_sha256(o):
- hasher = hashlib.sha256()
- hasher.update(repr(make_hashable(o)).encode())
- return base64.b64encode(hasher.digest()).decode()
-
-
-def make_hashable(o):
- if isinstance(o, (tuple, list)):
- return tuple((make_hashable(e) for e in o))
-
- if isinstance(o, dict):
- return tuple(sorted((k,make_hashable(v)) for k,v in o.items()))
-
- if isinstance(o, (set, frozenset)):
- return tuple(sorted(make_hashable(e) for e in o))
-
- return o
-
-
-def normalize_access_rules(full_config, config2import, import_id, mgm_details={}):
- rules = []
- logger = getFwoLogger()
-
- nw_obj_names = []
- for o in config2import['network_objects']:
- nw_obj_names.append(o["obj_name"])
-
- for device in full_config["devices"]:
- rule_number = 0
- for dev_id in full_config['devices'].keys():
- for rulebase in list(full_config['devices'][dev_id].keys()):
- for rule_orig in full_config['devices'][dev_id][rulebase]['rules']:
-
- # set some default values first
- rule = {'rule_src': 'any', 'rule_dst': 'any', 'rule_svc': 'any',
- 'rule_src_refs': 'any_obj_placeholder', 'rule_dst_refs': 'any_obj_placeholder',
- 'rule_src_neg': False, 'rule_dst_neg': False,
- 'rule_svc_refs': 'any_svc_placeholder'}
-
- if 'sources_excluded' in rule_orig and rule_orig['sources_excluded']:
- rule["rule_src_neg"] = True
- if 'destinations_excluded' in rule_orig and rule_orig['destinations_excluded']:
- rule["rule_dst_neg"] = True
- rule.update({
- "rule_svc_neg": False, # not possible to negate the svc field on NSX
- "rulebase_name": os.path.basename(rule_orig['parent_path']),
- "rule_name": rule_orig['relative_path'],
- 'rule_type': 'access',
- 'rule_num': rule_number,
- 'parent_rule_id': None,
- 'rule_time': None,
- 'rule_implied': False,
- 'rule_comment': None,
- 'rule_track': 'None',
- 'rule_uid': rule_orig['unique_id'],
- 'rule_disabled': rule_orig['disabled'],
- 'control_id': import_id
- })
-
- if "action" in rule_orig:
- if rule_orig['action']=='ALLOW':
- rule['rule_action'] = 'accept'
- elif rule_orig['action']=='drop':
- rule['rule_action'] = 'drop'
- elif rule_orig['action']=='deny':
- rule['rule_action'] = 'deny'
- elif rule_orig['action']=='REJECT':
- rule['rule_action'] = 'reject'
- else:
- logger.warning("found undefined action:" + str(rule_orig))
- else: # NAT rules
- rule['rule_action'] = "accept"
- rule['rule_type'] = 'nat'
-
- if 'logged' in rule_orig and rule_orig['logged']:
- rule['rule_track'] = 'log'
- else:
- rule['rule_track'] = 'none'
-
- if "source_groups" in rule_orig:
- rule['rule_src_refs'], rule["rule_src"] = parse_obj_list(rule_orig["source_groups"], import_id, config2import['network_objects'], rule["rule_uid"])
- else:
- logger.warning("found undefined source in rule: " + str(rule_orig))
-
- if "destination_groups" in rule_orig:
- rule['rule_dst_refs'], rule["rule_dst"] = parse_obj_list(rule_orig["destination_groups"], import_id, config2import['network_objects'], rule["rule_uid"])
- else:
- logger.warning("found undefined destination in rule: " + str(rule_orig))
-
- services = []
- if "services" in rule_orig:
- services = rule_orig["services"]
-
- if services != [ "ANY" ]:
- rule['rule_svc_refs'], rule["rule_svc"] = parse_svc_list(services, import_id, config2import['service_objects'], rule["rule_uid"], type='service')
-
- rule_number += 1
- rules.append(rule)
-
- config2import['rules'] += rules
diff --git a/roles/importer/files/importer/nsx4ff/nsx_service.py b/roles/importer/files/importer/nsx4ff/nsx_service.py
deleted file mode 100644
index d1b5af697d..0000000000
--- a/roles/importer/files/importer/nsx4ff/nsx_service.py
+++ /dev/null
@@ -1,162 +0,0 @@
-from fwo_const import list_delimiter
-from fwo_log import getFwoLogger
-import os.path
-
-
-def normalize_svcobjects(full_config, config2import, import_id):
- svc_objects = []
- for svc_orig in full_config['/infra/services']:
- svc_objects.append(parse_svc(svc_orig, import_id,config2import))
- # for svc_grp_orig in full_config['/Objects/ServiceGroups']:
- # svc_grp = extract_base_svc_infos(svc_grp_orig, import_id)
- # svc_grp['svc_typ'] = 'group'
- # svc_grp['svc_member_refs'] , svc_grp['svc_member_names'] = parse_svc_group(svc_grp_orig,config2import)
- # svc_objects.append(svc_grp)
- config2import['service_objects'] += svc_objects
-
-
-def parse_svc_group(orig_grp,config2import):
- refs = []
- names = []
- if 'dynamic' in orig_grp:
- pass
- if 'static' in orig_grp and 'member' in orig_grp['static']:
- for m in orig_grp['static']['member']:
- names.append(m)
- refs.append(m)
- return list_delimiter.join(refs), list_delimiter.join(names)
-
-
-def extract_base_svc_infos(svc_orig, import_id):
- svc = {}
- if 'display_name' in svc_orig:
- svc['svc_name'] = svc_orig['display_name']
- if 'path' in svc_orig:
- svc['svc_uid'] = svc_orig['path']
- if 'description' in svc_orig:
- svc['svc_comment'] = svc_orig['description']
- svc['svc_timeout'] = None
- svc['svc_color'] = None
- svc['control_id'] = import_id
- svc['svc_typ'] = 'simple'
- return svc
-
-
-def parse_svc(svc_orig, import_id,config2import):
- svc = extract_base_svc_infos(svc_orig, import_id)
- if 'service_entries' in svc_orig:
- for se in svc_orig['service_entries']: # TODO: handle list of service entries
- if 'l4_protocol' in se:
- proto_string = 'undefined'
- if se['l4_protocol'] == 'TCP':
- svc['ip_proto'] = 6
- proto_string = 'tcp'
- if se['l4_protocol'] == 'UDP':
- svc['ip_proto'] = 17
- proto_string = 'udp'
-
- if 'destination_ports' in se and len(se['destination_ports'])>0:
- svc['svc_port'] = se['destination_ports'][0] # TODO: handle list of ports!
- extract_port_for_service(svc['svc_port'], svc)
- else:
- pass
-
- if proto_string=='undefined':
- svc['svc_name'] += ' [Protocol \'' + str(se['l4_protocol']) + '\' not supported]'
- # else:
- # port_string = svc_orig['protocol'][proto_string]['port']
- # if ',' in port_string:
- # svc['svc_typ'] = 'group'
- # svc['svc_port'] = None
- # members = []
- # for p in port_string.split(','):
- # hlp_svc = create_helper_service(p, proto_string, svc['svc_name'], import_id)
- # add_service(hlp_svc, config2import)
- # members.append(hlp_svc['svc_uid'])
- # svc['svc_members'] = list_delimiter.join(members)
- # svc['svc_member_refs'] = list_delimiter.join(members)
- # else: # just a single port (range)
- # extract_port_for_service(port_string, svc)
- return svc
-
-
-# def add_service(svc, config2import):
-# #if svc not in config2import['service_objects']:
-# config2import['service_objects'].append(svc)
-
-
-def extract_port_for_service(port_string, svc):
- if '-' in port_string:
- port_range = port_string.split('-')
- if len(port_range)==2:
- svc['svc_port'] = port_range[0]
- svc['svc_port_end'] = port_range[1]
- else:
- logger = getFwoLogger()
- logger.warning('found strange port range with more than one hyphen: ' + str(port_string))
- else:
- svc['svc_port'] = port_string
-
-
-def create_helper_service(ports, proto_string, parent_svc_name, import_id):
- svc = {
- 'svc_name': parent_svc_name + '_' + proto_string + '_' + ports,
- 'svc_uid': parent_svc_name + '_' + proto_string + '_' + ports,
- 'svc_comment': 'helper service for NSX multiple port range object: ' + parent_svc_name,
- 'control_id': import_id,
- 'svc_typ': 'simple'
- }
-
- extract_port_for_service(ports, svc)
- return svc
-
-
-def parse_svc_list(svc_list, import_id, obj_list, id, type='network'):
- refs = []
- names = []
- for obj_name in svc_list:
- obj_name_base = os.path.basename(obj_name)
- names.append(obj_name_base)
- refs.append(obj_name)
- #refs.append(lookup_svc_obj_uid(obj_name_base, obj_list, import_id, type=type))
- return list_delimiter.join(refs), list_delimiter.join(names)
-
-
-def lookup_svc_obj_name(obj_name, obj_list, import_id, type='network'):
- logger = getFwoLogger()
- for o in obj_list:
- if type=='service' and 'svc_name' in o:
- if o['svc_name']==obj_name:
- return o['svc_uid']
- else:
- logger.warning('could not find object name in object ' + str(o))
-
- # could not find existing obj in obj list, so creating new one
- return add_svc_obj(obj_name, obj_list, import_id)
-
-
-def lookup_svc_obj_uid(obj_name, obj_list, import_id, type='network'):
- logger = getFwoLogger()
- for o in obj_list:
- if type=='service' and 'svc_name' in o:
- if o['svc_name']==obj_name:
- return o['svc_uid']
- else:
- logger.warning('could not find object name in object ' + str(o))
-
- # could not find existing obj in obj list, so creating new one
- return add_svc_obj(obj_name, obj_list, import_id)
-
-
-def add_svc_obj(svc_in, svc_list, import_id):
- svc_obj = {}
- svc_obj['svc_name'] = os.path.basename(svc_in)
- svc_obj['svc_uid'] = svc_in
- svc_obj['control_id'] = import_id
- svc_obj['svc_typ'] = 'simple'
-
- if svc_obj not in svc_list:
- # svc_list.append(svc_obj)
- logger = getFwoLogger()
- logger.warning('found undefined service: ' + str(svc_obj))
- return svc_obj['svc_name']
diff --git a/roles/importer/files/importer/nsx4ff/nsx_zone.py b/roles/importer/files/importer/nsx4ff/nsx_zone.py
deleted file mode 100644
index a55f86dc95..0000000000
--- a/roles/importer/files/importer/nsx4ff/nsx_zone.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from asyncio.log import logger
-from fwo_log import getFwoLogger
-from fwo_const import list_delimiter
-
-
-def normalize_zones(full_config, config2import, import_id):
- zones = []
- for zone_orig in full_config["/Network/Zones"]:
- zones.append({
- "zone_name": zone_orig["@name"],
- "zone_uid": zone_orig["@name"],
- "control_id": import_id
- })
-
- config2import['zone_objects'] = zones
diff --git a/roles/importer/files/importer/paloaltomanagement2023ff/discovery_logging.conf b/roles/importer/files/importer/paloaltomanagement2023ff/discovery_logging.conf
deleted file mode 100644
index 139c55a9cb..0000000000
--- a/roles/importer/files/importer/paloaltomanagement2023ff/discovery_logging.conf
+++ /dev/null
@@ -1,41 +0,0 @@
-[loggers]
-keys=root,discoveryDebugLogger
-#keys=root,__main__
-
-[handlers]
-keys=consoleHandler,debugFileHandler
-
-[formatters]
-keys=defaultFormatter,debugFileFormatter
-
-[logger_root]
-level=DEBUG
-handlers=consoleHandler
-
-[logger_discoveryDebugLogger]
-#[logger___main__]
-level=DEBUG
-handlers=debugFileHandler
-qualname=discoveryDebugLogger
-#qualname=__main__
-propagate=0
-
-[handler_consoleHandler]
-class=StreamHandler
-level=DEBUG
-formatter=defaultFormatter
-args=(sys.stderr,)
-
-[handler_debugFileHandler]
-class=FileHandler
-level=DEBUG
-formatter=debugFileFormatter
-args=('/tmp/fworch_discovery.log',)
-# args=('/var/log/fworch/discovery.log',)
-
-[formatter_defaultFormatter]
-format=%(levelname)s:%(name)s:%(message)s
-
-[formatter_debugFileFormatter]
-format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
-
diff --git a/roles/importer/files/importer/paloaltomanagement2023ff/fwcommon.py b/roles/importer/files/importer/paloaltomanagement2023ff/fwcommon.py
deleted file mode 100644
index 87de874495..0000000000
--- a/roles/importer/files/importer/paloaltomanagement2023ff/fwcommon.py
+++ /dev/null
@@ -1,101 +0,0 @@
-import sys
-from common import importer_base_dir
-sys.path.append(importer_base_dir + "/paloaltomanagement2023ff")
-from palo_service import normalize_svcobjects
-from palo_application import normalize_application_objects
-from palo_rule import normalize_access_rules
-from palo_network import normalize_nwobjects
-from palo_zone import normalize_zones
-from palo_getter import login, update_config_with_palofw_api_call
-from fwo_log import getFwoLogger
-from palo_base import api_version_str
-
-def has_config_changed(full_config, mgm_details, force=False):
- # dummy - may be filled with real check later on
- return True
-
-
-def get_config(config2import, full_config, current_import_id, mgm_details, limit=1000, force=False, jwt=''):
- logger = getFwoLogger()
- if full_config == {}: # no native config was passed in, so getting it from Azzure
- parsing_config_only = False
- else:
- parsing_config_only = True
-
- if not parsing_config_only: # no native config was passed in, so getting it from Palo Firewall
- apipwd = mgm_details["import_credential"]['secret']
- apiuser = mgm_details["import_credential"]['user']
- apihost = mgm_details["hostname"]
-
- vsys_objects = ["/Network/Zones", "/Objects/Addresses", "/Objects/Services", "/Objects/AddressGroups", "/Objects/ServiceGroups", "/Objects/Tags"]
- predef_objects = ["/Objects/Applications"]
- rulebase_names = ["/Policies/SecurityRules", "/Policies/NATRules"]
-
- for obj_path in vsys_objects:
- full_config[obj_path] = []
-
- for obj_path in predef_objects:
- full_config[obj_path] = []
-
- # login
- key = login(apiuser, apipwd, apihost)
- if key == None or key == "":
- logger.error('Did not succeed in logging in to Palo API, no key returned.')
- return 1
-
- ## get objects:
- base_url = "https://{apihost}/restapi/v{api_version_str}".format(apihost=apihost, api_version_str=api_version_str)
-
- vsys_name = "vsys1" # TODO - automate this hard-coded name
- location = "vsys" # alternative: panorama-pushed
-
- for obj_path in vsys_objects:
- update_config_with_palofw_api_call(key, base_url, full_config, obj_path + "?location={location}&vsys={vsys_name}".format(location=location, vsys_name=vsys_name), obj_type=obj_path)
-
- for obj_path in predef_objects:
- update_config_with_palofw_api_call(key, base_url, full_config, obj_path + "?location={location}".format(location="predefined"), obj_type=obj_path)
-
- # users
-
- # get rules
- full_config.update({'devices': {}})
- for device in mgm_details["devices"]:
- dev_id = device['id']
- dev_name = device['local_rulebase_name']
- full_config['devices'].update({ dev_id: {} })
-
- for obj_path in rulebase_names:
- update_config_with_palofw_api_call(
- key, base_url, full_config['devices'][device['id']],
- obj_path + "?location={location}&vsys={vsys_name}".format(location="vsys", vsys_name=dev_name),
- obj_type=obj_path)
-
- ##################
- # now we normalize relevant parts of the raw config and write the results to config2import dict
-
- normalize_nwobjects(full_config, config2import, current_import_id, jwt=jwt, mgm_id=mgm_details['id'])
- normalize_svcobjects(full_config, config2import, current_import_id)
- normalize_application_objects(full_config, config2import, current_import_id)
- # normalize_users(full_config, config2import, current_import_id, user_scope)
-
- # adding default any and predefined objects
- any_nw_svc = {"svc_uid": "any_svc_placeholder", "svc_name": "any", "svc_comment": "Placeholder service.",
- "svc_typ": "simple", "ip_proto": -1, "svc_port": 0, "svc_port_end": 65535, "control_id": current_import_id}
- http_svc = {"svc_uid": "http_predefined_svc", "svc_name": "service-http", "svc_comment": "Predefined service",
- "svc_typ": "simple", "ip_proto": 6, "svc_port": 80, "control_id": current_import_id}
- https_svc = {"svc_uid": "https_predefined_svc", "svc_name": "service-https", "svc_comment": "Predefined service",
- "svc_typ": "simple", "ip_proto": 6, "svc_port": 443, "control_id": current_import_id}
-
- config2import["service_objects"].append(any_nw_svc)
- config2import["service_objects"].append(http_svc)
- config2import["service_objects"].append(https_svc)
-
- any_nw_object = {"obj_uid": "any_obj_placeholder", "obj_name": "any", "obj_comment": "Placeholder object.",
- "obj_typ": "network", "obj_ip": "0.0.0.0/0", "control_id": current_import_id}
- config2import["network_objects"].append(any_nw_object)
-
- normalize_zones(full_config, config2import, current_import_id)
- normalize_access_rules(full_config, config2import, current_import_id, mgm_details=mgm_details)
- # normalize_nat_rules(full_config, config2import, current_import_id, jwt=jwt)
-
- return 0
diff --git a/roles/importer/files/importer/paloaltomanagement2023ff/palo_application.py b/roles/importer/files/importer/paloaltomanagement2023ff/palo_application.py
deleted file mode 100644
index 4652fa0c6a..0000000000
--- a/roles/importer/files/importer/paloaltomanagement2023ff/palo_application.py
+++ /dev/null
@@ -1,37 +0,0 @@
-from fwo_const import list_delimiter
-from fwo_log import getFwoLogger
-
-
-def normalize_application_objects(full_config, config2import, import_id):
- app_objects = []
- for app_orig in full_config["/Objects/Applications"]:
- app_objects.append(parse_app(app_orig, import_id,config2import))
- config2import['service_objects'] += app_objects
-
-
-def extract_base_app_infos(app_orig, import_id):
- app = {}
- if "@name" in app_orig:
- app["svc_uid"] = app_orig["@name"]
- app["svc_name"] = app_orig["@name"]
- if "comment" in app_orig:
- app["svc_comment"] = app_orig["comment"]
- app["control_id"] = import_id
- app["svc_typ"] = 'simple'
- return app
-
-
-def parse_app(app_orig, import_id,config2import):
- svc = extract_base_app_infos(app_orig, import_id)
- app_comment = ''
- if 'category' in app_orig:
- app_comment = "category: " + app_orig['category']
- if 'subcategory' in app_orig:
- app_comment += ", " + "subcategory: " + app_orig['subcategory']
- if 'technology' in app_orig:
- app_comment += ", " + "technology: " + app_orig['technology']
- if 'svc_comment' in svc:
- svc['svc_comment'] += "; " + app_comment
- else:
- svc['svc_comment'] = app_comment
- return svc
diff --git a/roles/importer/files/importer/paloaltomanagement2023ff/palo_base.py b/roles/importer/files/importer/paloaltomanagement2023ff/palo_base.py
deleted file mode 100644
index e4a69e545f..0000000000
--- a/roles/importer/files/importer/paloaltomanagement2023ff/palo_base.py
+++ /dev/null
@@ -1,2 +0,0 @@
-
-api_version_str="9.1"
diff --git a/roles/importer/files/importer/paloaltomanagement2023ff/palo_getter.py b/roles/importer/files/importer/paloaltomanagement2023ff/palo_getter.py
deleted file mode 100644
index 4e926c638b..0000000000
--- a/roles/importer/files/importer/paloaltomanagement2023ff/palo_getter.py
+++ /dev/null
@@ -1,100 +0,0 @@
-# library for API get functions
-import base64
-from typing import Dict
-from fwo_log import getFwoLogger
-import requests.packages
-import requests
-import xmltodict, json
-import fwo_globals
-from fwo_exception import FwLoginFailed
-
-
-def api_call(url, params = {}, headers = {}, data = {}, key = '', show_progress=False, method='get'):
- logger = getFwoLogger()
- result_type='xml'
- request_headers = {'Content-Type': 'application/json'}
- for header_key in headers:
- request_headers[header_key] = headers[header_key]
- if key != '':
- request_headers["X-PAN-KEY"] = '{key}'.format(key=key)
- result_type='json'
-
- if method == "post":
- response = requests.post(url, params=params, data=data, headers=request_headers, verify=fwo_globals.verify_certs)
- elif method == "get":
- response = requests.get(url, params=params, headers=request_headers, verify=fwo_globals.verify_certs)
- else:
- raise Exception("unknown HTTP method found in palo_getter")
-
- # error handling:
- exception_text = ''
- if response is None:
- if 'password' in json.dumps(data):
- exception_text = "error while sending api_call containing credential information to url '" + \
- str(url)
- else:
- exception_text = "error while sending api_call to url '" + str(url) + "' with payload '" + json.dumps(
- data, indent=2) + "' and headers: '" + json.dumps(request_headers, indent=2)
- if not response.ok:
- exception_text = 'error code: {error_code}, error={error}'.format(error_code=response.status_code, error=response.content)
- #logger.error(response.content)
- if (len(response.content) == 0):
- exception_text = 'empty response content'
-
- if exception_text != '':
- raise Exception(exception_text)
-
- # no errors found
- if result_type=='xml':
- r = xmltodict.parse(response.content)
- body_json = json.loads(json.dumps(r))
- elif result_type=='json':
- body_json = json.loads(response.content)
- if 'result' in body_json:
- body_json = body_json['result']
-
- else:
- body_json = None
-
- # if fwo_globals.debug_level > 5:
- # if 'pass' in json.dumps(data):
- # logger.debug("api_call containing credential information to url '" +
- # str(url) + " - not logging query")
- # else:
- # logger.debug("api_call to url '" + str(url) + "' with payload '" + json.dumps(
- # data, indent=2) + "' and headers: '" + json.dumps(request_headers, indent=2))
-
- return body_json
-
-
-def login(apiuser, apipwd, apihost):
- base_url = "https://{apihost}/api/?type=keygen&user={apiuser}&password={apipwd}".format(apihost=apihost, apiuser=apiuser, apipwd=apipwd)
- try:
- body = api_call(base_url, method="get", headers={}, data={})
- except Exception as e:
- raise FwLoginFailed("Palo FW login to firewall=" + str(apihost) + " failed; Message: " + str(e)) from None
-
- if 'response' in body and 'result' in body['response'] and 'key' in body['response']['result'] and not body['response']['result']['key'] == None:
- key = body['response']['result']['key']
- else:
- raise FwLoginFailed("Palo FW login to firewall=" + str(apihost) + " failed") from None
-
- if fwo_globals.debug_level > 2:
- logger = getFwoLogger()
- logger.debug("Login successful. Received key: " + key)
-
- return key
-
-
-def update_config_with_palofw_api_call(key, api_base_url, config, api_path, obj_type='generic', parameters={}, payload={}, show_progress=False, limit: int=1000, method="get"):
- returned_new_data = True
-
- full_result = []
- result = api_call(api_base_url + api_path,key=key, params=parameters, data=payload, show_progress=show_progress, method=method)
- if "entry" in result:
- returned_new_data = len(result['entry'])>0
- else:
- returned_new_data = False
- if returned_new_data:
- full_result.extend(result["entry"])
- config.update({obj_type: full_result})
diff --git a/roles/importer/files/importer/paloaltomanagement2023ff/palo_network.py b/roles/importer/files/importer/paloaltomanagement2023ff/palo_network.py
deleted file mode 100644
index 252ba38cf2..0000000000
--- a/roles/importer/files/importer/paloaltomanagement2023ff/palo_network.py
+++ /dev/null
@@ -1,175 +0,0 @@
-from asyncio.log import logger
-from fwo_log import getFwoLogger
-from fwo_const import list_delimiter
-import ipaddress
-
-
-def normalize_nwobjects(full_config, config2import, import_id, jwt=None, mgm_id=None):
- logger = getFwoLogger()
- nw_objects = []
- nw_tagged_groups = {}
- for obj_orig in full_config["/Objects/Addresses"]:
- nw_objects.append(parse_object(obj_orig, import_id, config2import, nw_objects))
- if 'tag' in obj_orig and 'member' in obj_orig['tag']:
- logger.info("found simple network object with tags: " + obj_orig['@name'])
- for t in obj_orig['tag']['member']:
- collect_tag_information(nw_tagged_groups, "#"+t, obj_orig['@name'])
-
- for tag in nw_tagged_groups:
- logger.info("handling nw_tagged_group: " + tag + " with members: " + list_delimiter.join(nw_tagged_groups[tag]))
- obj = {}
- obj["obj_name"] = tag
- obj["obj_uid"] = tag
- obj["obj_comment"] = 'dynamic group defined by tagging'
- obj['control_id'] = import_id
- obj['obj_typ'] = 'group'
- members = nw_tagged_groups[tag] # parse_dynamic_object_group(obj_grp_orig, nw_tagged_groups)
- obj['obj_members'] = list_delimiter.join(members)
- obj['obj_member_refs'] = list_delimiter.join(members)
- nw_objects.append(obj)
-
- for obj_grp_orig in full_config["/Objects/AddressGroups"]:
- logger.info("found network group: " + obj_grp_orig['@name'])
- obj_grp = extract_base_object_infos(obj_grp_orig, import_id, config2import, nw_objects)
- obj_grp["obj_typ"] = "group"
- if 'static' in obj_grp_orig and 'filter' in obj_grp_orig['static']:
- obj_grp["obj_member_refs"], obj_grp["obj_member_names"] = parse_static_obj_group(obj_grp_orig, import_id, nw_objects, config2import)
- if 'dynamic' in obj_grp_orig and 'filter' in obj_grp_orig['dynamic']:
- members = parse_dynamic_object_group(obj_grp_orig, nw_tagged_groups)
- obj_grp["obj_member_refs"] = list_delimiter.join(members)
- obj_grp["obj_member_names"] = list_delimiter.join(members)
- nw_objects.append(obj_grp)
- if 'tag' in obj_grp_orig and 'member' in obj_grp_orig['tag']:
- logger.info("found network group with tags: " + obj_grp_orig['@name'])
- for t in obj_grp_orig['tag']['member']:
- logger.info(" found tag " + t)
- collect_tag_information(nw_tagged_groups, "#"+t, obj_grp_orig['@name'])
-
- config2import['network_objects'] = nw_objects
-
-
-def parse_object(obj_orig, import_id, config2import, nw_objects):
- obj = extract_base_object_infos(obj_orig, import_id, config2import, nw_objects)
- obj['obj_ip'] = obj_orig['ip-netmask']
- if '/' in obj['obj_ip'] and not '/32' in obj['obj_ip']:
- obj['obj_typ'] = 'network'
- else:
- obj['obj_typ'] = 'host'
- return obj
-
-
-def extract_base_object_infos(obj_orig, import_id, config2import, nw_objects):
- obj = {}
- obj["obj_name"] = obj_orig["@name"]
- obj["obj_uid"] = obj_orig["@name"]
- if 'description' in obj_orig:
- obj["obj_comment"] = obj_orig["description"]
- if 'tag' in obj_orig:
- tag_list = ",".join(obj_orig["tag"]['member'])
- if 'obj_comment' in obj:
- obj["obj_comment"] += ("; tags: " + tag_list)
- else:
- obj["obj_comment"] = tag_list
- obj['control_id'] = import_id
- return obj
-
-
-def parse_dynamic_object_group(orig_grp, nw_tagged_groups):
- if "dynamic" in orig_grp:
- if 'filter' in orig_grp['dynamic']:
- if ' ' not in orig_grp['dynamic']['filter']:
- # just a single tag
- # add all nw objects with the tag to this group
- tag = "#" + orig_grp['dynamic']['filter'][1:-1]
- if tag in nw_tagged_groups:
- return nw_tagged_groups[tag]
- else:
- # later: deal with more complex tagging (and/or)
- return []
- return []
-
-
-def parse_static_obj_group(orig_grp, import_id, nw_objects, config2import, id = None):
- refs = []
- names = []
-
- if "static" in orig_grp and "member" in orig_grp["static"]:
- for m in orig_grp['static']['member']:
- names.append(m)
- refs.append(m)
- return list_delimiter.join(refs), list_delimiter.join(names)
-
-
-def parse_obj_list(nw_obj_list, import_id, obj_list, id, type='network'):
- refs = []
- names = []
- for obj_name in nw_obj_list:
- names.append(obj_name)
- refs.append(lookup_obj_uid(obj_name, obj_list, import_id, type=type))
- return list_delimiter.join(refs), list_delimiter.join(names)
-
-
-def lookup_obj_uid(obj_name, obj_list, import_id, type='network'):
- for o in obj_list:
- if type=='network' and 'obj_name' in o:
- if o['obj_name']==obj_name:
- return o['obj_uid']
- elif type=='service' and 'svc_name' in o:
- if o['svc_name']==obj_name:
- return o['svc_uid']
- else:
- logger.warning("could not find object name in object " + str(o))
-
- # could not find existing obj in obj list, so creating new one
- if type=='network':
- refs, names = add_ip_obj([obj_name], obj_list, import_id)
- return refs ## assuming only one object here
- elif type=='service':
- logger.warning("could not find service object " + str(obj_name))
- else:
- logger.warning("unknown object type '" + type + "' for object " + str(obj_name))
- return None
-
-
-def add_ip_obj(ip_list, obj_list, import_id):
- refs = []
- names = []
- for ip in ip_list:
- # TODO: lookup ip in network_objects and re-use
- ip_obj = {}
- ip_obj['obj_name'] = ip
- ip_obj['obj_uid'] = ip_obj['obj_name']
- try:
- ipaddress.ip_network(ip)
- # valid ip
- ip_obj['obj_ip'] = ip
- except:
- # no valid ip - asusming Tag
- ip_obj['obj_ip'] = '0.0.0.0/0'
- ip = '0.0.0.0/0'
- ip_obj['obj_name'] = "#"+ip_obj['obj_name']
- ip_obj['obj_uid'] = ip_obj['obj_name']
- ip_obj['obj_type'] = 'simple'
- ip_obj['obj_typ'] = 'host'
- if "/" in ip:
- ip_obj['obj_typ'] = 'network'
-
- if "-" in ip: # ip range
- ip_obj['obj_typ'] = 'ip_range'
- ip_range = ip.split("-")
- ip_obj['obj_ip'] = ip_range[0]
- ip_obj['obj_ip_end'] = ip_range[1]
-
- ip_obj['control_id'] = import_id
-
- obj_list.append(ip_obj)
- refs.append(ip_obj['obj_uid'])
- names.append(ip_obj['obj_name'])
- return list_delimiter.join(refs), list_delimiter.join(names)
-
-
-def collect_tag_information(tagged_groups, tag, obj_name):
- if tag in tagged_groups.keys():
- tagged_groups[tag].append(obj_name)
- else:
- tagged_groups.update({tag: [obj_name]})
diff --git a/roles/importer/files/importer/paloaltomanagement2023ff/palo_rule.py b/roles/importer/files/importer/paloaltomanagement2023ff/palo_rule.py
deleted file mode 100644
index 02ffb86e22..0000000000
--- a/roles/importer/files/importer/paloaltomanagement2023ff/palo_rule.py
+++ /dev/null
@@ -1,146 +0,0 @@
-from palo_service import parse_svc_list
-from palo_network import parse_obj_list
-from fwo_log import getFwoLogger
-from fwo_const import list_delimiter
-import hashlib
-import base64
-
-
-def make_hash_sha256(o):
- hasher = hashlib.sha256()
- hasher.update(repr(make_hashable(o)).encode())
- return base64.b64encode(hasher.digest()).decode()
-
-
-def make_hashable(o):
- if isinstance(o, (tuple, list)):
- return tuple((make_hashable(e) for e in o))
-
- if isinstance(o, dict):
- return tuple(sorted((k,make_hashable(v)) for k,v in o.items()))
-
- if isinstance(o, (set, frozenset)):
- return tuple(sorted(make_hashable(e) for e in o))
-
- return o
-
-
-def normalize_access_rules(full_config, config2import, import_id, mgm_details={}):
- rules = []
- logger = getFwoLogger()
-
- nw_obj_names = []
- for o in config2import['network_objects']:
- nw_obj_names.append(o["obj_name"])
-
- for device in full_config["devices"]:
- rule_number = 0
- for dev_id in full_config['devices'].keys():
- for rulebase in list(full_config['devices'][dev_id].keys()):
- for rule_orig in full_config['devices'][dev_id][rulebase]:
- rule = {'rule_src': 'any', 'rule_dst': 'any', 'rule_svc': 'any',
- 'rule_src_refs': 'any_obj_placeholder', 'rule_dst_refs': 'any_obj_placeholder',
- 'rule_src_neg': False, 'rule_dst_neg': False,
- 'rule_svc_refs': 'any_svc_placeholder'}
- if 'negate-source' in rule_orig and rule_orig['negate-source']=='yes':
- rule["rule_src_neg"] = True
- if 'negate-destination' in rule_orig and rule_orig['negate-destination']=='yes':
- rule["rule_dst_neg"] = True
- rule.update({
- "rule_svc_neg": False, # not possible to negate the svc field on Palo
- "rulebase_name": rule_orig['@vsys'],
- "rule_name": rule_orig['@name'],
- 'rule_type': 'access',
- 'rule_num': rule_number,
- 'rule_installon': rule_orig['@vsys'],
- 'parent_rule_id': None,
- 'rule_time': None,
- 'rule_implied': False,
- 'rule_comment': None,
- 'rule_track': 'None',
- 'rule_uid': rule_orig['@uuid'],
- 'rule_disabled': False,
- 'control_id': import_id
- })
-
- if "action" in rule_orig:
- if rule_orig['action']=='allow':
- rule['rule_action'] = 'accept'
- elif rule_orig['action']=='drop':
- rule['rule_action'] = 'drop'
- elif rule_orig['action']=='deny':
- rule['rule_action'] = 'deny'
- elif rule_orig['action']=='reset-client':
- rule['rule_action'] = 'reject'
- else:
- logger.warning("found undefined action:" + str(rule_orig))
- else: # NAT rules
- rule['rule_action'] = "accept"
- rule['rule_type'] = 'nat'
-
- # TODO: should either duplicate the rule for each zone to zone pair
- # or much better allow for n:m rule:zone mappings --> change of DB necessary
- # instead we are just picking the last one!!!
- for z in rule_orig['from']['member']:
- rule.update({'rule_from_zone': z})
- for z in rule_orig['to']['member']:
- rule.update({'rule_to_zone': z})
-
- if 'disabled' in rule_orig and rule_orig['disabled']=='yes':
- rule['rule_disabled'] = True
- if 'log-start' in rule_orig:
- if rule_orig['log-start']=='yes':
- rule['rule_track'] = 'all start'
- elif rule_orig['log-start']=='no':
- rule['rule_track'] = 'None'
- else:
- logger.warning ("found undefined track:" + str(rule_orig))
- rule['rule_track'] = 'None'
- else:
- rule['rule_track'] = 'None'
-
- if "source" in rule_orig:
- if 'member' in rule_orig["source"]:
- source_objects = rule_orig["source"]["member"]
- else:
- source_objects = [rule_orig["source"]]
- rule['rule_src_refs'], rule["rule_src"] = parse_obj_list(source_objects, import_id, config2import['network_objects'], rule["rule_uid"])
- else:
- logger.warning("found undefined source in rule: " + str(rule_orig))
-
- if "destination" in rule_orig:
- if 'member' in rule_orig["destination"]:
- destination_objects = rule_orig["destination"]["member"]
- else:
- destination_objects = [rule_orig["destination"]]
- rule['rule_dst_refs'], rule["rule_dst"] = parse_obj_list(destination_objects, import_id, config2import['network_objects'], rule["rule_uid"])
- else:
- logger.warning("found undefined destination in rule: " + str(rule_orig))
-
- services = []
- if "service" in rule_orig:
- if 'member' in rule_orig['service']:
- services = rule_orig["service"]["member"]
- else:
- services = [rule_orig["service"]]
- if services[0] == 'application-default' or services[0] == 'any':
- services = []
- apps = []
- if 'application' in rule_orig:
- # no services given but applications - parse apps
- if 'member' in rule_orig['application']:
- # apps = ['any'] ## TEMP before app parsing
- apps = rule_orig["application"]["member"]
- else:
- apps = [rule_orig["application"]]
- if apps[0] == 'any':
- apps = []
-
- rule['rule_svc_refs'], rule["rule_svc"] = parse_svc_list(apps + services, import_id, config2import['service_objects'], rule["rule_uid"], type='service')
-
- rule_number += 1
- rules.append(rule)
-
- if 'rules' not in config2import:
- config2import.update({'rules': []})
- config2import['rules'] += rules
diff --git a/roles/importer/files/importer/paloaltomanagement2023ff/palo_service.py b/roles/importer/files/importer/paloaltomanagement2023ff/palo_service.py
deleted file mode 100644
index 2441cae084..0000000000
--- a/roles/importer/files/importer/paloaltomanagement2023ff/palo_service.py
+++ /dev/null
@@ -1,136 +0,0 @@
-from fwo_const import list_delimiter
-from fwo_log import getFwoLogger
-
-
-def normalize_svcobjects(full_config, config2import, import_id):
- svc_objects = []
- for svc_orig in full_config["/Objects/Services"]:
- svc_objects.append(parse_svc(svc_orig, import_id,config2import))
- for svc_grp_orig in full_config["/Objects/ServiceGroups"]:
- svc_grp = extract_base_svc_infos(svc_grp_orig, import_id)
- svc_grp["svc_typ"] = "group"
- svc_grp["svc_member_refs"] , svc_grp["svc_member_names"] = parse_svc_group(svc_grp_orig,config2import)
- svc_objects.append(svc_grp)
- config2import['service_objects'] += svc_objects
-
-
-def parse_svc_group(orig_grp,config2import):
- refs = []
- names = []
- if "dynamic" in orig_grp:
- pass
- if "static" in orig_grp and "member" in orig_grp["static"]:
- for m in orig_grp['static']['member']:
- names.append(m)
- refs.append(m)
- return list_delimiter.join(refs), list_delimiter.join(names)
-
-
-def extract_base_svc_infos(svc_orig, import_id):
- svc = {}
- if "@name" in svc_orig:
- svc["svc_uid"] = svc_orig["@name"]
- svc["svc_name"] = svc_orig["@name"]
- if "comment" in svc_orig:
- svc["svc_comment"] = svc_orig["comment"]
- svc["svc_timeout"] = None
- svc["svc_color"] = None
- svc["control_id"] = import_id
- svc["svc_typ"] = 'simple'
- return svc
-
-
-def parse_svc(svc_orig, import_id,config2import):
- svc = extract_base_svc_infos(svc_orig, import_id)
- if 'protocol' in svc_orig:
- proto_string = 'undefined'
- if 'tcp' in svc_orig['protocol']:
- svc["ip_proto"] = 6
- proto_string = 'tcp'
- svc["svc_port"] = svc_orig['protocol']['tcp']['port']
- elif 'udp' in svc_orig['protocol']:
- svc["ip_proto"] = 17
- proto_string = 'udp'
-
- if proto_string=='undefined':
- svc["svc_name"] += " [Protocol \"" + str(svc_orig["protocol"]) + "\" not supported]"
- else:
- port_string = svc_orig['protocol'][proto_string]['port']
- if ',' in port_string:
- svc["svc_typ"] = "group"
- svc["svc_port"] = None
- members = []
- for p in port_string.split(","):
- hlp_svc = create_helper_service(p, proto_string, svc["svc_name"], import_id)
- add_service(hlp_svc, config2import)
- members.append(hlp_svc['svc_uid'])
- svc["svc_members"] = list_delimiter.join(members)
- svc["svc_member_refs"] = list_delimiter.join(members)
- else: # just a single port (range)
- extract_port_for_service(port_string, svc)
- return svc
-
-
-def add_service(svc, config2import):
- if 'service_objects' not in config2import:
- config2import.update({'service_objects': []})
- config2import['service_objects'].append(svc)
-
-
-def extract_port_for_service(port_string, svc):
- if '-' in port_string:
- port_range = port_string.split("-")
- if len(port_range)==2:
- svc["svc_port"] = port_range[0]
- svc["svc_port_end"] = port_range[1]
- else:
- logger = getFwoLogger()
- logger.warning("found strange port range with more than one hyphen: " + str(port_string))
- else:
- svc["svc_port"] = port_string
-
-
-def create_helper_service(ports, proto_string, parent_svc_name, import_id):
- svc = {
- "svc_name": parent_svc_name + "_" + proto_string + "_" + ports,
- "svc_uid": parent_svc_name + "_" + proto_string + "_" + ports,
- "svc_comment": "helper service for Palo Alto multiple port range object: " + parent_svc_name,
- "control_id": import_id,
- "svc_typ": 'simple'
- }
-
- extract_port_for_service(ports, svc)
- return svc
-
-
-def parse_svc_list(nw_obj_list, import_id, obj_list, id, type='network'):
- refs = []
- names = []
- for obj_name in nw_obj_list:
- names.append(obj_name)
- refs.append(lookup_svc_obj_uid(obj_name, obj_list, import_id, type=type))
- return list_delimiter.join(refs), list_delimiter.join(names)
-
-
-def lookup_svc_obj_uid(obj_name, obj_list, import_id, type='network'):
- logger = getFwoLogger()
- for o in obj_list:
- if type=='service' and 'svc_name' in o:
- if o['svc_name']==obj_name:
- return o['svc_uid']
- else:
- logger.warning("could not find object name in object " + str(o))
-
- # could not find existing obj in obj list, so creating new one
- return add_svc_obj(obj_name, obj_list, import_id)
-
-
-def add_svc_obj(svc_in, svc_list, import_id):
- svc_obj = {}
- svc_obj['svc_name'] = "#" + svc_in
- svc_obj['svc_uid'] = "#" + svc_in
- svc_obj['control_id'] = import_id
- svc_obj['svc_typ'] = 'simple'
-
- svc_list.append(svc_obj)
- return svc_obj['svc_name']
diff --git a/roles/importer/files/importer/paloaltomanagement2023ff/palo_zone.py b/roles/importer/files/importer/paloaltomanagement2023ff/palo_zone.py
deleted file mode 100644
index a55f86dc95..0000000000
--- a/roles/importer/files/importer/paloaltomanagement2023ff/palo_zone.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from asyncio.log import logger
-from fwo_log import getFwoLogger
-from fwo_const import list_delimiter
-
-
-def normalize_zones(full_config, config2import, import_id):
- zones = []
- for zone_orig in full_config["/Network/Zones"]:
- zones.append({
- "zone_name": zone_orig["@name"],
- "zone_uid": zone_orig["@name"],
- "control_id": import_id
- })
-
- config2import['zone_objects'] = zones
diff --git a/roles/importer/files/importer/query_analyzer.py b/roles/importer/files/importer/query_analyzer.py
new file mode 100644
index 0000000000..26e3ad15d7
--- /dev/null
+++ b/roles/importer/files/importer/query_analyzer.py
@@ -0,0 +1,167 @@
+try:
+ # GraphQL-core v3+
+ from graphql import parse, print_ast, visit
+ from graphql.language import Visitor
+ from graphql.language.ast import DocumentNode as Document # type: ignore # noqa: PGH003
+ from graphql.language.ast import OperationDefinitionNode as OperationDefinition
+ from graphql.language.ast import VariableDefinitionNode as VariableDefinition
+except ImportError:
+ # GraphQL-core v2
+ from graphql import parse, print_ast, visit
+ from graphql.language.ast import Document, OperationDefinition, VariableDefinition # type: ignore # noqa: PGH003
+ from graphql.language.visitor import Visitor
+
+from typing import Any
+
+from fwo_const import API_CALL_CHUNK_SIZE
+
+
+class QueryAnalyzer(Visitor):
+ """
+ A class for analyzing GraphQL queries.
+ """
+
+ _ast: Document | None
+ _variable_definitions: dict[str, dict[str, Any]]
+ _query_string: str
+ _query_variables: dict[str, Any]
+ _query_info: dict[str, Any]
+
+ @property
+ def variable_definitions(self) -> dict[str, dict[str, Any]]:
+ """Returns the dictionary of extracted variable definitions."""
+ return self._variable_definitions
+
+ @property
+ def ast(self) -> Document | None: # type: ignore # noqa: PGH003
+ """Returns the AST."""
+ return self._ast # type: ignore # noqa: PGH003
+
+ @property
+ def query_string(self) -> str:
+ """Returns the original query string."""
+ return self._query_string
+
+ @property
+ def query_variables(self) -> dict[str, Any]:
+ """Returns the provided query variables."""
+ return self._query_variables
+
+ def __init__(self):
+ super().__init__()
+ self._ast = None
+ self._variable_definitions = {}
+ self._query_string = ""
+ self._query_variables = {}
+ self._query_info = {}
+
+ def analyze_payload(self, query_string: str, query_variables: dict[str, Any] | None = None) -> dict[str, Any]:
+ """
+ Analyzes a GraphQL query and returns information about it.
+ """
+ self._ast = parse(query_string)
+ self._query_string = query_string
+ self._query_variables = query_variables or {}
+ self._variable_definitions = {}
+
+ # Apply visitor pattern (calls enter_* methods)
+
+ visit(self._ast, self) # type: ignore # noqa: PGH003
+
+ # Analyze necessity of chunking and parameters that are necessary for the chunking process.
+
+ needs_chunking, adjusted_chunk_size, list_elements_length, chunkable_variables = self._get_chunking_info(
+ query_variables
+ )
+ self._query_info["chunking_info"] = {
+ "needs_chunking": needs_chunking,
+ "adjusted_chunk_size": adjusted_chunk_size,
+ "chunkable_variables": chunkable_variables,
+ "total_elements": list_elements_length,
+ }
+
+ return self._query_info
+
+ def get_adjusted_chunk_size(self, lists_in_query_variable: dict[str, Any]) -> int:
+ """
+ Gets an adjusted chunk size.
+ """
+ return (
+ int(
+ API_CALL_CHUNK_SIZE
+ / len([list_object for list_object in lists_in_query_variable.values() if len(list_object) > 0])
+ )
+ or 1
+ )
+
+ def enter_OperationDefinition(self, node: OperationDefinition, *_): # type: ignore # noqa: N802, PGH003
+ """
+ Called by visit function for each variable definition in the AST.
+ """
+ self.enter_operation_definition(node) # type: ignore # noqa: PGH003
+
+ def enter_VariableDefinition(self, node: VariableDefinition, *_): # type: ignore # noqa: N802, PGH003
+ """
+ Called by visit function for each variable definition in the AST.
+ """
+ self.enter_variable_definition(node) # type: ignore # noqa: PGH003
+
+ def enter_operation_definition(self, node: OperationDefinition, *_): # type: ignore # noqa: PGH003
+ """
+ Called by visit function for each variable definition in the AST.
+ """
+ self._query_info["query_type"] = node.operation # type: ignore # noqa: PGH003
+ self._query_info["query_name"] = node.name.value if node.name else "" # type: ignore # noqa: PGH003
+
+ def enter_variable_definition(self, node: VariableDefinition, *_): # type: ignore # noqa: PGH003
+ """
+ Called by visit function for each variable definition in the AST.
+ """
+ var_name = node.variable.name.value # type: ignore # noqa: PGH003
+ type_str = print_ast(node.type) # type: ignore # noqa: PGH003
+
+ # Store information about the variable definitions.
+
+ if "query_args" not in self._query_info:
+ self._query_info["query_args"] = {}
+
+ self._query_info["query_args"][var_name] = type_str
+
+ self._variable_definitions[var_name] = {
+ "type": type_str,
+ "required": "!" in type_str,
+ "is_list": "[" in type_str,
+ }
+
+ # If a value was provided for this variable, store it.
+
+ if var_name in self._query_variables:
+ self._variable_definitions[var_name]["provided_value"] = self._query_variables[var_name]
+
+ def _get_chunking_info(self, query_variables: dict[str, Any] | None) -> tuple[bool, int, int, list[str]]:
+ # Get all query variables of type list.
+ query_vars = query_variables or {}
+
+ lists_in_query_variable: dict[str, Any] = {
+ chunkable_variable_name: list_object
+ for chunkable_variable_name, list_object in query_vars.items()
+ if isinstance(list_object, list)
+ }
+
+ # If there is no list typed query variable there is nothing chunkable.
+
+ if not lists_in_query_variable or len(lists_in_query_variable.items()) == 0:
+ return False, 0, 0, []
+
+ list_elements_length = sum(len(list_object) for list_object in lists_in_query_variable.values())
+
+ # If the number of all elements is lower than the configured threshold, there is no need for chunking.
+
+ if list_elements_length < API_CALL_CHUNK_SIZE:
+ return False, 0, 0, []
+
+ # If there are more than one chunkable variable, the chunk_size has to be adjusted accordingly.
+
+ adjusted_chunk_size = self.get_adjusted_chunk_size(lists_in_query_variable)
+
+ return True, adjusted_chunk_size, list_elements_length, list(lists_in_query_variable.keys())
diff --git a/roles/importer/files/importer/requirements.txt b/roles/importer/files/importer/requirements.txt
new file mode 100644
index 0000000000..aaeec449e7
--- /dev/null
+++ b/roles/importer/files/importer/requirements.txt
@@ -0,0 +1,26 @@
+# Core runtime
+pydantic>=2.0,<3.0
+jsonpickle>=3.0
+python-dateutil>=2.8
+netaddr>=1.0
+
+# Crypto (AES/CBC via cryptography.hazmat)
+cryptography>=40.0
+
+# HTTP / GraphQL stack
+# Note: Requests >=2.32.0 is compatible with urllib3 2.x
+requests>=2.32.0
+urllib3>=2.0
+graphql-core>=3.0
+
+# SSH
+scrapli>=2025.01.30
+scrapli-community>=2025.01.30
+
+# Test
+pytest>=7.0
+
+# Linting
+ruff>=0.14.8
+pre-commit>=3.0
+pyright>=1.1.407
diff --git a/roles/importer/files/importer/services/__init__.py b/roles/importer/files/importer/services/__init__.py
new file mode 100644
index 0000000000..8b13789179
--- /dev/null
+++ b/roles/importer/files/importer/services/__init__.py
@@ -0,0 +1 @@
+
diff --git a/roles/importer/files/importer/services/enums.py b/roles/importer/files/importer/services/enums.py
new file mode 100644
index 0000000000..3a731488fb
--- /dev/null
+++ b/roles/importer/files/importer/services/enums.py
@@ -0,0 +1,18 @@
+from enum import Enum
+
+
+class Lifetime(Enum):
+ SINGLETON = "singleton"
+ TRANSIENT = "transient"
+ IMPORT = "import"
+ MANAGEMENT = "management" # only holds data with a scope valid for a single sub-management
+
+
+class Services(Enum):
+ UID2ID_MAPPER = "uid2id_mapper"
+ GROUP_FLATS_MAPPER = "group_flats_mapper"
+ PREV_GROUP_FLATS_MAPPER = "prev_group_flats_mapper"
+ GLOBAL_STATE = "global_state"
+ FW_CONFIG_IMPORT_GATEWAY = "fwconfig_import_gateway"
+ FWO_CONFIG = "fwo_config"
+ RULE_ORDER_SERVICE = "rule_order_service"
diff --git a/roles/importer/files/importer/services/global_state.py b/roles/importer/files/importer/services/global_state.py
new file mode 100644
index 0000000000..e61ae2eeae
--- /dev/null
+++ b/roles/importer/files/importer/services/global_state.py
@@ -0,0 +1,22 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from model_controllers.import_state_controller import ImportStateController
+ from models.fwconfig_normalized import FwConfigNormalized
+
+
+class GlobalState:
+ import_state: ImportStateController
+ previous_config: FwConfigNormalized | None
+ previous_global_config: FwConfigNormalized | None
+ normalized_config: FwConfigNormalized | None
+ global_normalized_config: FwConfigNormalized | None
+
+ def __init__(self, import_state: ImportStateController):
+ self.import_state = import_state
+ self.previous_config = None
+ self.previous_global_config = None
+ self.normalized_config = None
+ self.global_normalized_config = None
diff --git a/roles/importer/files/importer/services/group_flats_mapper.py b/roles/importer/files/importer/services/group_flats_mapper.py
new file mode 100644
index 0000000000..081d795dd4
--- /dev/null
+++ b/roles/importer/files/importer/services/group_flats_mapper.py
@@ -0,0 +1,210 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any
+
+if TYPE_CHECKING:
+ from model_controllers.import_state_controller import ImportStateController
+ from models.fwconfig_normalized import FwConfigNormalized
+ from models.networkobject import NetworkObject
+ from models.serviceobject import ServiceObject
+import fwo_const
+from fwo_log import FWOLogger
+from services.service_provider import ServiceProvider
+
+MAX_RECURSION_LEVEL = 20
+CONFIG_NOT_SET_MESSAGE = "normalized config is not set"
+
+
+class GroupFlatsMapper:
+ """
+ Class is responsible for mapping group objects to their fully resolved members.
+ """
+
+ import_state: ImportStateController
+ normalized_config: FwConfigNormalized | None = None
+ global_normalized_config: FwConfigNormalized | None = None
+
+ def __init__(self):
+ global_state = ServiceProvider().get_global_state()
+ self.import_state = global_state.import_state
+ self.network_object_flats: dict[str, set[str]] = {}
+ self.service_object_flats: dict[str, set[str]] = {}
+ self.user_flats: dict[str, set[str]] = {}
+
+ def log_error(self, message: str) -> None:
+ """
+ Log an error message.
+
+ Args:
+ message (str): The error message to log.
+
+ """
+ FWOLogger.error(message)
+
+ def init_config(
+ self, normalized_config: FwConfigNormalized, global_normalized_config: FwConfigNormalized | None = None
+ ) -> None:
+ self.normalized_config = normalized_config
+ self.global_normalized_config = global_normalized_config
+ self.network_object_flats = {}
+ self.service_object_flats = {}
+ self.user_flats = {}
+
+ def get_network_object_flats(self, uids: list[str]) -> list[str]:
+ """
+ Flatten the network object UIDs to all members, including group objects, and the top-level group object itself.
+ Does not check if the given objects are group objects or not.
+
+ Args:
+ uids (list[str]): The list of network object UIDs to flatten.
+
+ Returns:
+ list[str]: The flattened network object UIDs.
+
+ """
+ if self.normalized_config is None:
+ self.log_error(f"{CONFIG_NOT_SET_MESSAGE} - networks")
+ return []
+ all_members: set[str] = set()
+ for uid in uids:
+ members = self.flat_nwobj_members_recursive(uid)
+ if members is not None:
+ all_members.update(members)
+ return list(all_members)
+
+ def flat_nwobj_members_recursive(self, group_uid: str, recursion_level: int = 0) -> set[str] | None:
+ if recursion_level > MAX_RECURSION_LEVEL:
+ FWOLogger.warning(f"recursion level exceeded for group {group_uid}")
+ return None
+ if group_uid in self.network_object_flats:
+ return self.network_object_flats[group_uid]
+ nwobj = self.get_nwobj(group_uid)
+ if nwobj is None:
+ self.log_error(f"object with uid {group_uid} not found in network objects of config")
+ return None
+ members: set[str] = {group_uid}
+ if nwobj.obj_member_refs is None or nwobj.obj_member_refs == "":
+ return members
+ for refs in nwobj.obj_member_refs.split(fwo_const.LIST_DELIMITER):
+ member_uid = refs.split(fwo_const.USER_DELIMITER)[0] if fwo_const.USER_DELIMITER in refs else refs
+ flat_members = self.flat_nwobj_members_recursive(member_uid, recursion_level + 1)
+ if flat_members is None:
+ continue
+ members.update(flat_members)
+ self.network_object_flats[group_uid] = members
+ return members
+
+ def get_nwobj(self, group_uid: str) -> NetworkObject | None:
+ if not self.normalized_config:
+ return None
+ nwobj = self.normalized_config.network_objects.get(group_uid, None)
+ if nwobj is None and self.global_normalized_config is not None:
+ nwobj = self.global_normalized_config.network_objects.get(group_uid, None)
+ return nwobj
+
+ def get_service_object_flats(self, uids: list[str]) -> list[str]:
+ """
+ Flatten the service object UIDs to all members, including group objects, and the top-level group object itself.
+ Does not check if the given objects are group objects or not.
+
+ Args:
+ uids (list[str]): The list of service object UIDs to flatten.
+
+ Returns:
+ list[str]: The flattened service object UIDs.
+
+ """
+ if self.normalized_config is None:
+ self.log_error(f"{CONFIG_NOT_SET_MESSAGE} - services")
+ return []
+ all_members: set[str] = set()
+ for uid in uids:
+ members = self.flat_svcobj_members_recursive(uid)
+ if members is not None:
+ all_members.update(members)
+ return list(all_members)
+
+ def flat_svcobj_members_recursive(self, group_uid: str, recursion_level: int = 0) -> set[str] | None:
+ if recursion_level > MAX_RECURSION_LEVEL:
+ FWOLogger.warning(f"recursion level exceeded for group {group_uid}")
+ return None
+ if group_uid in self.service_object_flats:
+ return self.service_object_flats[group_uid]
+ svcobj = self.get_svcobj(group_uid)
+ if svcobj is None:
+ self.log_error(f"object with uid {group_uid} not found in service objects of config")
+ return None
+ members: set[str] = {group_uid}
+ if svcobj.svc_member_refs is None or svcobj.svc_member_refs == "":
+ return members
+ for member_uid in svcobj.svc_member_refs.split(fwo_const.LIST_DELIMITER):
+ flat_members = self.flat_svcobj_members_recursive(member_uid, recursion_level + 1)
+ if flat_members is None:
+ continue
+ members.update(flat_members)
+ self.service_object_flats[group_uid] = members
+ return members
+
+ def get_svcobj(self, group_uid: str) -> ServiceObject | None:
+ if not self.normalized_config:
+ return None
+ svcobj = self.normalized_config.service_objects.get(group_uid, None)
+ if svcobj is None and self.global_normalized_config is not None:
+ # try to get from global normalized config if not found in current normalized config
+ svcobj = self.global_normalized_config.service_objects.get(group_uid, None)
+ return svcobj
+
+ def get_user_flats(self, uids: list[str]) -> list[str]:
+ """
+ Flatten the user UIDs to all members, including groups, and the top-level group itself.
+ Does not check if the given users are groups or not.
+
+ Args:
+ uids (list[str]): The list of user UIDs to flatten.
+
+ Returns:
+ list[str]: The flattened user UIDs.
+
+ """
+ if self.normalized_config is None:
+ self.log_error(f"{CONFIG_NOT_SET_MESSAGE} - users")
+ return []
+ all_members: set[str] = set()
+ for uid in uids:
+ members = self.flat_user_members_recursive(uid)
+ if members is not None:
+ all_members.update(members)
+ return list(all_members)
+
+ def flat_user_members_recursive(self, group_uid: str, recursion_level: int = 0) -> set[str] | None:
+ if recursion_level > MAX_RECURSION_LEVEL:
+ FWOLogger.warning(f"recursion level exceeded for group {group_uid}")
+ return None
+ if group_uid in self.user_flats:
+ return self.user_flats[group_uid]
+
+ user = self.get_user(group_uid)
+ if user is None:
+ self.log_error(f"object with uid {group_uid} not found in users of config")
+ return None
+ members: set[str] = {group_uid}
+ if "user_member_refs" not in user or user["user_member_refs"] is None or user["user_member_refs"] == "":
+ return members
+ for member_uid in user["user_member_refs"].split(
+ fwo_const.LIST_DELIMITER
+ ): # TODO: adjust when/if users are refactored into objects
+ flat_members = self.flat_user_members_recursive(member_uid, recursion_level + 1)
+ if flat_members is None:
+ continue
+ members.update(flat_members)
+ self.user_flats[group_uid] = members
+ return members
+
+ def get_user(self, group_uid: str) -> Any | None:
+ if not self.normalized_config:
+ return None
+ user = self.normalized_config.users.get(group_uid, None)
+ if user is None and self.global_normalized_config is not None:
+ # try to get from global normalized config if not found in current normalized config
+ user = self.global_normalized_config.users.get(group_uid, None)
+ return user
diff --git a/roles/importer/files/importer/services/service_provider.py b/roles/importer/files/importer/services/service_provider.py
new file mode 100644
index 0000000000..9243d7dbac
--- /dev/null
+++ b/roles/importer/files/importer/services/service_provider.py
@@ -0,0 +1,162 @@
+from collections.abc import Callable
+from typing import TYPE_CHECKING, Any
+
+from services.enums import Lifetime, Services
+
+if TYPE_CHECKING:
+ from model_controllers.fwconfig_import_ruleorder import RuleOrderService
+ from services.global_state import GlobalState
+ from services.group_flats_mapper import GroupFlatsMapper
+ from services.uid2id_mapper import Uid2IdMapper
+
+
+class ServiceProviderEntry:
+ def __init__(self, constructor: Callable[[], Any], lifetime: Lifetime):
+ self.constructor = constructor
+ self.lifetime = lifetime
+
+
+class ServiceProvider:
+ """
+ Class serves as an IOC-container (IOC = inversion of controls) and its purpose is to manage instantiation and lifetime of service classes.
+ """
+
+ _instance: "ServiceProvider | None" = None
+ _services: dict[Services, ServiceProviderEntry]
+ _singletons: dict[Services, Any]
+ _import: dict[tuple[int, Services], Any]
+ _management: dict[tuple[int, Services], Any]
+
+ def __new__(cls):
+ if cls._instance is None:
+ cls._instance = super().__new__(cls)
+ cls._instance._services = {} # noqa: SLF001
+ cls._instance._singletons = {} # noqa: SLF001
+ cls._instance._import = {} # noqa: SLF001
+
+ return cls._instance
+
+ def register(self, key: Services, constructor: Callable[[], Any], lifetime: Lifetime):
+ self._services[key] = ServiceProviderEntry(constructor, lifetime)
+
+ def get_global_state(self) -> "GlobalState":
+ return self.get_service(Services.GLOBAL_STATE)
+
+ def dispose_global_state(self):
+ self.dispose_service(Services.GLOBAL_STATE)
+
+ def get_fwo_config(self) -> dict[str, Any]:
+ return self.get_service(Services.FWO_CONFIG)
+
+ def dispose_fwo_config(self):
+ self.dispose_service(Services.FWO_CONFIG)
+
+ def get_group_flats_mapper(self, import_id: int = 0) -> "GroupFlatsMapper":
+ return self.get_service(Services.GROUP_FLATS_MAPPER, import_id=import_id)
+
+ def dispose_group_flats_mapper(self, import_id: int = 0):
+ self.dispose_service(Services.GROUP_FLATS_MAPPER, import_id=import_id)
+
+ def get_prev_group_flats_mapper(self, import_id: int = 0) -> "GroupFlatsMapper":
+ return self.get_service(Services.PREV_GROUP_FLATS_MAPPER, import_id=import_id)
+
+ def dispose_prev_group_flats_mapper(self, import_id: int = 0):
+ self.dispose_service(Services.PREV_GROUP_FLATS_MAPPER, import_id=import_id)
+
+ def get_uid2id_mapper(self, import_id: int = 0) -> "Uid2IdMapper":
+ return self.get_service(Services.UID2ID_MAPPER, import_id=import_id)
+
+ def dispose_uid2id_mapper(self, import_id: int = 0):
+ self.dispose_service(Services.UID2ID_MAPPER, import_id=import_id)
+
+ def get_rule_order_service(self, import_id: int = 0) -> "RuleOrderService":
+ return self.get_service(Services.RULE_ORDER_SERVICE, import_id=import_id)
+
+ def dispose_rule_order_service(self, import_id: int = 0):
+ self.dispose_service(Services.RULE_ORDER_SERVICE, import_id=import_id)
+
+ def get_service(self, key: Services, import_id: int = 0, management_id: int = 0) -> Any:
+ """
+ Get an instance of a service based on its lifetime. The service will be instantiated if it does not already exist.
+
+ :param key: The service to get.
+ :param import_id: The import ID for IMPORT lifetime services.
+ :param management_id: The management ID for MANAGEMENT lifetime services.
+ :return: The instance of the requested service.
+ """
+ entry = self._services.get(key)
+ service_instance = None
+
+ if not entry:
+ raise ValueError(f"Service '{key}' is not registered.")
+
+ match entry.lifetime:
+ case Lifetime.SINGLETON:
+ if key not in self._singletons:
+ self._singletons[key] = entry.constructor()
+ service_instance = self._singletons[key]
+
+ case Lifetime.IMPORT:
+ import_specific_key = (import_id, key)
+ if import_specific_key not in self._import:
+ self._import[import_specific_key] = entry.constructor()
+ service_instance = self._import[import_specific_key]
+
+ case Lifetime.MANAGEMENT:
+ management_specific_key = (management_id, key)
+ if management_specific_key not in self._management:
+ self._management[management_specific_key] = entry.constructor()
+ service_instance = self._management[management_specific_key]
+
+ case _:
+ raise ValueError(f"Unsupported lifetime '{entry.lifetime}' for service '{key}'.")
+
+ return service_instance
+
+ def dispose_service(self, key: Services, import_id: int = 0, management_id: int = 0):
+ """
+ Dispose of a service instance based on its lifetime.
+
+ :param key: The service to dispose of.
+ :param import_id: The import ID for IMPORT lifetime services.
+ :param management_id: The management ID for MANAGEMENT lifetime services.
+ """
+ entry = self._services.get(key)
+ if not entry:
+ raise ValueError(f"Service '{key}' is not registered.")
+
+ match entry.lifetime:
+ case Lifetime.SINGLETON:
+ if key in self._singletons:
+ del self._singletons[key]
+
+ case Lifetime.IMPORT:
+ import_specific_key = (import_id, key)
+ if import_specific_key in self._import:
+ del self._import[import_specific_key]
+
+ case Lifetime.MANAGEMENT:
+ management_specific_key = (management_id, key)
+ if management_specific_key in self._management:
+ del self._management[management_specific_key]
+
+ case _:
+ raise ValueError(f"Unsupported lifetime '{entry.lifetime}' for service '{key}'.")
+
+ def dispose_scope_import(self, import_id: int):
+ """
+ Dispose of all services associated with a specific import ID.
+ :param import_id: The import ID whose services should be disposed of.
+ """
+ keys_to_remove = [key for key in self._import if key[0] == import_id]
+ for key in keys_to_remove:
+ del self._import[key]
+
+ def dispose_scope_management(self, management_id: int):
+ """
+ Dispose of all services associated with a specific management ID.
+ :param management_id: The management ID whose services should be disposed of.
+ """
+ keys_to_remove = [key for key in self._management if key[0] == management_id]
+ for key in keys_to_remove:
+ del self._management[key]
diff --git a/roles/importer/files/importer/services/uid2id_mapper.py b/roles/importer/files/importer/services/uid2id_mapper.py
new file mode 100644
index 0000000000..c0cea96924
--- /dev/null
+++ b/roles/importer/files/importer/services/uid2id_mapper.py
@@ -0,0 +1,399 @@
+from logging import Logger
+from typing import TYPE_CHECKING, Any
+
+from fwo_log import FWOLogger
+
+if TYPE_CHECKING:
+ from model_controllers.import_state_controller import ImportStateController
+import fwo_const
+from fwo_api import FwoApi
+from fwo_exceptions import FwoImporterError
+from services.enums import Services
+from services.service_provider import ServiceProvider
+
+
+class Uid2IdMap:
+ """
+ A simple data structure to hold UID to ID mappings.
+ Includes current and outdated, local and global mappings.
+ """
+
+ def __init__(self):
+ self.local: dict[str, int] = {}
+ self.outdated_local: dict[str, int] = {}
+ self.global_map: dict[str, int] = {}
+ self.outdated_global: dict[str, int] = {}
+
+ def get(self, uid: str, before_update: bool = False, local_only: bool = False) -> int | None:
+ if before_update:
+ outdated_id = self.outdated_local.get(uid) or self.outdated_global.get(uid)
+ if outdated_id is not None:
+ return outdated_id
+ return self.local.get(uid) or self.global_map.get(uid) # was not updated, use current
+ if local_only:
+ return self.local.get(uid)
+ return self.local.get(uid) or self.global_map.get(uid)
+
+ def set(self, uid: str, db_id: int, is_global: bool = False):
+ target_map = self.global_map if is_global else self.local
+ outdated_map = self.outdated_global if is_global else self.outdated_local
+ if uid in target_map:
+ outdated_map[uid] = target_map[uid]
+ target_map[uid] = db_id
+
+ def update(self, new_mappings: dict[str, int], is_global: bool = False):
+ target_map = self.global_map if is_global else self.local
+ outdated_map = self.outdated_global if is_global else self.outdated_local
+ for uid, db_id in new_mappings.items():
+ if uid in target_map:
+ outdated_map[uid] = target_map[uid]
+ target_map[uid] = db_id
+
+
+class Uid2IdMapper:
+ """
+ A class to map unique identifiers (UIDs) to IDs.
+ This class is used to maintain a mapping between UID and relevant ID in the database.
+ """
+
+ import_state: "ImportStateController"
+ logger: Logger
+
+ nwobj_uid2id: Uid2IdMap
+ svc_uid2id: Uid2IdMap
+ user_uid2id: Uid2IdMap
+ zone_name2id: Uid2IdMap
+ rule_uid2id: Uid2IdMap
+
+ @property
+ def api_connection(self) -> FwoApi:
+ return self.import_state.api_connection
+
+ def __init__(self):
+ """
+ Initialize the Uid2IdMapper.
+ """
+ global_state = ServiceProvider().get_service(Services.GLOBAL_STATE)
+ self.import_state = global_state.import_state
+ self.nwobj_uid2id = Uid2IdMap()
+ self.svc_uid2id = Uid2IdMap()
+ self.user_uid2id = Uid2IdMap()
+ self.zone_name2id = Uid2IdMap()
+ self.rule_uid2id = Uid2IdMap()
+
+ def log_error(self, message: str):
+ """
+ Log an error message.
+
+ Args:
+ message (str): The error message to log.
+
+ """
+ self.logger.error(message)
+
+ def log_debug(self, message: str):
+ """
+ Log a debug message.
+
+ Args:
+ message (str): The debug message to log.
+
+ """
+ FWOLogger.debug(message)
+
+ def get_network_object_id(self, uid: str, before_update: bool = False, local_only: bool = False) -> int:
+ """
+ Get the ID for a given network object UID.
+
+ Args:
+ uid (str): The UID of the network object.
+ before_update (bool): If True, use the outdated mapping if available.
+
+ Returns:
+ int: The ID of the network object.
+
+ """
+ nwobj_id = self.nwobj_uid2id.get(uid, before_update, local_only)
+ if nwobj_id is None:
+ raise KeyError(f"Network object UID '{uid}' not found in mapping.")
+ return nwobj_id
+
+ def get_service_object_id(self, uid: str, before_update: bool = False, local_only: bool = False) -> int:
+ """
+ Get the ID for a given service object UID.
+
+ Args:
+ uid (str): The UID of the service object.
+ before_update (bool): If True, use the outdated mapping if available.
+
+ Returns:
+ int: The ID of the service object.
+
+ """
+ svc_id = self.svc_uid2id.get(uid, before_update, local_only)
+ if svc_id is None:
+ raise KeyError(f"Service object UID '{uid}' not found in mapping.")
+ return svc_id
+
+ def get_user_id(self, uid: str, before_update: bool = False, local_only: bool = False) -> int:
+ """
+ Get the ID for a given user UID.
+
+ Args:
+ uid (str): The UID of the user.
+ before_update (bool): If True, use the outdated mapping if available.
+
+ Returns:
+ int: The ID of the user.
+
+ """
+ user_id = self.user_uid2id.get(uid, before_update, local_only)
+ if user_id is None:
+ raise KeyError(f"User UID '{uid}' not found in mapping.")
+ return user_id
+
+ def get_zone_object_id(self, name: str, before_update: bool = False, local_only: bool = False) -> int:
+ """
+ Get the ID for a given zone UID.
+
+ Args:
+ name (str): The name of the zone.
+ before_update (bool): If True, use the outdated mapping if available.
+
+ Returns:
+ int: The ID of the zone
+
+ """
+ zone_id = self.zone_name2id.get(name, before_update, local_only)
+ if zone_id is None:
+ raise KeyError(f"Zone Name '{name}' not found in mapping.")
+ return zone_id
+
+ def get_rule_id(self, uid: str, before_update: bool = False) -> int:
+ """
+ Get the ID for a given rule UID.
+
+ Args:
+ uid (str): The UID of the rule.
+ before_update (bool): If True, use the outdated mapping if available.
+
+ Returns:
+ int: The ID of the rule.
+
+ """
+ rule_id = self.rule_uid2id.get(uid, before_update)
+ if rule_id is None:
+ raise KeyError(f"Rule UID '{uid}' not found in mapping.")
+ return rule_id
+
+ def add_network_object_mappings(self, mappings: list[dict[str, Any]], is_global: bool = False):
+ """
+ Add network object mappings to the internal mapping dictionary.
+
+ Args:
+ mappings (list[dict]): A list of dictionaries containing UID and ID mappings.
+ Each dictionary should have 'obj_uid' and 'obj_id' keys.
+
+ """
+ for mapping in mappings:
+ if "obj_uid" not in mapping or "obj_id" not in mapping:
+ raise ValueError("Invalid mapping format. Each mapping must contain 'obj_uid' and 'obj_id'.")
+ self.nwobj_uid2id.set(mapping["obj_uid"], mapping["obj_id"], is_global)
+
+ msg = f"Added {len(mappings)} {'global ' if is_global else ''}network object mappings."
+ self.log_debug(msg)
+
+ def add_service_object_mappings(self, mappings: list[dict[str, Any]], is_global: bool = False):
+ """
+ Add service object mappings to the internal mapping dictionary.
+
+ Args:
+ mappings (list[dict]): A list of dictionaries containing UID and ID mappings.
+ Each dictionary should have 'svc_uid' and 'svc_id' keys.
+
+ """
+ for mapping in mappings:
+ if "svc_uid" not in mapping or "svc_id" not in mapping:
+ raise ValueError("Invalid mapping format. Each mapping must contain 'svc_uid' and 'svc_id'.")
+ self.svc_uid2id.set(mapping["svc_uid"], mapping["svc_id"], is_global)
+
+ self.log_debug(f"Added {len(mappings)} {'global ' if is_global else ''}service object mappings.")
+
+ def add_user_mappings(self, mappings: list[dict[str, Any]], is_global: bool = False):
+ """
+ Add user object mappings to the internal mapping dictionary.
+
+ Args:
+ mappings (list[dict]): A list of dictionaries containing UID and ID mappings.
+ Each dictionary should have 'user_uid' and 'user_id' keys.
+
+ """
+ for mapping in mappings:
+ if "user_uid" not in mapping or "user_id" not in mapping:
+ raise ValueError("Invalid mapping format. Each mapping must contain 'user_uid' and 'user_id'.")
+ self.user_uid2id.set(mapping["user_uid"], mapping["user_id"], is_global)
+
+ self.log_debug(f"Added {len(mappings)} {'global ' if is_global else ''}user mappings.")
+
+ def add_zone_mappings(self, mappings: list[dict[str, Any]], is_global: bool = False):
+ """
+ Add zone object mappings to the internal mapping dictionary.
+
+ Args:
+ mappings (list[dict]): A list of dictionaries containing Name and ID mappings.
+ Each dictionary should have 'zone_name' and 'zone_id' keys.
+
+ """
+ for mapping in mappings:
+ if "zone_name" not in mapping or "zone_id" not in mapping:
+ raise ValueError("Invalid mapping format. Each mapping must contain 'zone_name' and 'zone_id'.")
+ self.zone_name2id.set(mapping["zone_name"], mapping["zone_id"], is_global)
+
+ self.log_debug(f"Added {len(mappings)} {'global ' if is_global else ''}zone mappings.")
+
+ def add_rule_mappings(self, mappings: list[dict[str, Any]]):
+ """
+ Add rule mappings to the internal mapping dictionary.
+
+ Args:
+ mappings (list[dict]): A list of dictionaries containing UID and ID mappings.
+ Each dictionary should have 'rule_uid' and 'rule_id' keys.
+
+ """
+ for mapping in mappings:
+ if "rule_uid" not in mapping or "rule_id" not in mapping:
+ raise ValueError("Invalid mapping format. Each mapping must contain 'rule_uid' and 'rule_id'.")
+ self.rule_uid2id.set(mapping["rule_uid"], mapping["rule_id"])
+
+ self.log_debug(f"Added {len(mappings)} rule mappings.")
+
+ def update_network_object_mapping(self, uids: list[str] | None = None, is_global: bool = False):
+ """
+ Update the mapping for network objects based on the provided UIDs.
+
+ Args:
+ uids (list[str]): A list of UIDs to update the mapping for. If None, all UIDs for the Management will be fetched.
+
+ """
+ query = FwoApi.get_graphql_code([fwo_const.GRAPHQL_QUERY_PATH + "networkObject/getMapOfUid2Id.graphql"])
+
+ if uids is not None:
+ if len(uids) == 0:
+ self.log_debug("Network object mapping updated for 0 objects")
+ return
+ variables = {"uids": uids}
+ else:
+ # If no UIDs are provided, fetch all UIDs for the Management
+ variables = {"mgmId": self.import_state.state.mgm_details.current_mgm_id}
+ try:
+ response = self.import_state.api_connection.call(query, variables)
+ if "errors" in response:
+ raise FwoImporterError(f"Error updating network object mapping: {response['errors']}")
+ self.nwobj_uid2id.update({obj["obj_uid"]: obj["obj_id"] for obj in response["data"]["object"]}, is_global)
+ self.log_debug(f"Network object mapping updated for {len(response['data']['object'])} objects")
+ except Exception as e:
+ raise FwoImporterError(f"Error updating network object mapping: {e}")
+
+ def update_service_object_mapping(self, uids: list[str] | None = None, is_global: bool = False):
+ """
+ Update the mapping for service objects based on the provided UIDs.
+
+ Args:
+ uids (list[str]): A list of UIDs to update the mapping for. If None, all UIDs for the Management will be fetched.
+
+ """
+ query = FwoApi.get_graphql_code([fwo_const.GRAPHQL_QUERY_PATH + "networkService/getMapOfUid2Id.graphql"])
+ if uids is not None:
+ if len(uids) == 0:
+ self.log_debug("Service object mapping updated for 0 objects")
+ return
+ variables = {"uids": uids}
+ else:
+ # If no UIDs are provided, fetch all UIDs for the Management
+ variables = {"mgmId": self.import_state.state.mgm_details.current_mgm_id}
+ try:
+ response = self.import_state.api_connection.call(query, variables)
+ if "errors" in response:
+ raise FwoImporterError(f"Error updating service object mapping: {response['errors']}")
+ self.svc_uid2id.update({obj["svc_uid"]: obj["svc_id"] for obj in response["data"]["service"]}, is_global)
+ self.log_debug(f"Service object mapping updated for {len(response['data']['service'])} objects")
+ except Exception as e:
+ raise FwoImporterError(f"Error updating service object mapping: {e}")
+
+ def update_user_mapping(self, uids: list[str] | None = None, is_global: bool = False):
+ """
+ Update the mapping for users based on the provided UIDs.
+
+ Args:
+ uids (list[str]): A list of UIDs to update the mapping for. If None, all UIDs for the Management will be fetched.
+
+ """
+ query = FwoApi.get_graphql_code([fwo_const.GRAPHQL_QUERY_PATH + "user/getMapOfUid2Id.graphql"])
+ if uids is not None:
+ if len(uids) == 0:
+ self.log_debug("User mapping updated for 0 objects")
+ return
+ variables = {"uids": uids}
+ else:
+ # If no UIDs are provided, fetch all UIDs for the Management
+ variables = {"mgmId": self.import_state.state.mgm_details.current_mgm_id}
+ try:
+ response = self.import_state.api_connection.call(query, variables)
+ if "errors" in response:
+ raise FwoImporterError(f"Error updating user mapping: {response['errors']}")
+ self.user_uid2id.update({obj["user_uid"]: obj["user_id"] for obj in response["data"]["usr"]}, is_global)
+ self.log_debug(f"User mapping updated for {len(response['data']['usr'])} objects")
+ except Exception as e:
+ raise FwoImporterError(f"Error updating user mapping: {e}")
+
+ def update_zone_mapping(self, names: list[str] | None = None, is_global: bool = False):
+ """
+ Update the mapping for zones based on the provided names.
+
+ Args:
+ names (list[str]): A list of zone names to update the mapping for. If None, all zones for the Management will be fetched.
+
+ """
+ query = FwoApi.get_graphql_code([fwo_const.GRAPHQL_QUERY_PATH + "zone/getMapOfName2Id.graphql"])
+ if names is not None:
+ if len(names) == 0:
+ self.log_debug("Zone mapping updated for 0 objects")
+ return
+ variables = {"names": names}
+ else:
+ # If no names are provided, fetch all zones for the Management
+ variables = {"mgmId": self.import_state.state.mgm_details.current_mgm_id}
+ try:
+ response = self.import_state.api_connection.call(query, variables)
+ if "errors" in response:
+ raise FwoImporterError(f"Error updating zone mapping: {response['errors']}")
+ self.zone_name2id.update({obj["zone_name"]: obj["zone_id"] for obj in response["data"]["zone"]}, is_global)
+ self.log_debug(f"Zone mapping updated for {len(response['data']['zone'])} objects")
+ except Exception as e:
+ raise FwoImporterError(f"Error updating zone mapping: {e}")
+
+ def update_rule_mapping(self, uids: list[str] | None = None):
+ """
+ Update the mapping for rules based on the provided UIDs.
+
+ Args:
+ uids (list[str]): A list of UIDs to update the mapping for. If None, all UIDs for the Management will be fetched.
+
+ """
+ query = FwoApi.get_graphql_code([fwo_const.GRAPHQL_QUERY_PATH + "rule/getMapOfUid2Id.graphql"])
+ if uids is not None:
+ if len(uids) == 0:
+ self.log_debug("Rule mapping updated for 0 objects")
+ return
+ variables = {"uids": uids}
+ else:
+ # If no UIDs are provided, fetch all UIDs for the Management
+ variables = {"mgmId": self.import_state.state.mgm_details.current_mgm_id}
+ try:
+ response = self.import_state.api_connection.call(query, variables)
+ if "errors" in response:
+ raise FwoImporterError(f"Error updating rule mapping: {response['errors']}")
+ self.rule_uid2id.update({obj["rule_uid"]: obj["rule_id"] for obj in response["data"]["rule"]})
+ self.log_debug(f"Rule mapping updated for {len(response['data']['rule'])} objects")
+ except Exception as e:
+ raise FwoImporterError(f"Error updating rule mapping: {e}")
diff --git a/roles/importer/files/importer/ssh-client.pl b/roles/importer/files/importer/ssh-client.pl
deleted file mode 100755
index a20c7d8b63..0000000000
--- a/roles/importer/files/importer/ssh-client.pl
+++ /dev/null
@@ -1,1360 +0,0 @@
-#! /usr/bin/perl -w
-#origin: program: sendcommand.pl 2009-2011
-#author: Youri Reddy-Girard (melk0r101@yahoo.com - http//sendcommand.sourceforge.net/)
-#version: 0.1.6 (20110213)
-# SendCommand is a perl script using expect library. It permits the execution of remote
-# command via telnet or ssh on different network devices (Cisco switch, Juniper Netscreen,
-# Blue Coat proxySG, TippingPoint IPS, Linux). Output is highly customizable.
-# IMPORTANT: You need the expect.pm module installed.
-# - on debian type "aptitude install libexpect-perl"
-# - on centos type "cpan" and then type "install Expect"
-#
-# calling syntax: ./ssh-client.pl -z 89.19.225.167 -t netscreen -i .ssh/id_rsa -c "get config" -u cadmin -d 0 -o /tmp/publikat_fw02.cfg
-# needed to handle netscreen imports
-
-#external librairies
-use strict;
-use warnings;
-use diagnostics;
-use File::Path;
-use POSIX qw(strftime);
-use Expect;
-# $Expect::Debug = 9;
-use Getopt::Long qw(:config no_ignore_case bundling);
-
-#############################################################
-# GLOBAL VARIABLE DECLARATION
-#############################################################
-#global constants
-use constant PROGRAM => 'ssh-client.pl'; # name of this program / script
-use constant VERSION => "1.1"; # version of the program
-use constant SSH => "/usr/bin/ssh"; # path to the ssh binary
-use constant TELNET => "/usr/bin/telnet"; # path to the telnet binary
-use constant TIMEOUT_COMMAND => 600; # timeout in seconds after sending the command if no prompt is seen
-use constant TIMEOUT_CONNECT => 120; # timeout in seconds after opening the socket to get the login prompt
-use constant TIMEOUT_GENERIC => 10; # timeout in seconds used for all remaining expect commands
-use constant MAXVAR => 9; # Number of variables (columns) read in the input file
-use constant NBTRY => 1; # Default number of retry if connection fails
-use constant DISPLAYLEVEL => 1; # Default display level
-use constant METHOD => "ssh"; # Default method
-use constant OUTPUTFILEAPPEND => 0; # Default mode for output file (0=erase 1=append)
-use constant PROMPT_REGEX_DEBIAN => "\r\n[^\r\n ]+:[^\r\n]+[\\\$#] ";
-use constant PROMPT_REGEX_IPSO => "\r\n[^\r\n ]+\\\[[^\r\n]+\\\]#";
-use constant PROMPT_REGEX_SPLAT => "\r\n\\\[[^\r\n ]+@[^\r\n]+\\\]#";
-use constant PROMPT_REGEX_REDHAT => "\r\n\\\[[^\r\n ]+@[^\r\n]+\\\]#";
-#use constant PROMPT_REGEX_GENTOO => "[^\n]+@[^\n]+ [^\n]+ \\\$|[^\n]+ [^\n]+ #";
-use constant PROMPT_REGEX_POSIX => PROMPT_REGEX_DEBIAN."|".PROMPT_REGEX_IPSO."|".PROMPT_REGEX_SPLAT."|".PROMPT_REGEX_REDHAT;
-use constant PROMPT_REGEX_BLUECOAT => "\r\n[^\r\n ]+>";
-use constant PROMPT_REGEX_BLUECOATENABLE => "\r\n[^\r\n ]+#";
-use constant PROMPT_REGEX_BLUECOATCONFIG => "\r\n[^\r\n ]+#([^)]+)";
-use constant PROMPT_REGEX_CISCO => "\r\n[^\r\n# ]+>";
-use constant PROMPT_REGEX_CISCOENABLE => "\r\n[^\r\n# ]+#";
-#use constant PROMPT_REGEX_NETSCREEN => qr/\r\n.+?\(.\)\-\>/; # das funktioniert mit clustern
-use constant PROMPT_REGEX_NETSCREEN =>
- qr/\r\n(\-\-\-\smore\s\-\-\-)?([^\s]+?)\-\>/; # annahme: prompt-Zeile enthält kein Whitespace vor ->
-#use constant PROMPT_REGEX_NETSCREEN => "\r?\n?[^\r\n ]+([^\r\n ]+)->";
-#use constant PROMPT_REGEX_TIPPINGPOINT => "\r\n[^\r\n ]+#"; # original
-use constant PROMPT_REGEX_TIPPINGPOINT => "[\r\n]+[^\r\n ]+#"; #TippingPoint is bugged and returned tons of \r when no rows and columns are sent (TTY: telnet and ssh)
-
-
-#global expect
-$Expect::Log_Stdout = 0;
-
-#global variables
-my $PROMPT_REGEX;
-my $LOGFILE;
-my $DISPLAYLEVEL;
-
-
-
-#############################################################
-# FUNCTIONS DECLARATION
-#############################################################
-
-# append_array_to_file()
-# Append an array to a file
-# IN:
-# $file = filename
-# $parr = pointer to array to append
-# OUT:
-# 1 = OK
-# 0 = NOK
-sub append_array_to_file(){
- my $file = $_[0];
- my $parr = $_[1];
- my $result = 0;
- if (open(FILE, ">>", $file)){
- if (print FILE @$parr){
- $result = 1;
- }
- close(FILE);
- }
-}
-
-# append_string_to_file()
-# Append a string to a file
-# IN:
-# $file = filename
-# $string = string to append
-# OUT:
-# 1 = OK
-# 0 = NOK
-sub append_string_to_file(){
- my $file = $_[0];
- my $string = $_[1];
- my $result = 0;
- if (open(FILE, ">>", $file)){
- if (print FILE $string){
- $result = 1;
- }
- close(FILE);
- }
-}
-
-# exp_connect()
-# Connects to any known equipment type
-# IN:
-# $exp = Expect object
-# $equipmenttype = Equipment type (must be supported)
-# $method = Method (must be supported)
-# $port = Port
-# $parrEquipment = Array containing equipmentname, hostname, username, password, enablepassword
-# OUT:
-# 1 = OK
-# 0 = NOK
-sub exp_connect(){
- my $exp = $_[0];
- my $equipmenttype = $_[1];
- my $method = $_[2];
- my $port = $_[3];
- my $parrEquipment = $_[4];
- my $identity_file = $_[5];
- my $result = 0;
- if (($equipmenttype eq "posix") && ($method eq "ssh")){
- $result = &exp_connect_ssh($exp, @$parrEquipment[0], @$parrEquipment[1], @$parrEquipment[2], @$parrEquipment[3], $port, PROMPT_REGEX_POSIX, $identity_file);
- }elsif (($equipmenttype eq "posix") && ($method eq "telnet")){
- $result = &exp_connect_telnet($exp, @$parrEquipment[0], @$parrEquipment[1], @$parrEquipment[2], @$parrEquipment[3], $port, PROMPT_REGEX_POSIX);
- }elsif (($equipmenttype eq "netscreen") && ($method eq "ssh")){
- $result = &exp_connect_ssh($exp, @$parrEquipment[0], @$parrEquipment[1], @$parrEquipment[2], @$parrEquipment[3], $port, PROMPT_REGEX_NETSCREEN, $identity_file);
- }elsif (($equipmenttype eq "bluecoat")&&($method eq "ssh")){
- $result = &exp_connect_ssh($exp, @$parrEquipment[0], @$parrEquipment[1], @$parrEquipment[2], @$parrEquipment[3], $port, PROMPT_REGEX_BLUECOAT, $identity_file);
- if ($result){
- $result = &exp_enter_enable_mode($exp, @$parrEquipment[0], @$parrEquipment[4], PROMPT_REGEX_BLUECOATENABLE);
- if ($result){
- $result = &exp_enter_config_mode($exp, @$parrEquipment[0], PROMPT_REGEX_BLUECOATCONFIG);
- if ($result){
- my $commandresult = "";
- my $sendcommandresult = 0;
- $sendcommandresult = &exp_send_command($exp, $equipmenttype, @$parrEquipment[0], "line-vty ;mode", \$commandresult);
- if ($sendcommandresult){
- $sendcommandresult = &exp_send_command($exp, $equipmenttype, @$parrEquipment[0], "no length", \$commandresult);
- if ($sendcommandresult){
- $sendcommandresult = &exp_send_command($exp, $equipmenttype, @$parrEquipment[0], "exit", \$commandresult);
- if (!$sendcommandresult){
- &log_msg("@$parrEquipment[0]: error when exiting line-vty mode");
- }
- }else{
- &log_msg("@$parrEquipment[0]: error when entering line-vty mode");
- }
- }else{
- &log_msg("@$parrEquipment[0]: error when entering line-vty mode");
- }
- $result = $sendcommandresult;
- if ($result){
- $result = &exp_exit_config_mode($exp, @$parrEquipment[0], PROMPT_REGEX_BLUECOATENABLE);
- }
- }
- }
- }
- }elsif (($equipmenttype eq "cisco")&&($method eq "ssh")){
- $result = &exp_connect_ssh($exp, @$parrEquipment[0], @$parrEquipment[1], @$parrEquipment[2], @$parrEquipment[3], $port, PROMPT_REGEX_CISCO);
- if ($result){
- $result = &exp_enter_enable_mode($exp, @$parrEquipment[0], @$parrEquipment[4], PROMPT_REGEX_CISCOENABLE);
- if ($result){
- my $commandresult = "";
- my $sendcommandresult = &exp_send_command($exp, $equipmenttype, @$parrEquipment[0], "term length 0", \$commandresult);
- if (!$sendcommandresult){
- &log_msg("@$parrEquipment[0]: error when setting term length");
- }
- $result = $sendcommandresult;
- }
- }
- }elsif (($equipmenttype eq "cisco")&&($method eq "telnet")){
- $result = &exp_connect_telnet($exp, @$parrEquipment[0], @$parrEquipment[1], @$parrEquipment[2], @$parrEquipment[3], $port, PROMPT_REGEX_CISCO);
- if ($result){
- $result = &exp_enter_enable_mode($exp, @$parrEquipment[0], @$parrEquipment[4], PROMPT_REGEX_CISCOENABLE);
- if ($result){
- my $commandresult = "";
- my $sendcommandresult = &exp_send_command($exp, $equipmenttype, @$parrEquipment[0], "term length 0", \$commandresult);
- if (!$sendcommandresult){
- &log_msg("@$parrEquipment[0]: error when setting term length");
- }
- $result = $sendcommandresult;
- }
- }
- }elsif (($equipmenttype eq "tippingpoint") && ($method eq "ssh")){
- $result = &exp_connect_ssh($exp, @$parrEquipment[0], @$parrEquipment[1], @$parrEquipment[2], @$parrEquipment[3], $port, PROMPT_REGEX_TIPPINGPOINT);
- }else{
- &log_msg("Connection for equipmenttype and method combination undefined. (equipmenttype=$equipmenttype method=$method)");
- }
- return $result;
-}
-
-# exp_connect_ssh
-# Expect sequences to connect to a system via ssh
-# IN:
-# $exp = Expect object
-# $equipmentname = equipment name / id / label
-# $hostname = hostname or ip address
-# $username = remote username
-# $password = remote password
-# $port = remote ssh port
-# $prompt_regex = prompt expected
-# OUT:
-# 1 = OK
-# 0 = NOK
-sub exp_connect_ssh(){
- my $exp = $_[0];
- my $equipmentname = $_[1];
- my $hostname = $_[2];
- my $username = $_[3];
- my $password = $_[4];
- my $port = $_[5];
- my $prompt_regex = $_[6];
- my $identity_file = $_[7];
- my $result=0;
-
- if (defined($identity_file) && $identity_file ne '') {
- $identity_file = " -i $identity_file";
- } else {
- $identity_file = '';
- }
-
- my $port_txt = ' ';
- if ($port ne "") { $port_txt = " -p $port "; }
- $exp->spawn(SSH." $identity_file -T $port_txt $username\@$hostname");
- &log_msg("$equipmentname: connecting via ssh $port_txt to $username\@$hostname...");
- $exp->notransfer(1);
- $exp->expect(TIMEOUT_CONNECT,[
- "-re","Terminal type",sub{ #ipso specific
- $exp->set_accum($exp->after);
- &log_msg("$equipmentname: terminal type requested. sending CR");
- $exp->send("\r");
- $exp->exp_continue;
- }
- ],[
- "-re",$prompt_regex,sub{
- $exp->notransfer(0);
- $PROMPT_REGEX = $prompt_regex;
- &log_msg("$equipmentname: login successfully");
- $result=1;
- }
- ],[
- "-re","[^\n]+assword:",sub{
- $exp->notransfer(0);
- $exp->set_accum($exp->after);
- if($password){
- $exp->send("$password\r");
- $exp->notransfer(1);
- $exp->expect(TIMEOUT_GENERIC,[
- "-re","Terminal type",sub{ #ipso specific
- $exp->set_accum($exp->after);
- &log_msg("$equipmentname: terminal type requested. sending CR");
- $exp->send("\r");
- $exp->exp_continue;
- }
- ],[
- "-re",$prompt_regex,sub{
- $exp->notransfer(0);
- $PROMPT_REGEX = $prompt_regex;
- &log_msg("$equipmentname: login successfully");
- $result=1;
- }
- ],[
- "-re","[^\n]+assword:",sub{
- $exp->set_accum($exp->after);
- $exp->notransfer(0);
- &log_error("$equipmentname: invalid username/password");
- $exp->hard_close();
- $result=0;
- }
- ],[
- timeout => sub{
- $exp->set_accum($exp->after);
- $exp->notransfer(0);
- &log_error("$equipmentname: timeout due to unknown input after login");
- $exp->hard_close();
- $result=0;
- }
- ]);
- }else{
- &log_error("$equipmentname: password requested but no password supplied. disconnecting");
- $exp->hard_close();
- $result=0;
- }
- }
- ],[
- "-re","[^\n]+not known",sub{
- $exp->notransfer(0);
- $exp->set_accum($exp->after);
- &log_error("$equipmentname: unknown hostname $hostname");
- $exp->soft_close();
- $result=0;
- }
- ],[
- "-re","[^\n]+refused",sub{
- $exp->notransfer(0);
- $exp->set_accum($exp->after);
- &log_error("$equipmentname: connection refused to $hostname");
- $exp->soft_close();
- $result=0;
- }
- ],[
- "-re","Host key verification failed",sub{
- $exp->notransfer(0);
- $exp->set_accum($exp->after);
- &log_error("$equipmentname: Host $hostname key verification failed");
- $exp->soft_close();
- $result=0;
- }
- ],[
- "Are you sure you want to continue connecting (yes/no)?",sub{
- $exp->set_accum($exp->after);
- &log_msg("$equipmentname: new key fingerprint");
- $exp->send("yes\r");
- $exp->exp_continue;
- }
- ],[
- timeout => sub{
- if ($exp->before() eq ""){
- &log_error("$equipmentname: connection timeout");
- }else{
- &log_error("$equipmentname: timeout due to unknown input before login");
- }
- $result=0;
- }
- ]);
- return $result;
-}
-
-
-# exp_connect_telnet
-# Expect sequences to connect to a system via telnet
-# IN:
-# $exp = Expect object
-# $equipmentname = equipment name / id / label
-# $hostname = hostname or ip address
-# $username = remote username
-# $password = remote password
-# $port = remote ssh port
-# $prompt_regex = prompt expected
-# OUT:
-# 1 = OK
-# 0 = NOK
-sub exp_connect_telnet(){
- my $exp = $_[0];
- my $equipmentname = $_[1];
- my $hostname = $_[2];
- my $username = $_[3];
- my $password = $_[4];
- my $port = $_[5];
- my $prompt_regex = $_[6];
- my $result=0;
-
- if ($port ne ""){
- $exp->spawn(TELNET." $hostname $port");
- &log_msg("$equipmentname: connecting via telnet to $hostname on port $port...");
- }else{
- $exp->spawn(TELNET." $hostname");
- &log_msg("$equipmentname: connecting via telnet to $hostname...");
- }
-
- $exp->expect(TIMEOUT_CONNECT,[
- "-re","[^\n]+incorrect",sub{
- $exp->set_accum($exp->after);
- $exp->notransfer(0);
- &log_error("$equipmentname: invalid username");
- $exp->hard_close();
- $result=0;
- }
- ],[
- "-re","[^\n]+ogin:|Username:",sub{
- $exp->send("$username\r");
- &log_msg("$equipmentname: login prompt detected. sending username (username=$username)");
- $exp->exp_continue;
- }
- ],[
- "-re","[^\n]+assword:",sub{
- if($password){
- $exp->send("$password\r");
- $exp->notransfer(1);
- $exp->expect(TIMEOUT_GENERIC,[
- "-re","Terminal type",sub{ #ipso specific
- $exp->set_accum($exp->after);
- &log_msg("$equipmentname: terminal type requested. sending CR");
- $exp->send("\r");
- $exp->exp_continue;
- }
- ],[
- "-re",$prompt_regex,sub{
- $exp->notransfer(0);
- $PROMPT_REGEX = $prompt_regex;
- &log_msg("$equipmentname: login successfully");
- $result=1;
- }
- ],[
- "-re","[^\n]+incorrect|[^\n]+invalid",sub{
- $exp->set_accum($exp->after);
- $exp->notransfer(0);
- &log_error("$equipmentname: invalid username/password");
- $exp->hard_close();
- $result=0;
- }
- ],[
- "-re","[^\n]+assword:",sub{
- $exp->set_accum($exp->after);
- $exp->notransfer(0);
- &log_error("$equipmentname: invalid username/password");
- $exp->hard_close();
- $result=0;
- }
- ],[
- timeout => sub{
- $exp->set_accum($exp->after);
- $exp->notransfer(0);
- &log_error("$equipmentname: timeout due to unknown input after login");
- $exp->hard_close();
- $result=0;
- }
- ]);
- }else{
- &log_error("$equipmentname: password requested but no password supplied. disconnecting");
- $exp->hard_close();
- $result=0;
- }
- }
- ],[
- "-re","[^\n]+not known",sub{
- $exp->notransfer(0);
- $exp->set_accum($exp->after);
- &log_error("$equipmentname: unknown hostname $hostname");
- $exp->soft_close();
- $result=0;
- }
- ],[
- "-re","[^\n]+refused",sub{
- $exp->notransfer(0);
- $exp->set_accum($exp->after);
- &log_error("$equipmentname: connection refused to $hostname");
- $exp->soft_close();
- $result=0;
- }
- ],[
- timeout => sub{
- if ($exp->before() eq ""){
- &log_error("$equipmentname: connection timeout");
- }else{
- &log_error("$equipmentname: timeout due to unknown input before login");
- }
- $result=0;
- }
- ]);
- return $result;
-}
-
-# exp_exit_config_mode
-# Expect sequences to exit config mode
-# IN:
-# $exp = Expect object
-# $equipmentname = equipment name / id / label
-# $promptenable_regex = prompt expected in enable mode
-# OUT:
-# 1 = OK
-# 0 = NOK
-sub exp_exit_config_mode(){
- my $exp = $_[0];
- my $equipmentname = $_[1];
- my $promptenable_regex = $_[2];
- my $result=0;
-
- $exp->expect(TIMEOUT_GENERIC,[
- "-re",$PROMPT_REGEX,sub{
- $exp->send("exit\r");
- $exp->notransfer(1);
- $exp->expect(TIMEOUT_GENERIC,[
- "-re",$promptenable_regex,sub{
- $exp->notransfer(0);
- $PROMPT_REGEX=$promptenable_regex;
- &log_msg("$equipmentname: exiting config mode successfully");
- $result=1;
- }
- ],[
- timeout => sub{
- $exp->notransfer(0);
- &log_error("$equipmentname: timeout due to unknown input after trying to exit config mode");
- $exp->hard_close();
- $result=0;
- }
- ]);
- }
- ],[
- timeout => sub{
- $exp->notransfer(0);
- &log_error("$equipmentname: timeout due to unknown input before trying to exit config mode");
- $exp->hard_close();
- $result=0;
- }
- ]);
- return $result;
-}
-
-# exp_enter_config_mode
-# Expect sequences to enter config mode
-# IN:
-# $exp = Expect object
-# $equipmentname = equipment name / id / label
-# $promptconfig_regex = prompt expected in config mode
-# OUT:
-# 1 = OK
-# 0 = NOK
-sub exp_enter_config_mode(){
- my $exp = $_[0];
- my $equipmentname = $_[1];
- my $promptconfig_regex = $_[2];
- my $result=0;
-
- $exp->expect(TIMEOUT_GENERIC,[
- "-re",$PROMPT_REGEX,sub{
- $exp->send("configure terminal\r");
- $exp->notransfer(1);
- $exp->expect(TIMEOUT_GENERIC,[
- "-re",$promptconfig_regex,sub{
- $exp->notransfer(0);
- $PROMPT_REGEX=$promptconfig_regex;
- &log_msg("$equipmentname: entering config mode successfully");
- $result=1;
- }
- ],[
- timeout => sub{
- $exp->notransfer(0);
- &log_error("$equipmentname: timeout due to unknown input after trying to enter config mode");
- $exp->hard_close();
- $result=0;
- }
- ]);
- }
- ],[
- timeout => sub{
- $exp->notransfer(0);
- &log_error("$equipmentname: timeout due to unknown input before trying to enter config mode");
- $exp->hard_close();
- $result=0;
- }
- ]);
- return $result;
-}
-
-# exp_enter_enable_mode
-# Expect sequences to enter enable mode
-# IN:
-# $exp = Expect object
-# $equipmentname = equipment name / id / label
-# $enablepassword = remote enablepassword
-# $promptenable_regex = prompt expected in enable mode
-# OUT:
-# 1 = OK
-# 0 = NOK
-sub exp_enter_enable_mode(){
- my $exp = $_[0];
- my $equipmentname = $_[1];
- my $enablepassword = $_[2];
- my $promptenable_regex = $_[3];
- my $result=0;
-
- $exp->expect(TIMEOUT_GENERIC,[
- "-re",$PROMPT_REGEX,sub{
- $exp->send("enable\r");
- $exp->expect(TIMEOUT_GENERIC,[
- "-re","[^\n]+assword:",sub{
- $exp->send("$enablepassword\r");
- $exp->notransfer(1);
- $exp->expect(TIMEOUT_GENERIC,[
- "-re",$promptenable_regex,sub{
- $exp->notransfer(0);
- $PROMPT_REGEX=$promptenable_regex;
- &log_msg("$equipmentname: entering enable mode successfully");
- $result=1;
- }
- ],[
- "-re","[^\n]+assword:",sub{
- $exp->notransfer(0);
- &log_error("$equipmentname: invalid enable password");
- $exp->hard_close();
- $result=0;
- }
- ],[
- timeout => sub{
- $exp->notransfer(0);
- &log_error("$equipmentname: timeout due to unknown input after enable password has been sent");
- $exp->hard_close();
- $result=0;
- }
- ]);
- }
- ],[
- timeout => sub{
- &log_error("$equipmentname: timeout due to unknown input after enable command");
- $exp->hard_close();
- $result=0;
- }
- ]);
- }
- ],[
- timeout => sub{
- $exp->notransfer(0);
- &log_error("$equipmentname: timeout due to unknown input before sending enable command");
- $exp->hard_close();
- $result=0;
- }
- ]);
- return $result;
-}
-
-# exp_disconnect()
-# Disconnect from equipment
-# IN:
-# $exp = Expect object
-# $equipmenttype = Equipment type (must be supported)
-# $method = Method (must be supported)
-# $parrEquipment = Array containing equipmentname, hostname, username, password, enablepassword
-# OUT:
-# 1 = OK
-# 0 = NOK
-sub exp_disconnect(){
- my $exp = $_[0];
- my $equipmenttype = $_[1];
- my $method = $_[2];
- my $parrEquipment = $_[3];
- my $result;
- if (($equipmenttype eq "posix")||($equipmenttype eq "bluecoat")||($equipmenttype eq "cisco")||($equipmenttype eq "netscreen")){
- $result = &exp_disconnect_generic_exit($exp, @$parrEquipment[0]);
- }elsif (($equipmenttype eq "tippingpoint")){
- $result = &exp_disconnect_generic_quit($exp, @$parrEquipment[0]);
- }else{
- &log_msg("Disconnection for equipmenttype and method combination undefined. (equipmenttype=$equipmenttype method=$method)");
- }
- return !$result;
-}
-
-# exp_disconnect_generic_exit
-# Expect sequences - disconnect from generic system (ie: send "exit")
-# IN:
-# $equipmentname = equipment name / id / label
-# OUT:
-# 1 = OK
-# 0 = NOK
-sub exp_disconnect_generic_exit(){
- my $exp = $_[0];
- my $equipmentname = $_[1];
- my $result=0;
- $exp->expect(TIMEOUT_GENERIC,[
- "-re",$PROMPT_REGEX,sub{
- $exp->send("exit\r");
- $exp->expect(TIMEOUT_GENERIC,[
- "-re","Configuration modified, save?",sub{ #netscreen specific
- &log_msg("$equipmentname: equipment wants to save config, sending no");
- $exp->send("n\r");
- $exp->exp_continue;
- }
- ],[
- "-re","onnection[^\n]+closed",sub{
- &log_msg("$equipmentname: clean disconnect");
- $exp->soft_close();
- $result=1;
- }
- ],[
- timeout => sub{
- &log_error("$equipmentname: timeout due to unknown input after disconnect");
- $exp->soft_close();
- $result=0;
- }
- ]);
- }
- ],[
- timeout => sub{
- log_error("$equipmentname: timeout due to unknown input before disconnect");
- $exp->soft_close();
- $result=0;
- }
- ]);
- return $result;
-}
-
-# exp_disconnect_generic_quit
-# Expect sequences - disconnect from generic system (ie: send "quit")
-# IN:
-# $equipmentname = equipment name / id / label
-# OUT:
-# 1 = OK
-# 0 = NOK
-sub exp_disconnect_generic_quit(){
- my $exp = $_[0];
- my $equipmentname = $_[1];
- my $result=0;
- $exp->expect(TIMEOUT_GENERIC,[
- "-re",$PROMPT_REGEX,sub{
- $exp->send("quit\r");
- $exp->expect(TIMEOUT_GENERIC,[
- timeout => sub{
- &log_error("$equipmentname: timeout due to unknown input after disconnect");
- $exp->soft_close();
- $result=0;
- }
- ]);
- }
- ],[
- timeout => sub{
- log_error("$equipmentname: timeout due to unknown input before disconnect");
- $exp->soft_close();
- $result=0;
- }
- ]);
- return $result;
-}
-
-# exp_send_command
-# Send command to equipment
-# IN:
-# $exp = Expect object
-# $equipmenttype = Type of equipment
-# $equipmentname = Equipment name
-# $command = command to be sent
-# $pcommandresult = pointer to commandresult variable in order to bring back result
-# OUT:
-# 1 = OK
-# 0 = NOK
-sub exp_send_command(){
- my $exp = $_[0];
- my $equipmenttype = $_[1];
- my $equipmentname = $_[2];
- my $command = $_[3];
- my $pcommandresult = $_[4];
- my $result;
- if (($equipmenttype eq "posix")||($equipmenttype eq "bluecoat")||($equipmenttype eq "cisco")||($equipmenttype eq "netscreen")||($equipmenttype eq "tippingpoint")){
- $result = &exp_send_command_generic($exp, $equipmentname, $command, $pcommandresult);
- }else{
- &log_msg("Send command for equipmenttype undefined. (equipmenttype=$equipmenttype)");
- }
- return $result;
-}
-
- sub print_debug_expect {
- my $before = shift;
- my $match = shift;
- my $after = shift;
- print("\n\n>>>>>TESTING-START<<<<<<\n");
- print("\n>>>BEFORE:###".$before."###\n");
- print("\n>>>MATCH:###".$match."###\n");
- print("\n>>>AFTER:###".$after."###\n");
- print("\n>>>>>TESTING-END<<<<<<\n");
- }
-
-# exp_send_command_generic
-# Expect sequences to send command to remote equipment
-# IN:
-# $exp = Expect object
-# $equipmentname = Equipment name
-# $command = command to be sent
-# $pcommandresult = pointer to commandresult variable in order to bring back result
-# OUT:
-# 1 = OK
-# 0 = NOK
-sub exp_send_command_generic(){
- my $exp = $_[0];
- my $equipmentname = $_[1];
- my $command = $_[2];
- my $pcommandresult = $_[3];
- my $result=0;
- my $more = "--- more --- ";
-
- $exp->expect(TIMEOUT_GENERIC,[
- "-re",$PROMPT_REGEX,sub{
- $exp->send("$command\r");
-# $exp->notransfer(1);
- $exp->expect(TIMEOUT_COMMAND,[
- "-re",$PROMPT_REGEX,sub{
-# $exp->notransfer(0);
- log_msg("$equipmentname: command sent successfully (cmd = $command)");
- ${$pcommandresult} .= $exp->before();
-# ${$pcommandresult} .= substr($exp->before(),index($exp->before(),"\n")+1)."\r\n";
-# print("\n\n>>>>>TESTING-START<<<<<<\n");
-# my $before=$exp->before();
-# $before=~s/\r/\\r/g;
-# $before=~s/\n/\\n/g;
-# my $match=$exp->match();
-# $match=~s/\r/\\r/g;
-# $match=~s/\n/\\n/g;
-# my $after=$exp->after();
-# $after=~s/\r/\\r/g;
-# $after=~s/\n/\\n/g;
-# print("\n>>>BEFORE:###".$before."###\n");
-# print("\n>>>MATCH:###".$match."###\n");
-# print("\n>>>AFTER:###".$after."###\n");
-# print("\n>>>>>TESTING-END<<<<<<\n");
- $result=1;
- $exp->send("\r\n"); # sending line break for exit match
- }
- ],[
- "-re",$more,sub{
-# $exp->notransfer(0);
- log_msg("$equipmentname: found --- more --- ");
-# ${$pcommandresult} .= substr($exp->before(),index($exp->before(),"\n")+1);
- ${$pcommandresult} .= $exp->before();
-# &print_debug_expect ($exp->before(), $exp->match(), $exp->after());
- $exp->send(" \n");
-# $exp->notransfer(1);
- $exp->exp_continue;
- }
- ],[
- "-re","onnection[^\n]+closed",sub{
-# $exp->notransfer(0);
- log_msg("$equipmentname: connection closed while waiting for prompt");
- $exp->soft_close();
- $result=1;
- }
-# ],[
-# "-re",$PROMPT_REGEX,sub{
-# $exp->notransfer(0);
-# print("\n\n>>>>>TESTING-START<<<<<<\n");
-# my $before=$exp->before();
-# $before=~s/\r/\\r/g;
-# $before=~s/\n/\\n/g;
-# my $match=$exp->match();
-# $match=~s/\r/\\r/g;
-# $match=~s/\n/\\n/g;
-# my $after=$exp->after();
-# $after=~s/\r/\\r/g;
-# $after=~s/\n/\\n/g;
-# print("\n>>>BEFORE:###".$before."###\n");
-# print("\n>>>MATCH:###".$match."###\n");
-# print("\n>>>AFTER:###".$after."###\n");
-# print("\n>>>>>TESTING-END<<<<<<\n");
-# print("\n\n");
-# $result=0;
-# }
- ],[
- timeout => sub{
-# $exp->notransfer(0);
- log_error("$equipmentname: timeout due to unknown input after command");
-# print("\n>>>PROMPT_REGEX:###".$PROMPT_REGEX."###\n");
-# print("\n>>>BEFORE:###".$exp->before()."###\n");
-# print("\n>>>MATCH:###".$exp->match()."###\n");
-# print("\n>>>AFTER:###".$exp->after()."###\n");
- $exp->soft_close();
- $result=0;
- }
- ]);
- }
- ],[
- timeout => sub{
- log_error("$equipmentname: timeout due to unknown input before command");
- $exp->soft_close();
- $result=0;
- }
- ]);
- return $result;
-}
-
-# fill_arrCommandFile_from_dir()
-# Fill array of commandfiles, outputfiles from the commandirectory
-# IN:
-# $passCommandFileOutputFileFile = Pointer to associative array of commandfiles and outputfiles
-# $commanddir = directory containing commandfiles
-# OUT:
-# 1 = OK
-# 0 = NOK
-sub fill_arrCommandFile_from_dir(){
- my $passCommandFileOutputFileFile = $_[0];
- my $commanddir = $_[1];
- my @arrCommandFile;
- my @arrOutputFileFile;
- my $numcommandfiles = 0;
- my $result = 0;
- opendir(DIR,$commanddir);
- @arrCommandFile = grep {/.*\.C/ && -f "$commanddir/$_"} readdir(DIR);
- closedir(DIR);
- opendir(DIR,$commanddir);
- @arrOutputFileFile = grep {/.*\.O/ && -f "$commanddir/$_"} readdir(DIR);
- closedir(DIR);
- for (@arrCommandFile) {
- $_ = $commanddir."/".$_;
- $_ =~ s/\/\//\//g; # replace // with /
- }
- for (@arrOutputFileFile) {
- $_ = $commanddir."/".$_;
- $_ =~ s/\/\//\//g; # replace // with /
- }
-
- for (@arrCommandFile) {
- my $commandfile = $_;
- my $commandfileprefix = $commandfile;
- my $outputfilematched = "";
- $commandfileprefix =~ s/.C$//g;
- for (@arrOutputFileFile) {
- my $outputfile = $_;
- my $outputfileprefix = $outputfile;
- $outputfileprefix =~ s/.O$//g;
- if ($commandfileprefix eq $outputfileprefix){
- $outputfilematched = $outputfile;
- last;
- }
- }
- $$passCommandFileOutputFileFile{$commandfile} = $outputfilematched;
- }
- $numcommandfiles = keys(%$passCommandFileOutputFileFile);
- if ($numcommandfiles ne ""){
- $result = 1;
- }
- return $result;
-}
-
-# get_directory()
-# Returns the directory part of a fullpath
-# IN:
-# $fullpath = path to file
-# OUT:
-# $directory = directory containing $fullpath
-sub get_directory(){
- my $fullpath = $_[0];
- my $directory = $fullpath;
- $directory =~ s/[^\/]*$//g;
- return $directory;
-}
-
-
-# get_timestamp()
-# Return timestamp
-# IN:
-# $msg = error message
-sub get_timestamp(){
- return (strftime "%Y%m%d %H:%M:%S", localtime);
-}
-
-
-# is_directory()
-# Returns true if $directory is a real directory
-# IN:
-# $directory = directory to verify
-# OUT:
-# 1 = TRUE
-# 0 = FALSE
-sub is_directory(){
- my $directory = $_[0];
- my $result = 0;
- if (opendir(DIR,$directory)){
- $result = 1;
- closedir(DIR);
- }
- return $result;
-}
-
-
-
-# log_error()
-# Log error message using default settings
-# IN:
-# $msg = error message
-sub log_error(){
- my $msg = $_[0];
- my $timestamp = &get_timestamp();
- if ($DISPLAYLEVEL>=1 && $DISPLAYLEVEL<=4){
- print("[LOGFILE: $timestamp $msg]\n");
- }elsif ($DISPLAYLEVEL>=5){
- print("\n[LOGFILE: $timestamp $msg]\n");
- }
- if ($LOGFILE ne ""){
- &append_string_to_file($LOGFILE, "$timestamp $msg\n");
- }
-}
-
-# log_msg()
-# Log message using default settings
-# IN:
-# $msg = error message
-sub log_msg(){
- my $msg = $_[0];
- my $timestamp = &get_timestamp();
- if ($DISPLAYLEVEL==4){
- print("[LOGFILE: $timestamp $msg]\n");
- }elsif ($DISPLAYLEVEL>=5){
- print("\n[LOGFILE: $timestamp $msg]\n");
- }
- if ($LOGFILE ne ""){
- &append_string_to_file($LOGFILE, "$timestamp $msg\n");
- }
-}
-
-sub normalize_commandresult {
- my $pinput = $_[0];
- my $prompt = $_[1];
- my $result = '';
- my $line_count = 0;
- foreach my $line (split(/\n/, ${$pinput})) {
- $line =~ s/\r//g;
- $line =~ s/\s+$//;
- if ($line =~ /(\010)+(.*?)$/) { $line = $2;}
- if ($line_count>0 && $line !~ /^.+?\(\w\)\-\>/ && $line !~ /^\-+$/ && $line !~ /^--- more/) { $result .= "$line\n"; }
- # remove first line which contains the command
- # remove trailing line if it contains prompt
- # remove trailing line if it contains --------------------
- # remore --- more relict which only happens if last line contains more when paging config
- $line_count++;
- }
- ${$pinput} = $result;
-}
-
-# process_commandresult()
-# Display to stdout depending of settings and send to outputfile
-# IN:
-# $parrEquipment = array containing equipmentname, hostname, username, password, enablepassword
-# $command = command sent to equipment
-# $commandresult = command result
-# $outputfile = file to put command result in
-# $outputfileappend = if true then append instead of writing.
-# OUT:
-# 1 = OK
-# 0 = NOK
-sub process_commandresult(){
- my $parrEquipment = $_[0];
- my $command = $_[1];
- my $commandresult = $_[2];
- $commandresult =~ s/\r\r\n\r\n\r\n\r/\r\n/g; #special replacement for tippingpoint columns and rows display bug
- my $outputfile = $_[3];
- my $outputfileappend = $_[4];
- if ($DISPLAYLEVEL==0){
- }elsif ($DISPLAYLEVEL==1){
- print($commandresult);
- }elsif ($DISPLAYLEVEL==2){
- print(@$parrEquipment[0]." ".$commandresult);
- }elsif ($DISPLAYLEVEL>=3){
- print("--------------------------------------------------------------\n");
- print("EQUIPMENT: ".@$parrEquipment[0]."\n");
- print("COMMAND: ".$command."\n");
- print("RESULT: ".$commandresult);
- if ($outputfile ne ""){
- print("OUTPUTFILE: ".$outputfile."\n");
- }
- print("--------------------------------------------------------------\n");
- print("\n");
- }
- if ($outputfile ne ""){
- my $directory = &get_directory($outputfile);
- if ($directory ne "" && !&is_directory($directory)){
- &log_msg("@$parrEquipment[0]: creating $directory because it does not exist");
- mkpath($directory);
- }
- #if outputfileappend is true then we append to existing files
- if ($outputfileappend){
- &log_msg("@$parrEquipment[0]: appending command result to output file $outputfile");
- &append_string_to_file($outputfile, $commandresult);
-
- } else {
- &log_msg("@$parrEquipment[0]: writing command result to output file $outputfile");
- &write_string_to_file($outputfile, $commandresult);
- }
- }
-}
-
-# show_help()
-# Print out usage with complete details
-sub show_help() {
- show_version();
- show_usage();
- print "Parameter details:
--i inputfile : File containing the list of equipment with credentials. Expected format:
- equipmentname1 hostname1 username1 password1 enablepassword1
- equipmentname2 hostname2 username2 password2 enablepassword2
- (...)
--t equipmenttype : Type of equipment. Valid values: posix, bluecoat, cisco, netscreen, tippingpoint.
--c command : Command to run on remote equipment.
--C commandir : Directory containing many commands to be run in one connection on a remote equipment. It must have the following content:
- Files named *.C : Contain command(s) to run.
- Files named *.O : Contain name of output file for corresponding commands.
--m method : Method used to access equipment. Valid values: ssh, telnet. Default is ssh.
--p port : Specify port in order to access equipment. Default is using standard port for service.
--r numretry : Number of times the script will try to connect to the equipment on timeout. Default is ".NBTRY."
--d displaylevel : Level of information displayed on stdout. Expected level are:
- 0 : Nothing displayed.
- 1 : Result of command displayed. DEFAULT.
- 2 : Equipment's name and command results displayed on one line.
- 3 : Equipment's name, command and command results all displayed on multiple lines.
- 4 : Equipment's name, command and command results all displayed on multiple lines. Logs displayed.
- 5 : Equipment's name, command and command results all displayed on multiple lines. Logs displayed. Session logs displayed.
--o outputfile : File where results of command will be saved.
--a : append to output file instead of writing new content.
--l logfile : File where logs will be saved.
--L sessionlogfile : File where session logs (the entire remote session) will be saved.
--v : show version.
--h : show this help.
-
-User variables:
-It is possible to use variables in the output file and in the command string. Use the following syntax:
- \%VAR0\% = equipmentname
- \%VAR1\% = hostname
- (...)
- \%VAR".MAXVAR."\% = custom variables
-
-Concrete examples:
- ".PROGRAM." -i login.txt -t posix -m ssh -c \"uptime\"
- ".PROGRAM." -i login.txt -t posix -m ssh -c \"uptime\" -d 0 -o \"%VAR0%-uptime.txt\"
-
-";
-}
-
-# show_usage()
-# Print out usage
-sub show_usage() {
- print("usage: ".PROGRAM." -z zielhost -u username -t equipmenttype -i identity-file -c command [-C commanddir] [-m method] [-p port] [-r numretry] [-d displaylevel] [-o outputfile] [-a] [-l logfile] [-L sessionlogfile] [-v] [-h]\n\n");
-}
-
-# show_version()
-# Print out usage
-sub show_version() {
- print(PROGRAM." v".VERSION." by Youri Reddy-Girard http://sendcommand.sourceforge.net/\n\n");
-}
-
-# validate_arguments()
-# Validate arguments received by command line argument
-# IN:
-# $inputfile = Filename containing the list of equipment with credentials.
-# $equipmenttype = Type of equipment (posix, bluecoat, cisco or netscreen).
-# $command = Command to run on remote equipment.
-# $commanddir = Directory containing commands and output files.
-# $method = Method used to access equipment (ssh, telnet).
-# $port = Port to connect to
-# $numretry = Number of times the script will try to connect to the equipment on timeout
-# $displaylevel = Level on information displayed on stdout.
-# $outputfile = File that will contain the result of the command.
-# $outputfileappend = Append to output file instead of writing new content.
-# $logfile = File where logging will take place.
-# $sessionlogfile = File where session logging will take place.
-# $version = if variable defined, show version.
-# $help = if variable defined, show help.
-# OUT:
-# 1 = OK
-# 0 = NOK
-sub validate_arguments(){
- my $inputfile = $_[0];
- my $equipmenttype = $_[1];
- my $command = $_[2];
- my $commanddir = $_[3];
- my $method = $_[4];
- my $port = $_[5];
- my $numretry = $_[6];
- my $displaylevel = $_[7];
- my $outputfile = $_[8];
- my $outputfileappend = $_[9];
- my $logfile = $_[10];
- my $sessionlogfile = $_[11];
- my $version = $_[12];
- my $help = $_[13];
- my $result = 1;
- if ($inputfile eq ""){
- printf("ERROR - missing inputfile.\n");
- $result = 0;
- }
- if ($equipmenttype eq ""){
- printf("ERROR - missing equipment type.\n");
- $result = 0;
- }elsif ( $equipmenttype ne "posix" && $equipmenttype ne "bluecoat" && $equipmenttype ne "cisco" && $equipmenttype ne "netscreen" && $equipmenttype ne "tippingpoint"){
- printf("ERROR - Unknown equipment type. Valid values are posix, bluecoat, cisco, netscreen and tippingpoint.\n");
- $result = 0;
- }
- if (($method ne "ssh") && ($method ne "telnet")){
- printf("ERROR - unreconized method $method.\n");
- $result = 0;
- }
- if (($port ne "")&&(!(($port>0)&&($port<65535)))){
- printf("ERROR - port must be numeric between 0 and 65534.\n");
- $result = 0;
- }
- if (($command eq "") && ($commanddir eq "")){
- printf("ERROR - missing command.\n");
- $result = 0;
- }
- if ($commanddir ne ""){
- if (&is_directory($commanddir)){
- }else{
- printf("ERROR - commanddir $commanddir is not a directory.\n");
- $result = 0;
- }
- }
- return $result;
-}
-
-# validate_equipment()
-# Validate equipment input file
-# IN:
-# equipmenttype : Type of equipment (posix, bluecoat, ...)
-# arrEquipment : Array containing equipmentname, hostname, username, password, enablepassword
-# OUT:
-# 1 = OK
-# 0 = NOK
-sub validate_equipment(){
- my $equipmenttype = $_[0];
- my $parrEquipment = $_[1];
- my $result = 0;
- my $size = @$parrEquipment;
- #if (($equipmenttype eq "posix") && ($size==3 || $size==4)){
- if (($equipmenttype eq "posix") && ($size>=3)){
- $result = 1;
- #}elsif ($equipmenttype eq "bluecoat" && $size==5){
- }elsif ($equipmenttype eq "bluecoat" && $size>=5){
- $result = 1;
- #}elsif ($equipmenttype eq "cisco" && $size==5){
- }elsif ($equipmenttype eq "cisco" && $size>=5){
- $result = 1;
- #}elsif ($equipmenttype eq "netscreen" && $size==4){
- }elsif ($equipmenttype eq "netscreen" && $size>=4){
- $result = 1;
- #}elsif ($equipmenttype eq "tippingpoint" && $size==4){
- }elsif ($equipmenttype eq "tippingpoint" && $size>=4){
- $result = 1;
- }else{
- $result = 0;
- }
- return $result;
-}
-
-# write_array_to_file()
-# Write an array to a file
-# IN:
-# $file = filename
-# $parr = pointer to array to write
-# OUT:
-# 1 = OK
-# 0 = NOK
-sub write_array_to_file(){
- my $file = $_[0];
- my $parr = $_[1];
- my $result = 0;
- if (open(FILE, ">", $file)){
- if (print FILE @$parr){
- $result = 1;
- }
- close(FILE);
- }
-}
-
-# write_string_to_file()
-# Write a string to a file
-# IN:
-# $file = filename
-# $string = string to write
-# OUT:
-# 1 = OK
-# 0 = NOK
-sub write_string_to_file(){
- my $file = $_[0];
- my $string = $_[1];
- my $result = 0;
- if (open(FILE, ">", $file)){
- if (print FILE $string){
- $result = 1;
- }
- close(FILE);
- }
-}
-
-sub get_prompt {
- my $equipmenttype = shift;
-
- if ($equipmenttype eq "netscreen") { return PROMPT_REGEX_NETSCREEN; }
- if ($equipmenttype eq "bluecoat") { return PROMPT_REGEX_BLUECOAT; }
- return PROMPT_REGEX_POSIX;
-}
-
-#############################################################
-# MAIN PROGRAM
-#############################################################
-# (command line arguments : -i inputfile -t equipmenttype -c command [-C commanddir] [-m method] [-p port] [-r numretry] [-d displaylevel] [-o outputfile] [-l logfile] [-L sessionlogfile])
- my $inputfile = "";
- my $equipmenttype = "netscreen";
- my $command = "";
- my $commandformatted = "";
- my $commanddir = "";
- my $method = METHOD;
- my $port = "";
- my $numretry = NBTRY;
- my $displaylevel = DISPLAYLEVEL;
- my $outputfile = "";
- my $outputfileformatted = "";
- my $outputfileappend = OUTPUTFILEAPPEND;
- my $outputfileappendcontext = "";
- my $logfile = "";
- my $sessionlogfile = "";
- my $version = "";
- my $help = "";
- my $identity_file = "";
- my $username= "";
- my $zielhost = "";
- my $return_code = 0;
- my $connect_check = 0;
- my $send_command_check = 0;
-
- #Get command line arguments
- GetOptions("i:s"=>\$identity_file, "z:s"=>\$zielhost, "u:s"=>\$username, "t:s"=>\$equipmenttype, "c:s"=>\$command,
- "C:s"=>\$commanddir, "m:s"=>\$method, "p:i"=>\$port, "n:i"=>\$numretry, "d:i"=>\$displaylevel, "o:s"=>\$outputfile, "a"=>\$outputfileappend,
- "l:s"=>\$logfile, "v" => \$version, "L:s"=>\$sessionlogfile, "help" => \$help, "h" => \$help);
- my $prompt = &get_prompt($equipmenttype);
-
- if ($help){ show_help(); exit 0; } #if -h is specified
- if ($version){ show_version(); exit 0; } #if -v is specified
- #validate command line arguments
- if (!&validate_arguments("dummy_input_file", $equipmenttype, $command, $commanddir, $method, $port, $numretry, $displaylevel, $outputfile, $outputfileappend, $logfile, $sessionlogfile, $version, $help)){
- show_usage();
- exit 1;
- }
- #Fill global variables
- $LOGFILE = $logfile;
- $DISPLAYLEVEL = $displaylevel;
- my @arrEquipment = ($zielhost, $zielhost, $username, 'dummy_password', 'dummy_enable_password');
- if(&validate_equipment($equipmenttype, \@arrEquipment)){
- my $iTry=0;
- my $bConnected=0;
- while($iTry<$numretry && $bConnected==0){
- my $objExpect = Expect->new();
- if ($sessionlogfile ne ""){
- $objExpect->log_file($sessionlogfile);
- }
- if ($DISPLAYLEVEL>=5){
- $objExpect->log_user(1);
- }
- $bConnected=&exp_connect($objExpect, $equipmenttype, $method, $port, \@arrEquipment, $identity_file);
- if ($bConnected) {
- if ($command ne ""){
- #replace user variables
- $commandformatted = $command;
- $outputfileformatted = $outputfile;
- foreach my $i(0..MAXVAR) {
- $commandformatted =~ s/%VAR$i%/$arrEquipment[$i]/g;
- $outputfileformatted =~ s/%VAR$i%/$arrEquipment[$i]/g;
- }
- my $commandresult = "";
- $send_command_check = &exp_send_command($objExpect, $equipmenttype, $arrEquipment[0], $commandformatted, \$commandresult);
- if ($send_command_check){
- &normalize_commandresult(\$commandresult, $prompt);
- &process_commandresult(\@arrEquipment, $commandformatted, $commandresult, $outputfileformatted, $outputfileappend);
- $return_code = 0;
- } else{
- &log_msg("$arrEquipment[0]: Error when sending command. (command=$commandformatted)");
- $return_code = 1;
- }
- }
- if (&exp_disconnect($objExpect, $equipmenttype, $method, \@arrEquipment)) { $return_code = 1; }
- }
- $iTry++;
- }
- $return_code = $return_code || (!$bConnected);
- } else { $return_code = 1; }
- exit ($return_code);
\ No newline at end of file
diff --git a/roles/importer/files/importer/test/__init__.py b/roles/importer/files/importer/test/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/roles/importer/files/importer/test/mocking/__init__.py b/roles/importer/files/importer/test/mocking/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/roles/importer/files/importer/test/mocking/mock_config.py b/roles/importer/files/importer/test/mocking/mock_config.py
new file mode 100644
index 0000000000..5bdb3b8514
--- /dev/null
+++ b/roles/importer/files/importer/test/mocking/mock_config.py
@@ -0,0 +1,600 @@
+import secrets
+
+from fwo_const import DUMMY_IP, LIST_DELIMITER, RULE_NUM_NUMERIC_STEPS, USER_DELIMITER
+from models.fwconfig_normalized import FwConfigNormalized
+from models.gateway import Gateway
+from models.networkobject import NetworkObject
+from models.rule import RuleAction, RuleNormalized, RuleTrack, RuleType
+from models.rulebase import Rulebase
+from models.rulebase_link import RulebaseLinkUidBased
+from models.serviceobject import ServiceObject
+from netaddr import IPNetwork
+from pydantic import PrivateAttr
+
+from .uid_manager import UidManager
+
+DUMMY_IP = IPNetwork(DUMMY_IP)
+
+
+class MockFwConfigNormalizedBuilder:
+ """
+ A mock subclass of FwConfigNormalized for testing purposes.
+
+ This class creates a fake firewall configuration including rulebases
+ and rules, using a UID manager to generate unique identifiers.
+ """
+
+ _uid_manager: UidManager = PrivateAttr()
+ _seed: int = 42 # Seed for reproducibility in tests
+
+ def __init__(self):
+ """
+ Initializes the mock configuration builder with an internal UID manager.
+ """
+ self.set_up()
+
+ @property
+ def uid_manager(self) -> UidManager:
+ """Returns the internal UID manager."""
+ return self._uid_manager
+
+ @staticmethod
+ def empty_config() -> FwConfigNormalized:
+ """
+ Returns an empty FwConfigNormalized object with no rulebases or rules.
+ """
+ return FwConfigNormalized(
+ action="INSERT", # type: ignore
+ gateways=[],
+ network_objects={},
+ service_objects={},
+ users={},
+ rulebases=[],
+ )
+
+ def set_up(self):
+ """
+ Sets up config builder to an initial state.
+ """
+ self._uid_manager = UidManager()
+
+ def build_config(self, mock_config: dict):
+ """
+ Initializes the mock configuration with rulebases and rules.
+
+ Args:
+ mock_config (dict): A dictionary with configuration values. Expected keys:
+ - "rule_config": list of integers, each representing the number of rules per rulebase.
+ - "initialize_rule_num_numeric": optional boolean, if True assigns incremental numeric rule numbers.
+
+ """
+ config = self.empty_config()
+
+ mock_mgm_uid = self.uid_manager.create_uid()
+ created_rule_counter = 1
+
+ # Add network objects.
+
+ # TODO: test various object types
+ # obj_types = [entry["obj_typ_name"] for entry in mock_fwo_api_oo.STM_TABLES["stm_obj_typ"]]
+
+ for index in range(mock_config["network_object_config"]):
+ new_network_object_uid = self.uid_manager.create_uid()
+
+ new_network_object = NetworkObject(
+ obj_uid=new_network_object_uid,
+ obj_ip=DUMMY_IP,
+ obj_ip_end=DUMMY_IP,
+ obj_name=f"Network Object {index}",
+ obj_color="black",
+ obj_typ="group",
+ )
+
+ config.network_objects[new_network_object_uid] = new_network_object
+
+ # Add users.
+
+ for index in range(mock_config["user_config"]):
+ new_user_uid = self.uid_manager.create_uid()
+
+ new_user_obj = NetworkObject(
+ obj_uid=new_user_uid,
+ obj_ip=DUMMY_IP,
+ obj_ip_end=DUMMY_IP,
+ obj_name=f"IA_{index}",
+ obj_color="black",
+ obj_typ="access-role",
+ )
+
+ new_user = {"user_uid": new_user_uid, "user_name": f"IA_{index}", "user_typ": "simple"}
+
+ config.network_objects[new_user_uid] = new_user_obj
+ config.users[new_user_uid] = new_user
+
+ # Add services.
+
+ for index in range(mock_config["service_config"]):
+ new_service_uid = self.uid_manager.create_uid()
+
+ new_service = ServiceObject(
+ svc_uid=new_service_uid, svc_name=f"Service {index}", svc_color="black", svc_typ="group"
+ )
+
+ config.service_objects[new_service_uid] = new_service
+
+ # Add rules and rulebases.
+
+ for number_of_rules in mock_config["rule_config"]:
+ # Create a new rulebase.
+
+ new_rulebase = self.add_rulebase(config, mock_mgm_uid)
+
+ for _i in range(number_of_rules):
+ # Add a new rule to the rulebase.
+
+ new_rule = self.add_rule(config, new_rulebase.uid)
+ self.add_references_to_rule(config, new_rule)
+
+ if mock_config.get("initialize_rule_num_numeric"):
+ new_rule.rule_num_numeric = created_rule_counter * RULE_NUM_NUMERIC_STEPS
+ created_rule_counter += 1
+
+ # Add Gateway.
+
+ gateway_uid = (
+ self.uid_manager.create_uid() if not mock_config.get("gateway_uid") else mock_config["gateway_uid"]
+ )
+ gateway_name = f"Gateway {gateway_uid}" if not mock_config.get("gateway_name") else mock_config["gateway_name"]
+ config.gateways.append(
+ Gateway(Uid=gateway_uid, Name=gateway_name, RulebaseLinks=self.create_rulebase_links(config))
+ )
+
+ return config, mock_mgm_uid
+
+ def add_rule_with_nested_groups(self, config: FwConfigNormalized):
+ """
+ Adds a rule with highest possible reference complexity for testing correctness
+ of reference imports
+ """
+ rulebase = config.rulebases[0]
+ config_objs = {
+ "rules": [
+ {
+ "rule_uid": "cpr",
+ "rule_src": f"cpr-from1{LIST_DELIMITER}cpr-from2",
+ "rule_src_refs": f"cpr-from1{LIST_DELIMITER}cpr-from2",
+ "rule_dst": "cpr-to1",
+ "rule_dst_refs": "cpr-to1",
+ "rule_svc": "cpr-svc1",
+ "rule_svc_refs": "cpr-svc1",
+ }
+ ],
+ "network_objects": [
+ {
+ "obj_uid": "cpr-from1",
+ "obj_name": "cpr-from1",
+ "obj_member_names": f"cpr-from1-member1{LIST_DELIMITER}cpr-from1-member2",
+ "obj_member_refs": f"cpr-from1-member1{LIST_DELIMITER}cpr-from1-member2",
+ },
+ {
+ "obj_uid": "cpr-from1-member1",
+ "obj_name": "cpr-from1-member1",
+ "obj_member_names": f"cpr-from1-member1-member1{LIST_DELIMITER}cpr-from1-member1-member2",
+ "obj_member_refs": f"cpr-from1-member1-member1{LIST_DELIMITER}cpr-from1-member1-member2",
+ },
+ {
+ "obj_uid": "cpr-from1-member2",
+ "obj_name": "cpr-from1-member2",
+ "obj_member_names": "cpr-from1-member1-member2",
+ "obj_member_refs": "cpr-from1-member1-member2",
+ },
+ {
+ "obj_uid": "cpr-from1-member1-member1",
+ "obj_name": "cpr-from1-member1-member1",
+ "obj_typ": "host",
+ },
+ {
+ "obj_uid": "cpr-from1-member1-member2",
+ "obj_name": "cpr-from1-member1-member2",
+ "obj_typ": "host",
+ },
+ {
+ "obj_uid": "cpr-from2",
+ "obj_name": "cpr-from2",
+ "obj_member_names": "cpr-from1-member2",
+ "obj_member_refs": "cpr-from1-member2",
+ },
+ {"obj_uid": "cpr-to1", "obj_name": "cpr-to1", "obj_typ": "host"},
+ ],
+ "service_objects": [
+ {
+ "svc_uid": "cpr-svc1",
+ "svc_name": "cpr-svc1",
+ "svc_member_names": "cpr-svc1-member1",
+ "svc_member_refs": "cpr-svc1-member1",
+ },
+ {
+ "svc_uid": "cpr-svc1-member1",
+ "svc_name": "cpr-svc1-member1",
+ },
+ ],
+ }
+
+ for rule in config_objs["rules"]:
+ self.add_rule(config, rulebase.uid, rule)
+ for obj in config_objs["network_objects"]:
+ self.add_network_object(config, obj)
+ for svc in config_objs["service_objects"]:
+ self.add_service_object(config, svc)
+
+ def change_rule_with_nested_groups(
+ self, config: FwConfigNormalized, change_type: str = "change", change_obj: str = "from"
+ ):
+ """
+ Changes a rule with nested groups in a subtle way to test if the import process detects changes correctly.
+
+ Args:
+ config (FwConfigNormalized): The configuration to change.
+ change_type (str): The type of change to apply. Can be "change", "add", or "remove".
+ change_obj (str): The object to change. Can be "from", "svc", "member", "member_svc", "nested_member", or "nested_member_svc".
+
+ """
+ rulebase = config.rulebases[0]
+ rule = rulebase.rules.get("cpr")
+ if not rule:
+ raise ValueError("Rule 'cpr' not found in the rulebase.")
+ if change_type == "change":
+ if change_obj == "from":
+ self.change_network_object_subtle(config, "cpr-from1")
+ elif change_obj == "svc":
+ self.change_service_object_subtle(config, "cpr-svc1")
+ elif change_obj == "member":
+ self.change_network_object_subtle(config, "cpr-from1-member1")
+ elif change_obj == "member_svc":
+ self.change_service_object_subtle(config, "cpr-svc1-member1")
+ elif change_obj == "nested_member":
+ self.change_network_object_subtle(config, "cpr-from1-member1-member1")
+ elif change_type == "add":
+ self._add_rule_with_nested_groups(config, rule, change_obj)
+
+ elif change_type == "remove":
+ self._remove_rule_with_nested_groups(config, rule, rulebase, change_obj)
+
+ def _add_rule_with_nested_groups(self, config: FwConfigNormalized, rule, change_obj):
+ if change_obj == "from":
+ self.add_network_object(config, {"obj_uid": "cpr-from-new", "obj_name": "cpr-from-new", "obj_typ": "host"})
+ rule.rule_src += LIST_DELIMITER + "cpr-from-new"
+ rule.rule_src_refs += LIST_DELIMITER + "cpr-from-new"
+ elif change_obj == "svc":
+ self.add_service_object(config, {"svc_uid": "cpr-svc-new", "svc_name": "cpr-svc-new", "svc_typ": "simple"})
+ rule.rule_svc += LIST_DELIMITER + "cpr-svc-new"
+ rule.rule_svc_refs += LIST_DELIMITER + "cpr-svc-new"
+ elif change_obj == "member":
+ from_obj = config.network_objects.get("cpr-from1")
+ self.add_network_object(
+ config, {"obj_uid": "cpr-from-member-new", "obj_name": "cpr-from-member-new", "obj_typ": "host"}
+ )
+ from_obj.obj_member_names += LIST_DELIMITER + "cpr-from-member-new"
+ from_obj.obj_member_refs += LIST_DELIMITER + "cpr-from-member-new"
+ elif change_obj == "member_svc":
+ svc_obj = config.service_objects.get("cpr-svc1")
+ self.add_service_object(
+ config, {"svc_uid": "cpr-svc-member-new", "svc_name": "cpr-svc-member-new", "svc_typ": "simple"}
+ )
+ svc_obj.svc_member_names += LIST_DELIMITER + "cpr-svc-member-new"
+ svc_obj.svc_member_refs += LIST_DELIMITER + "cpr-svc-member-new"
+ elif change_obj == "nested_member":
+ member = config.network_objects.get("cpr-from1-member1")
+ self.add_network_object(
+ config,
+ {
+ "obj_uid": "cpr-from-member1-member-new",
+ "obj_name": "cpr-from-member1-member-new",
+ "obj_typ": "host",
+ },
+ )
+ member.obj_member_names += LIST_DELIMITER + "cpr-from-member1-member-new"
+ member.obj_member_refs += LIST_DELIMITER + "cpr-from-member1-member-new"
+ elif change_obj == "nested_member_svc":
+ member_svc = config.service_objects.get("cpr-svc1-member1")
+ self.add_service_object(
+ config,
+ {
+ "svc_uid": "cpr-svc-member1-member-new",
+ "svc_name": "cpr-svc-member1-member-new",
+ "svc_typ": "simple",
+ },
+ )
+ member_svc.svc_member_names = "cpr-svc-member1-member-new"
+ member_svc.svc_member_refs = "cpr-svc-member1-member-new"
+
+ def _remove_rule_with_nested_groups(self, config: FwConfigNormalized, rule, rulebase, change_obj):
+ """
+ Removes a rule with nested groups from the configuration.
+
+ Args:
+ config (FwConfigNormalized): The configuration to modify.
+ rule (RuleNormalized): The rule to remove.
+ rulebase (Rulebase): The rulebase containing the rule.
+
+ """
+ if change_obj == "from":
+ rule.rule_src = rule.rule_src.replace("cpr-from1", "").strip(LIST_DELIMITER)
+ rule.rule_src_refs = rule.rule_src_refs.replace("cpr-from1", "").strip(LIST_DELIMITER)
+ elif change_obj == "svc":
+ rule.rule_svc = rule.rule_svc.replace("cpr-svc1", "").strip(LIST_DELIMITER)
+ rule.rule_svc_refs = rule.rule_svc_refs.replace("cpr-svc1", "").strip(LIST_DELIMITER)
+ elif change_obj == "member":
+ from_obj = config.network_objects.get("cpr-from1")
+ from_obj.obj_member_names = from_obj.obj_member_names.replace("cpr-from1-member1", "").strip(LIST_DELIMITER)
+ from_obj.obj_member_refs = from_obj.obj_member_refs.replace("cpr-from1-member1", "").strip(LIST_DELIMITER)
+ elif change_obj == "member_svc":
+ svc_obj = config.service_objects.get("cpr-svc1")
+ svc_obj.svc_member_names = svc_obj.svc_member_names.replace("cpr-svc1-member1", "").strip(LIST_DELIMITER)
+ svc_obj.svc_member_refs = svc_obj.svc_member_refs.replace("cpr-svc1-member1", "").strip(LIST_DELIMITER)
+ elif change_obj == "nested_member":
+ from_obj = config.network_objects.get("cpr-from1-member1")
+ from_obj.obj_member_names = from_obj.obj_member_names.replace("cpr-from1-member1-member2", "").strip(
+ LIST_DELIMITER
+ )
+ from_obj.obj_member_refs = from_obj.obj_member_refs.replace("cpr-from1-member1-member2", "").strip(
+ LIST_DELIMITER
+ )
+ elif change_obj == "nested_member_svc":
+ svc_obj = config.service_objects.get("cpr-svc1-member1")
+ svc_obj.svc_member_names = svc_obj.svc_member_names.replace("cpr-svc1-member1-member1", "").strip(
+ LIST_DELIMITER
+ )
+ svc_obj.svc_member_refs = svc_obj.svc_member_refs.replace("cpr-svc1-member1-member1", "").strip(
+ LIST_DELIMITER
+ )
+
+ def create_rulebase_links(self, config: FwConfigNormalized) -> list[RulebaseLinkUidBased]:
+ rulebase_links = []
+
+ # Add initial link.
+
+ initial_rulebase_uid = config.rulebases[0].uid
+ rulebase_links.append(
+ RulebaseLinkUidBased(
+ from_rulebase_uid=None,
+ from_rule_uid=None,
+ to_rulebase_uid=initial_rulebase_uid,
+ link_type="ordered",
+ is_initial=True,
+ is_global=False,
+ is_section=False,
+ )
+ )
+
+ # Add remaining links.
+
+ for index, _rulebase in enumerate(config.rulebases):
+ # Skip first rulebase.
+
+ if index == 0:
+ continue
+
+ # Create ordered rulebase link.
+
+ current_from_rulebase_uid = config.rulebases[index - 1].uid
+ current_from_rule_uid = list(config.rulebases[index - 1].rules.values())[-1].rule_uid
+ rulebase_links.append(
+ RulebaseLinkUidBased(
+ from_rulebase_uid=current_from_rulebase_uid,
+ from_rule_uid=current_from_rule_uid,
+ to_rulebase_uid=config.rulebases[index].uid,
+ link_type="ordered",
+ is_initial=False,
+ is_global=False,
+ is_section=False,
+ )
+ )
+
+ return rulebase_links
+
+ def add_network_object(self, config: FwConfigNormalized, obj_dict: dict):
+ uid = obj_dict.get("obj_uid", self.uid_manager.create_uid())
+ dummy_ip = DUMMY_IP if obj_dict.get("obj_typ", "group") != "group" else None
+ new_network_object = NetworkObject(
+ obj_uid=uid,
+ obj_ip=obj_dict.get("obj_ip", dummy_ip),
+ obj_ip_end=obj_dict.get("obj_ip_end", dummy_ip),
+ obj_name=obj_dict.get("obj_name", f"Network Object {uid}"),
+ obj_color=obj_dict.get("obj_color", "black"),
+ obj_typ=obj_dict.get("obj_typ", "group"),
+ obj_member_names=obj_dict.get("obj_member_names", ""),
+ obj_member_refs=obj_dict.get("obj_member_refs", ""),
+ )
+ config.network_objects[new_network_object.obj_uid] = new_network_object
+ return new_network_object
+
+ def add_service_object(self, config: FwConfigNormalized, svc_dict: dict):
+ uid = svc_dict.get("svc_uid", self.uid_manager.create_uid())
+ default_port = 80 if svc_dict.get("svc_typ", "group") != "group" else None
+ default_proto = 6 if svc_dict.get("svc_typ", "group") != "group" else None
+ new_service_object = ServiceObject(
+ svc_uid=uid,
+ svc_name=svc_dict.get("svc_name", f"Service {uid}"),
+ svc_color=svc_dict.get("svc_color", "black"),
+ svc_typ=svc_dict.get("svc_typ", "group"),
+ svc_port=svc_dict.get("svc_port", default_port),
+ svc_port_end=svc_dict.get("svc_port_end", default_port),
+ ip_proto=svc_dict.get("ip_proto", default_proto),
+ svc_member_names=svc_dict.get("svc_member_names", ""),
+ svc_member_refs=svc_dict.get("svc_member_refs", ""),
+ )
+ config.service_objects[new_service_object.svc_uid] = new_service_object
+ return new_service_object
+
+ def add_rule(self, config: FwConfigNormalized, rulebase_uid: str, rule_dict: dict | None = None) -> RuleNormalized:
+ """
+ Adds a new rule to the rulebase identified by the given UID.
+
+ Args:
+ rulebase_uid (str): The UID of the rulebase to which the rule should be added.
+
+ Returns:
+ Rule: The newly created rule.
+
+ """
+ if rule_dict is None:
+ rule_dict = {}
+ rulebase = next(rb for rb in config.rulebases if rb.uid == rulebase_uid)
+
+ src_objs, dst_objs, svc_objs = [], [], []
+ if "rule_src" not in rule_dict:
+ src_objs = secrets.SystemRandom(self._seed).sample(
+ list(config.network_objects.values()), k=secrets.SystemRandom(self._seed).randint(1, 5)
+ )
+ if "rule_dst" not in rule_dict:
+ dst_objs = secrets.SystemRandom(self._seed).sample(
+ list(config.network_objects.values()), k=secrets.SystemRandom(self._seed).randint(1, 5)
+ )
+ if "rule_svc" not in rule_dict:
+ svc_objs = secrets.SystemRandom(self._seed).sample(
+ list(config.service_objects.values()), k=secrets.SystemRandom(self._seed).randint(1, 5)
+ )
+ uid = rule_dict.get("rule_uid", self.uid_manager.create_uid())
+ new_rule = RuleNormalized(
+ rule_disabled=rule_dict.get("rule_disabled", False),
+ rule_src_neg=rule_dict.get("rule_src_neg", False),
+ rule_src=rule_dict.get("rule_src", LIST_DELIMITER.join(obj.obj_name for obj in src_objs)),
+ rule_src_refs=rule_dict.get("rule_src_refs", LIST_DELIMITER.join(obj.obj_uid for obj in src_objs)),
+ rule_dst_neg=rule_dict.get("rule_dst_neg", False),
+ rule_dst=rule_dict.get("rule_dst", LIST_DELIMITER.join(obj.obj_name for obj in dst_objs)),
+ rule_dst_refs=rule_dict.get("rule_dst_refs", LIST_DELIMITER.join(obj.obj_uid for obj in dst_objs)),
+ rule_svc_neg=rule_dict.get("rule_svc_neg", False),
+ rule_svc=rule_dict.get("rule_svc", LIST_DELIMITER.join(svc.svc_name for svc in svc_objs)),
+ rule_svc_refs=rule_dict.get("rule_svc_refs", LIST_DELIMITER.join(svc.svc_uid for svc in svc_objs)),
+ rule_action=rule_dict.get("rule_action", RuleAction.ACCEPT),
+ rule_track=rule_dict.get("rule_track", RuleTrack.NONE),
+ rule_installon=rule_dict.get("rule_installon"),
+ rule_time=rule_dict.get("rule_time"),
+ rule_name=rule_dict.get("rule_name", f"Rule {uid}"),
+ rule_uid=uid,
+ rule_custom_fields=rule_dict.get("rule_custom_fields"),
+ rule_implied=rule_dict.get("rule_implied", False),
+ rule_type=rule_dict.get("rule_type", RuleType.SECTIONHEADER),
+ last_change_admin=rule_dict.get("last_change_admin"),
+ parent_rule_uid=rule_dict.get("parent_rule_uid"),
+ last_hit=rule_dict.get("last_hit"),
+ rule_comment=rule_dict.get("rule_comment"),
+ rule_src_zone=rule_dict.get("rule_src_zone"),
+ rule_dst_zone=rule_dict.get("rule_dst_zone"),
+ rule_head_text=rule_dict.get("rule_head_text"),
+ rule_num=rule_dict.get("rule_num", 0),
+ rule_num_numeric=rule_dict.get("rule_num_numeric", 0.0),
+ )
+ rulebase.rules[new_rule.rule_uid] = new_rule
+
+ return new_rule
+
+ def add_references_to_rule(
+ self, config: FwConfigNormalized, rule: RuleNormalized, num_src: int = 1, num_dst: int = 1, num_svc: int = 1
+ ):
+ network_objects = [
+ network_object for network_object in config.network_objects.values() if network_object.obj_typ == "group"
+ ]
+ src_network_objects = secrets.SystemRandom(self._seed).sample(
+ network_objects, k=min(num_src, len(network_objects))
+ )
+ dst_network_objects = secrets.SystemRandom(self._seed).sample(
+ network_objects, k=min(num_dst, len(network_objects))
+ )
+
+ service_objects = list(config.service_objects.values())
+
+ svcs = secrets.SystemRandom(self._seed).sample(service_objects, k=min(num_svc, len(service_objects)))
+
+ users = [user for user in config.network_objects.values() if user.obj_typ == "access-role"]
+ src_user = secrets.choice(users)
+ dst_user = secrets.choice(users)
+
+ rule.rule_dst = LIST_DELIMITER.join(
+ [f"{obj.obj_name}{USER_DELIMITER}{src_user.obj_name}" for obj in dst_network_objects]
+ )
+ rule.rule_dst_refs = LIST_DELIMITER.join(
+ [f"{obj.obj_uid}{USER_DELIMITER}{src_user.obj_uid}" for obj in dst_network_objects]
+ )
+ rule.rule_src = LIST_DELIMITER.join(
+ [f"{obj.obj_name}{USER_DELIMITER}{dst_user.obj_name}" for obj in src_network_objects]
+ )
+ rule.rule_src_refs = LIST_DELIMITER.join(
+ [f"{obj.obj_uid}{USER_DELIMITER}{dst_user.obj_uid}" for obj in src_network_objects]
+ )
+ rule.rule_svc = LIST_DELIMITER.join([svc.svc_name for svc in svcs])
+ rule.rule_svc_refs = LIST_DELIMITER.join([svc.svc_uid for svc in svcs])
+
+ def change_network_object_subtle(self, config, obj_uid):
+ """
+ Changes a network object in a subtle way (excluding name and UID) to test
+ if the import process detects changes correctly, esp. in nested groups.
+ """
+ if obj_uid not in config.network_objects:
+ raise ValueError(f"Object with UID {obj_uid} not found in config.")
+
+ obj = config.network_objects[obj_uid]
+ # # Change the IP address slightly
+ # new_ip = IPNetwork(str(IPAddress(DUMMY_IP.first + 1)) + '/' + str(DUMMY_IP.prefixlen))
+ # obj.obj_ip = new_ip
+ # obj.obj_ip_end = new_ip
+ obj.obj_color = "blue" if obj.obj_color == "black" else "black" # Toggle color for subtle change
+
+ def change_service_object_subtle(self, config, svc_uid):
+ """
+ Changes a service object in a subtle way (excluding name and UID) to test
+ if the import process detects changes correctly.
+ """
+ if svc_uid not in config.service_objects:
+ raise ValueError(f"Service with UID {svc_uid} not found in config.")
+
+ svc = config.service_objects[svc_uid]
+ # # Change the service protocol slightly
+ # svc.svc_port = 6 if svc.svc_port == 17 else 17 # Toggle between TCP (6) and UDP (17)
+ svc.svc_color = "blue" if svc.svc_color == "black" else "black" # Toggle color for subtle change
+
+ def add_cp_section_header(
+ self, gateway: Gateway, from_rulebase_uid: str, to_rulebase_uid: str, from_rule_uid: str
+ ) -> None:
+ gateway.RulebaseLinks.append(
+ RulebaseLinkUidBased(
+ from_rulebase_uid=from_rulebase_uid,
+ from_rule_uid=from_rule_uid,
+ to_rulebase_uid=to_rulebase_uid,
+ link_type="ordered",
+ is_initial=False,
+ is_global=False,
+ is_section=True,
+ )
+ )
+
+ def add_inline_layer(
+ self, gateway: Gateway, from_rulebase_uid: str, from_rule_uid: str, to_rulebase_uid: str, index: int = 0
+ ) -> None:
+ if index == 0:
+ index = len(gateway.RulebaseLinks)
+
+ gateway.RulebaseLinks.insert(
+ index,
+ RulebaseLinkUidBased(
+ from_rulebase_uid=from_rulebase_uid,
+ from_rule_uid=from_rule_uid,
+ to_rulebase_uid=to_rulebase_uid,
+ link_type="inline",
+ is_initial=False,
+ is_global=False,
+ is_section=False,
+ ),
+ )
+
+ def add_rulebase(self, config: FwConfigNormalized, mgm_uid: str, rulebase: Rulebase = None) -> Rulebase:
+ if not rulebase:
+ new_rulebase_uid = self.uid_manager.create_uid()
+ new_rulebase = Rulebase(uid=new_rulebase_uid, name=f"Rulebase {new_rulebase_uid}", mgm_uid=mgm_uid, id=None)
+ else:
+ new_rulebase = rulebase
+
+ config.rulebases.append(new_rulebase)
+
+ return new_rulebase
diff --git a/roles/importer/files/importer/test/mocking/mock_fwconfig_import_gateway.py b/roles/importer/files/importer/test/mocking/mock_fwconfig_import_gateway.py
new file mode 100644
index 0000000000..df77e8b7db
--- /dev/null
+++ b/roles/importer/files/importer/test/mocking/mock_fwconfig_import_gateway.py
@@ -0,0 +1,10 @@
+from mocking.mock_import_state import MockImportStateController
+from model_controllers.fwconfig_import_gateway import FwConfigImportGateway
+
+
+class MockFwConfigImportGateway(FwConfigImportGateway):
+ def __init__(self):
+ super().__init__()
+ self._import_details = MockImportStateController(stub_setCoreData=True)
+
+ self._global_state.import_state = self._import_details
diff --git a/roles/importer/files/importer/test/mocking/mock_fwconfig_import_rule.py b/roles/importer/files/importer/test/mocking/mock_fwconfig_import_rule.py
new file mode 100644
index 0000000000..9709b90a74
--- /dev/null
+++ b/roles/importer/files/importer/test/mocking/mock_fwconfig_import_rule.py
@@ -0,0 +1,344 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from models.fwconfig_normalized import FwConfigNormalized
+ from models.rulebase import Rulebase
+
+import sys
+from pathlib import Path
+
+sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "importer"))
+
+from fwo_base import init_service_provider
+from model_controllers.fwconfig_import_rule import FwConfigImportRule
+from test.mocking.mock_import_state import MockImportStateController
+
+
+class MockFwConfigImportRule(FwConfigImportRule):
+ """
+ A mock subclass of FwConfigImportRule for testing.
+
+ This class allows selective stubbing of internal methods to control behavior during tests.
+ """
+
+ def __init__(self):
+ """
+ Initializes the mock import rule controller with stubs enabled by default.
+ """
+ service_provider = init_service_provider()
+ self._import_details = MockImportStateController(stub_setCoreData=True)
+ service_provider.get_global_state().import_state = self._import_details
+
+ self._stub_markRulesRemoved = True
+ self._stub_getRules = False
+ self._stub_addNewRuleMetadata = True
+ self._stub_addNewRules = True
+ self._stub_moveRules = True
+ self._stub_create_new_rule_version = True
+ self._stub_add_new_refs = True
+ self._stub_remove_outdated_refs = True
+ self._stub_write_changelog_rules = True
+
+ super().__init__()
+
+ # Properties to control stubbing behavior
+
+ @property
+ def import_details(self) -> MockImportStateController:
+ """
+ Returns the mock import state controller.
+ """
+ return self._import_details
+
+ @import_details.setter
+ def import_details(self, value: MockImportStateController):
+ self._import_details = value
+
+ @property
+ def stub_markRulesRemoved(self) -> bool:
+ """
+ Indicates whether to stub markRulesRemoved.
+ """
+ return self._stub_markRulesRemoved
+
+ @stub_markRulesRemoved.setter
+ def stub_markRulesRemoved(self, value: bool):
+ self._stub_markRulesRemoved = value
+
+ @property
+ def stub_getRules(self) -> bool:
+ """
+ Indicates whether to stub getRules.
+ """
+ return self._stub_getRules
+
+ @stub_getRules.setter
+ def stub_getRules(self, value: bool):
+ self._stub_getRules = value
+
+ @property
+ def stub_addNewRuleMetadata(self) -> bool:
+ """
+ Indicates whether to stub addNewRuleMetadata.
+ """
+ return self._stub_addNewRuleMetadata
+
+ @stub_addNewRuleMetadata.setter
+ def stub_addNewRuleMetadata(self, value: bool):
+ self._stub_addNewRuleMetadata = value
+
+ @property
+ def stub_addNewRules(self) -> bool:
+ """
+ Indicates whether to stub addNewRules.
+ """
+ return self._stub_addNewRules
+
+ @stub_addNewRules.setter
+ def stub_addNewRules(self, value: bool):
+ self._stub_addNewRules = value
+
+ @property
+ def stub_moveRules(self) -> bool:
+ """
+ Indicates whether to stub moveRules.
+ """
+ return self._stub_moveRules
+
+ @stub_moveRules.setter
+ def stub_moveRules(self, value: bool):
+ self._stub_moveRules = value
+
+ @property
+ def stub_create_new_rule_version(self) -> bool:
+ """
+ Indicates whether to stub create_new_rule_version.
+ """
+ return self._stub_create_new_rule_version
+
+ @stub_create_new_rule_version.setter
+ def stub_create_new_rule_version(self, value: bool):
+ self._stub_create_new_rule_version = value
+
+ @property
+ def stub_add_new_refs(self) -> bool:
+ """
+ Indicates whether to stub add_new_refs.
+ """
+ return self._stub_add_new_refs
+
+ @stub_add_new_refs.setter
+ def stub_add_new_refs(self, value: bool):
+ self._stub_add_new_refs = value
+
+ @property
+ def stub_remove_outdated_refs(self) -> bool:
+ """
+ Indicates whether to stub remove_outdated_refs.
+ """
+ return self._stub_remove_outdated_refs
+
+ @stub_remove_outdated_refs.setter
+ def stub_remove_outdated_refs(self, value: bool):
+ self._stub_remove_outdated_refs = value
+
+ @property
+ def stub_write_changelog_rules(self) -> bool:
+ """
+ Indicates whether to stub write_changelog_rules.
+ """
+ return self._stub_write_changelog_rules
+
+ @stub_write_changelog_rules.setter
+ def stub_write_changelog_rules(self, value: bool):
+ self._stub_write_changelog_rules = value
+
+ # Overridden methods with stubbing behavior
+
+ def mark_rules_removed(self, removedRuleUids: dict[str, list[str]]) -> tuple[int, list[int]]:
+ """
+ Simulates marking rules as removed. Can delegate to the base class if not stubbed.
+
+ Args:
+ removedRuleUids (dict): A dict mapping rulebase identifiers to lists of rule UIDs.
+ changedRuleUids (dict): A dict mapping rulebase identifiers to lists of rule UIDs.
+
+ Returns:
+ tuple: (changes, collectedRemovedRuleIds)
+
+ """
+ changes = 0
+ collectedRemovedRuleIds = []
+ for rulebase in removedRuleUids:
+ changes += len(removedRuleUids[rulebase])
+ collectedRemovedRuleIds.extend(removedRuleUids[rulebase])
+
+ if not self.stub_markRulesRemoved:
+ changes, collectedRemovedRuleIds = super().mark_rules_removed(removedRuleUids)
+
+ return changes, collectedRemovedRuleIds
+
+ def get_rules(self, ruleUids: list[str]) -> list[Rulebase]:
+ """
+ Simulates returning rules by UID. Delegates to base if not stubbed.
+
+ Args:
+ ruleUids (list): list of rule UIDs to fetch.
+
+ Returns:
+ list: list of Rulebase instances containing the rules.
+
+ """
+ rulebases: list[Rulebase] = []
+
+ if not self.stub_getRules:
+ rulebases = super().get_rules(ruleUids)
+
+ return rulebases
+
+ def add_new_rule_metadata(self, newRules: list[Rulebase]) -> tuple[int, list[int]]:
+ """
+ Simulates adding metadata for new rules. Delegates to base if not stubbed.
+
+ Args:
+ newRules (list): list of Rulebase objects with new rules.
+
+ Returns:
+ tuple: (changes, newRuleIds)
+
+ """
+ changes = 0
+ newRuleIds: list[int] = []
+
+ if not self.stub_addNewRuleMetadata:
+ changes, newRuleIds = super().add_new_rule_metadata(newRules)
+
+ return changes, newRuleIds
+
+ def add_new_rules(self, rulebases: list[Rulebase]) -> tuple[int, list[dict]]:
+ """
+ Simulates adding new rules to db and returning their ids. Delegates to base if not stubbed.
+
+ Args:
+ newRules (list): list of Rulebase objects with new rules.
+
+ Returns:
+ tuple: (changes, newRuleIds)
+
+ """
+ changes = 0
+ newRuleIds = []
+
+ for rulebase in rulebases:
+ for rule in list(rulebase.rules.values()):
+ changes += 1
+ newRuleIds.append({"rule_uid": rule.rule_uid, "rule_id": changes})
+
+ if not self.stub_addNewRuleMetadata:
+ changes, newRuleIds = super().add_new_rules(rulebases)
+
+ return changes, newRuleIds
+
+ def moveRules(self, moved_rule_uids: dict[str, list[str]]) -> tuple[int, list[int]]:
+ """
+ Simulates moving rules to a new location. Delegates to base if not stubbed.
+
+ Args:
+ moved_rule_uids (dict): Mapping from rulebase to list of rule UIDs to move.
+
+ Returns:
+ tuple: (changes, moved_rule_ids)
+
+ """
+ changes = 0
+ moved_rule_ids: list[int] = []
+
+ for rulebase in moved_rule_uids:
+ for _ in moved_rule_uids[rulebase]:
+ changes += 1
+ moved_rule_ids.append(changes)
+
+ if not self.stub_moveRules:
+ changes, moved_rule_ids = super().moveRules(moved_rule_uids)
+
+ return changes, moved_rule_ids
+
+ def create_new_rule_version(self, rule_uids):
+ """
+ Simulates creating a new version of a rule. Delegates to base if not stubbed.
+
+ Args:
+ rule_uid (str): The UID of the rule to version.
+
+ """
+ changes = 0
+ collected_rule_ids: list[int] = []
+ insert_rules_return: list[dict] = []
+
+ if not self.stub_create_new_rule_version:
+ changes, collected_rule_ids, insert_rules_return = super().create_new_rule_version(rule_uids)
+ else:
+ for rulebase_rule_uids in rule_uids.values():
+ changes += len(rulebase_rule_uids)
+ collected_rule_ids = list(range(1, len(rulebase_rule_uids) + 1))
+ for counter in range(len(rulebase_rule_uids)):
+ insert_rule_return = {}
+ insert_rule_return["rule_uid"] = rulebase_rule_uids[counter]
+ insert_rule_return["rule_id"] = changes + counter + 1
+ insert_rules_return.append(insert_rule_return)
+
+ return changes, collected_rule_ids, insert_rules_return
+
+ def add_new_refs(self, prev_config: FwConfigNormalized) -> int:
+ """
+ Simulates adding new references for rules. Delegates to base if not stubbed.
+
+ Args:
+ rule_uids (list): List of rule UIDs to add references for.
+
+ Returns:
+ int: (changes)
+
+ """
+ changes = 0
+
+ if not self.stub_add_new_refs:
+ changes = super().add_new_refs(prev_config)
+
+ return changes
+
+ def remove_outdated_refs(self, prev_config: FwConfigNormalized) -> int:
+ """
+ Simulates removing outdated references for rules. Delegates to base if not stubbed.
+
+ Args:
+ prev_config (FwConfigNormalized): The previous configuration.
+
+ Returns:
+ int: (changes)
+
+ """
+ changes = 0
+
+ if not self.stub_remove_outdated_refs:
+ changes = super().remove_outdated_refs(prev_config)
+
+ return changes
+
+ def write_changelog_rules(self, added_rules_ids, removed_rules_ids):
+ """
+ Simulates writing a changelog entry for rules. Delegates to base if not stubbed.
+
+ Args:
+ added_rules_ids (list): List of added rule IDs.
+ removed_rules_ids (list): List of removed rule IDs.
+
+ """
+ errors = 0
+
+ if not self.stub_write_changelog_rules:
+ errors = super().write_changelog_rules(added_rules_ids, removed_rules_ids)
+
+ return errors
diff --git a/roles/importer/files/importer/test/mocking/mock_fwo_api_oo.py b/roles/importer/files/importer/test/mocking/mock_fwo_api_oo.py
new file mode 100644
index 0000000000..52188bd69e
--- /dev/null
+++ b/roles/importer/files/importer/test/mocking/mock_fwo_api_oo.py
@@ -0,0 +1,886 @@
+import fwo_const
+from fwo_api import FwoApi
+from graphql import (
+ ArgumentNode,
+ BooleanValueNode,
+ IntValueNode,
+ ListValueNode,
+ ObjectValueNode,
+ OperationDefinitionNode,
+ OperationType,
+ StringValueNode,
+ VariableNode,
+ parse,
+)
+from models.networkobject import NetworkObject
+from models.rule import RuleNormalized
+from models.rulebase import Rulebase
+from models.serviceobject import ServiceObject
+
+from .mock_config import MockFwConfigNormalizedBuilder
+
+CHECK_POINT = "Check Point"
+PALO_ALTO = "Palo Alto"
+FORTINET_FORTIOS_GATEWAY = "Fortinet FortiOS Gateway"
+FORTIOS_MANAGEMENT = "FortiOS Management"
+
+TABLE_IDENTIFIERS = {
+ "stm_change_type": "change_type_id",
+ "stm_usr_typ": "usr_typ_id",
+ "stm_svc_typ": "svc_typ_id",
+ "stm_obj_typ": "obj_typ_id",
+ "stm_action": "action_id",
+ "stm_track": "track_id",
+ "stm_color": "color_id",
+ "stm_dev_typ": "dev_typ_id",
+ "object": "obj_id",
+ "service": "svc_id",
+ "usr": "user_id",
+ "rulebase": "id",
+}
+
+STM_TABLES = {
+ "stm_change_type": [
+ {"change_type_id": 1, "change_type_name": "factory settings"},
+ {"change_type_id": 2, "change_type_name": "initial import"},
+ {"change_type_id": 3, "change_type_name": "in operation"},
+ ],
+ "stm_usr_typ": [{"usr_typ_id": 1, "usr_typ_name": "group"}, {"usr_typ_id": 2, "usr_typ_name": "simple"}],
+ "stm_svc_typ": [
+ {"svc_typ_id": 1, "svc_typ_name": "simple", "svc_typ_comment": "standard services"},
+ {"svc_typ_id": 2, "svc_typ_name": "group", "svc_typ_comment": "groups of services"},
+ {"svc_typ_id": 3, "svc_typ_name": "rpc", "svc_typ_comment": "special services, here: RPC"},
+ ],
+ "stm_obj_typ": [
+ {"obj_typ_id": i, "obj_typ_name": name}
+ for i, name in enumerate(
+ [
+ "network",
+ "group",
+ "host",
+ "machines_range",
+ "dynamic_net_obj",
+ "sofaware_profiles_security_level",
+ "gateway",
+ "cluster_member",
+ "gateway_cluster",
+ "domain",
+ "group_with_exclusion",
+ "ip_range",
+ "uas_collection",
+ "sofaware_gateway",
+ "voip_gk",
+ "gsn_handover_group",
+ "voip_sip",
+ "simple-gateway",
+ "external-gateway",
+ "voip",
+ "access-role",
+ ],
+ start=1,
+ )
+ ],
+ "stm_action": [
+ {"action_id": i, "action_name": name, "allowed": allowed}
+ for i, (name, allowed) in enumerate(
+ [
+ ("accept", True),
+ ("drop", False),
+ ("deny", False),
+ ("access", True),
+ ("client encrypt", True),
+ ("client auth", True),
+ ("reject", False),
+ ("encrypt", True),
+ ("user auth", True),
+ ("session auth", True),
+ ("permit", True),
+ ("permit webauth", True),
+ ("redirect", True),
+ ("map", True),
+ ("permit auth", True),
+ ("tunnel l2tp", True),
+ ("tunnel vpn-group", True),
+ ("tunnel vpn", True),
+ ("actionlocalredirect", True),
+ ("inner layer", True),
+ # NAT actions
+ ("NAT src", False),
+ ("NAT src, dst", False),
+ ("NAT src, dst, svc", False),
+ ("NAT dst", False),
+ ("NAT dst, svc", False),
+ ("NAT svc", False),
+ ("NAT src, svc", False),
+ ("NAT", False),
+ ("inform", True),
+ ],
+ start=1,
+ )
+ ],
+ "stm_track": [
+ {"track_id": i, "track_name": name}
+ for i, name in enumerate(
+ [
+ "log",
+ "none",
+ "alert",
+ "userdefined",
+ "mail",
+ "account",
+ "userdefined 1",
+ "userdefined 2",
+ "userdefined 3",
+ "snmptrap",
+ # junos
+ "log count",
+ "count",
+ "log alert",
+ "log alert count",
+ "log alert count alarm",
+ "log count alarm",
+ "count alarm",
+ # fortinet
+ "all",
+ "all start",
+ "utm",
+ "network log",
+ "utm start",
+ "detailed log",
+ ],
+ start=1,
+ )
+ ],
+ "stm_dev_typ": [
+ {
+ "dev_typ_id": i,
+ "dev_typ_name": name,
+ "dev_typ_version": version,
+ "dev_typ_manufacturer": manufacturer,
+ "dev_typ_predef_svc": predef_svc,
+ "dev_typ_is_mgmt": is_mgmt,
+ "dev_typ_is_multi_mgmt": is_multi_mgmt,
+ "is_pure_routing_device": is_pure_routing_device,
+ }
+ for i, (name, version, manufacturer, predef_svc, is_mgmt, is_multi_mgmt, is_pure_routing_device) in enumerate(
+ [
+ ("Netscreen", "5.x-6.x", "Netscreen", "", True, False, False),
+ ("FortiGateStandalone", "5ff", "Fortinet", "", True, False, False),
+ ("Barracuda Firewall Control Center", "Vx", "phion", "", True, False, False),
+ ("phion netfence", "3.x", "phion", "", False, False, False),
+ ("Check Point R5x-R7x", "", CHECK_POINT, "", True, False, False),
+ ("JUNOS", "10-21", "Juniper", "any;0;0;65535;;junos-predefined-service;simple;", True, False, False),
+ ("Check Point R8x", "", CHECK_POINT, "", True, False, False),
+ ("FortiGate", "5ff", "Fortinet", "", False, False, False),
+ ("FortiADOM", "5ff", "Fortinet", "", True, False, False),
+ ("FortiManager", "5ff", "Fortinet", "", True, True, False),
+ ("Check Point MDS R8x", "", CHECK_POINT, "", True, True, False),
+ ("Cisco Firepower Management Center", "7ff", "Cisco", "", True, True, False),
+ ("Cisco Firepower Domain", "7ff", "Cisco", "", False, True, False),
+ ("Cisco Firepower Gateway", "7ff", "Cisco", "", False, False, False),
+ ("DummyRouter Management", "1", "DummyRouter", "", False, True, True),
+ ("DummyRouter Gateway", "1", "DummyRouter", "", False, False, True),
+ ("Azure", "2022ff", "Microsoft", "", False, True, False),
+ ("Azure Firewall", "2022ff", "Microsoft", "", False, False, False),
+ ("Palo Alto Firewall", "2023ff", PALO_ALTO, "", False, True, False),
+ ("Palo Alto Panorama", "2023ff", PALO_ALTO, "", True, True, False),
+ ("Palo Alto Management", "2023ff", PALO_ALTO, "", False, True, False),
+ (FORTIOS_MANAGEMENT, "REST", "Fortinet", "", False, True, False),
+ (FORTINET_FORTIOS_GATEWAY, "REST", "Fortinet", "", False, False, False),
+ ("NSX DFW Gateway", "REST", "VMWare", "", False, False, False),
+ ("NSX", "REST", "VMWare", "", False, True, False),
+ (FORTIOS_MANAGEMENT, "REST", "Fortinet", "", False, True, False),
+ (FORTINET_FORTIOS_GATEWAY, "REST", "Fortinet", "", False, False, False),
+ ("NSX DFW Gateway", "REST", "VMWare", "", False, False, False),
+ ("NSX", "REST", "VMWare", "", False, True, False),
+ (FORTIOS_MANAGEMENT, "REST", "Fortinet", "", False, True, False),
+ (FORTINET_FORTIOS_GATEWAY, "REST", "Fortinet", "", False, False, False),
+ ],
+ start=1,
+ )
+ ],
+ "stm_color": [
+ {"color_id": 0, "color_name": "black"},
+ {"color_id": 1, "color_name": "blue"},
+ ],
+}
+
+INSERT_UNIQUE_PK_CONSTRAINTS = {
+ "rule_from": ["rule_id", "obj_id", "user_id"],
+ "rule_to": ["rule_id", "obj_id", "user_id"],
+ "rule_svc": ["rule_id", "svc_id"],
+ "rule_nwobj_resolved": ["rule_id", "obj_id", "user_id"],
+ "rule_svc_resolved": ["rule_id", "svc_id"],
+ "rule_user_resolved": ["rule_id", "user_id"],
+ "objgrp": ["objgrp_id", "objgrp_member_id"],
+ "svcgrp": ["svcgrp_id", "svcgrp_member_id"],
+ "usergrp": ["usergrp_id", "usergrp_member_id"],
+ "objgrp_flat": ["objgrp_flat_id", "objgrp_flat_member_id"],
+ "svcgrp_flat": ["svcgrp_flat_id", "svcgrp_flat_member_id"],
+ "usergrp_flat": ["usergrp_flat_id", "usergrp_flat_member_id"],
+}
+
+
+def object_to_dict(obj, max_depth=10):
+ """
+ Converts an object to a dictionary, recursively converting nested objects.
+ """
+ if max_depth < 0:
+ return str(obj) # Avoid infinite recursion
+ obj_dict = {}
+ obj_dict["type"] = type(obj).__name__
+ if hasattr(obj, "name"):
+ name = obj.name.value if hasattr(obj.name, "value") else obj.name
+ obj_dict["name"] = name
+ if hasattr(obj, "value"):
+ obj_dict["value"] = object_to_dict(obj.value, max_depth - 1)
+ elif hasattr(obj, "fields"):
+ obj_dict["fields"] = [object_to_dict(field, max_depth - 1) for field in obj.fields]
+ elif hasattr(obj, "arguments"):
+ obj_dict["arguments"] = [object_to_dict(arg, max_depth - 1) for arg in obj.arguments]
+ elif hasattr(obj, "values"):
+ obj_dict["values"] = [object_to_dict(value, max_depth - 1) for value in obj.values]
+ return obj_dict
+
+
+class MockFwoApi(FwoApi):
+ """
+ A mock FwoApi that simulates Hasura GraphQL mutations/queries using in-memory tables.
+ """
+
+ def __init__(self, ApiUri=None, Jwt=None):
+ super().__init__(ApiUri, Jwt)
+ self.tables = {} # {table_name: {pk: row_dict}}
+ # Initialize with some standard tables
+ for table_name, rows in STM_TABLES.items():
+ self.tables[table_name] = {row[TABLE_IDENTIFIERS[table_name]]: row for row in rows}
+
+ def call(self, query, query_variables="", debug_level=0, analyze_payload=False):
+ ast = parse(query)
+ # Find the first operation definition
+ op_def = next((d for d in ast.definitions if hasattr(d, "operation")), None)
+ if op_def is None:
+ raise NotImplementedError("No operation definition found in query.")
+
+ if not isinstance(op_def, OperationDefinitionNode):
+ raise NotImplementedError("Only operation definitions supported in mock.")
+
+ if op_def.operation == OperationType.MUTATION:
+ return self._handle_mutation(op_def, query_variables)
+ if op_def.operation == OperationType.QUERY:
+ return self._handle_query(op_def, query_variables)
+ raise NotImplementedError("Only query and mutation supported in mock.")
+
+ def _handle_mutation(self, op_def, variables):
+ result = {"data": {}}
+ for sel in op_def.selection_set.selections:
+ field = sel.name.value
+ # pprint.pprint(object_to_dict(sel))
+ if field.startswith("insert_"):
+ self._handle_insert(sel, variables, result, field)
+ elif field.startswith("update_"):
+ self._handle_update(sel, variables, result, field)
+ elif field.startswith("delete_"):
+ self._handle_delete(sel, variables, result, field)
+ return result
+
+ def _handle_insert(self, sel, variables, result, field):
+ table = field[len("insert_") :]
+ # Find the argument name for objects
+ try:
+ arg_name = next((a.value.name.value for a in sel.arguments if a.name.value == "objects"), None)
+ objects = variables.get(arg_name, [])
+ except AttributeError:
+ objects = [
+ {
+ field.name.value: variables.get(field.value.name.value, None)
+ for field in sel.arguments[0].value.fields
+ }
+ ]
+ if table not in self.tables:
+ self.tables[table] = {}
+ returning = []
+ for obj in objects:
+ if self._handle_object(table, obj, returning):
+ continue # skip due to unique constraint
+ result["data"][field] = {"affected_rows": len(objects), "returning": returning}
+
+ def _handle_object(self, table, obj, returning):
+ pk = len(self.tables[table]) + 1
+ obj = dict(obj) # copy
+ if table not in [
+ "objgrp",
+ "svcgrp",
+ "usergrp",
+ "objgrp_flat",
+ "svcgrp_flat",
+ "usergrp_flat",
+ ]: # using pair of reference ids as primary key
+ obj[TABLE_IDENTIFIERS.get(table, f"{table}_id")] = pk
+ if any("create" in key for key in obj):
+ obj["removed"] = None
+ obj["active"] = True
+ if table == "rulebase":
+ obj.pop("rules", None) # remove rules from rulebase insert. why are they even there?
+ if any(
+ row["mgm_id"] == obj["mgm_id"] and row["uid"] == obj["uid"]
+ for row in self.tables.get("rulebase", {}).values()
+ ):
+ return True # mock on_conflict unique_rulebase_mgm_id_uid constraint
+ if table in INSERT_UNIQUE_PK_CONSTRAINTS:
+ # Check for unique constraints
+ unique_keys = INSERT_UNIQUE_PK_CONSTRAINTS[table]
+ if any(all(row.get(key) == obj.get(key) for key in unique_keys) for row in self.tables[table].values()):
+ raise ValueError(f"Unique constraint violation for {table} with keys {unique_keys}.")
+ self.tables[table][pk] = obj
+ returning.append(obj)
+
+ return False # not skipped
+
+ def _handle_update(self, sel, variables, result, field):
+ table = field[len("update_") :]
+ returning = []
+ affected = 0
+ # Find argument names for where and _set
+ where_arg = next((a for a in sel.arguments if a.name.value == "where"), None)
+ set_arg = next((a for a in sel.arguments if a.name.value == "_set"), None)
+ if where_arg and set_arg:
+ # pprint.pprint(object_to_dict(where_arg))
+ for row in self.tables.get(table, {}).values():
+ if self._row_matches_where(row, where_arg, variables):
+ self._update_row(row, set_arg, variables)
+ returning.append(row)
+ affected += 1
+ result["data"][field] = {"affected_rows": affected, "returning": returning}
+
+ def _handle_delete(self, sel, variables, result, field):
+ table = field[len("delete_") :]
+ returning = []
+ affected = 0
+ # Find argument name for where
+ where_arg = next((a for a in sel.arguments if a.name.value == "where"), None)
+ if where_arg:
+ to_delete = []
+ for pk, row in self.tables.get(table, {}).items():
+ if self._row_matches_where(row, where_arg, variables):
+ returning.append(row)
+ to_delete.append(pk)
+ affected += 1
+ for pk in to_delete:
+ del self.tables[table][pk]
+ result["data"][field] = {"affected_rows": affected, "returning": returning}
+
+ def _get_value_from_node(self, value_node, variables):
+ """Resolve a value from an AST node, using variables if needed."""
+ if isinstance(value_node, VariableNode):
+ return variables.get(value_node.name.value)
+ if isinstance(value_node, StringValueNode):
+ return value_node.value
+ if isinstance(value_node, IntValueNode):
+ return int(value_node.value)
+ if isinstance(value_node, BooleanValueNode):
+ return value_node.value
+ if isinstance(value_node, ListValueNode):
+ return [self._get_value_from_node(v, variables) for v in value_node.values]
+ if isinstance(value_node, ObjectValueNode):
+ return {f.name.value: self._get_value_from_node(f.value, variables) for f in value_node.fields}
+ if hasattr(value_node, "value"):
+ return value_node.value
+ raise ValueError(f"Unsupported AST node type: {type(value_node)}")
+
+ def _row_matches_where(self, row, where_node: ArgumentNode, variables):
+ """
+ Recursively checks if a row matches the where clause AST.
+ Supports _and, _or, _not, and flat field checks.
+ """
+ where_value: ObjectValueNode
+ if isinstance(where_node, ObjectValueNode):
+ where_value = where_node
+ elif isinstance(where_node.value, ObjectValueNode):
+ try:
+ where_value = where_node.value
+ except AttributeError:
+ where_value = where_node.fields[0]
+
+ for field in where_value.fields:
+ key = field.name.value
+ value = field.value
+
+ return self._check_where_field(row, variables, key, value)
+
+ return True
+
+ def _check_where_field(self, row, variables, key, value):
+ if key == "_and":
+ if not isinstance(value, ListValueNode):
+ raise ValueError("_and must be a list")
+ if not all(self._row_matches_where(row, v, variables) for v in value.values):
+ return False
+ elif key == "_or":
+ self._check_where_field_or(row, variables, value)
+
+ elif key == "_not":
+ if self._row_matches_where(row, value, variables):
+ return False
+ else:
+ return self._check_flat_field(row, variables, key, value)
+ return None
+
+ def _check_where_field_or(self, row, variables, value):
+ if isinstance(value, ListValueNode):
+ if not any(self._row_matches_where(row, v, variables) for v in value.values): # type: ignore
+ return False
+ elif isinstance(value, VariableNode):
+ or_values = variables.get(value.name.value, [])
+ if not isinstance(or_values, list):
+ raise ValueError(f"Expected list for '_or' variable '{value.name.value}', got {type(or_values)}.")
+ if not self._row_matches_where_bool_exp(row, or_values):
+ return False
+ return None
+
+ def _check_flat_field(self, row, variables, key, value):
+ if key not in row:
+ raise ValueError(f"Field '{key}' not present in row (possible reference or invalid field).")
+ if not isinstance(value, ObjectValueNode):
+ raise ValueError(f"Non-flat where clause for field '{key}'.")
+ for op_field in value.fields:
+ op = op_field.name.value
+ op_val = self._get_value_from_node(op_field.value, variables)
+ if op == "_eq":
+ return row.get(key) == op_val
+ if op == "_in":
+ return row.get(key) in op_val
+ if op == "_is_null":
+ return op_val or row.get(key) is None
+ if op == "_neq":
+ return row.get(key) != op_val
+ raise ValueError(f"Unsupported operator '{op}' in where clause.")
+ return None
+
+ def _row_matches_where_bool_exp(self, row, or_values):
+ """
+ Checks if a row matches any of the boolean expressions in or_values.
+ """
+ for v in or_values: # v = {'_and': [{'field1: {'_eq': 'value1'}}, {'field2': {'_eq': 'value2'}}]}
+ fields = v["_and"] # [{'field1': {'_eq': 'value1'}}, {'field2': {'_eq': 'value2'}}]
+ for field in fields: # {'field1': {'_eq': 'value1'}}
+ field_name, value = next(iter(field.items()))
+ key, value = next(iter(value.items()))
+ if key == "_eq":
+ if row.get(field_name) != value:
+ return False
+ else:
+ raise ValueError(f"Unsupported operator '{key}' in where clause.")
+ return True
+
+ def _update_row(self, row, set_node: ArgumentNode, variables):
+ """
+ Updates a row based on the _set argument node.
+ """
+ if not isinstance(set_node.value, ObjectValueNode):
+ raise ValueError("Expected ObjectValueNode for _set clause value.")
+ set_value: ObjectValueNode = set_node.value
+
+ for field in set_value.fields:
+ key = field.name.value
+ value = self._get_value_from_node(field.value, variables)
+ if key not in row:
+ raise ValueError(f"Field '{key}' not present in row (invalid field).")
+ # Update the row with the new value
+ row[key] = value
+
+ def _get_returning_fields(self, sel):
+ """
+ Extracts the returning fields mapping from the selection set.
+ Returns a dict: {requested_field: actual_field}
+ """
+ for subsel in (
+ getattr(sel, "selection_set", []).selections if hasattr(sel, "selection_set") and sel.selection_set else []
+ ):
+ if subsel.name.value == "returning":
+ # If aliasing is used, subsel.alias.value is the requested field, subsel.name.value is the actual field
+ return {
+ (f.alias.value if f.alias else f.name.value): f.name.value
+ for f in getattr(subsel, "selection_set", []).selections
+ if hasattr(subsel, "selection_set") and subsel.selection_set
+ }
+ return None # None means return full row
+
+ def _map_returning_fields(self, row, returning_fields):
+ """
+ Maps the row dict to the requested returning fields.
+ """
+ if not returning_fields:
+ return row
+ return {req: row.get(actual) for req, actual in returning_fields.items()}
+
+ def _handle_query(self, op_def, variables):
+ result = {"data": {}}
+ for sel in op_def.selection_set.selections:
+ field = sel.name.value
+ table = field
+ rows = list(self.tables.get(table, {}).values())
+ result["data"][field] = rows
+ return result
+
+ def build_config_from_db(self, import_state, mgm_uid, gateways):
+ """
+ Builds a configuration object from the in-memory tables.
+ This is a mock implementation that simulates fetching data from a database.
+ """
+ config = MockFwConfigNormalizedBuilder.empty_config()
+
+ config.gateways = gateways
+ import_id = import_state.ImportId
+
+ for table_name, rows in self.tables.items():
+ for row in rows.values():
+ if row.get("active", True) is False:
+ continue
+ self._process_row(row, import_id, mgm_uid, import_state, config, table_name)
+
+ return config
+
+ def _process_row(self, row, import_id, mgm_uid, import_state, config, table_name):
+ if table_name == "object":
+ # create new dict without the primary key
+ obj = self.obj_dict_from_row(row, import_id, import_state)
+ if obj:
+ config.network_objects[row["obj_uid"]] = NetworkObject.parse_obj(obj)
+ elif table_name == "service":
+ # create new dict without the primary key
+ svc = self.service_dict_from_row(row, import_id, import_state)
+ if svc:
+ config.service_objects[row["svc_uid"]] = ServiceObject.parse_obj(svc)
+ elif table_name == "usr":
+ # create new dict without the primary key
+ user = self.user_dict_from_row(row, import_id)
+ if user:
+ config.users[row["user_uid"]] = user
+ elif table_name == "rulebase":
+ # create new dict without the primary key
+ self._try_add_rulebase(config, row, import_id, mgm_uid)
+ elif table_name == "rule":
+ # create new dict without the primary key
+ rule = self.rule_dict_from_row(row, import_id, mgm_uid)
+ # find the rulebase by uid
+ rulebase = next(
+ (
+ rb
+ for rb in config.rulebases
+ if next(
+ (
+ rb_db["uid"]
+ for rb_db in self.tables.get("rulebase", {}).values()
+ if rb_db["id"] == row["rulebase_id"]
+ ),
+ None,
+ )
+ == rb.uid
+ ),
+ None,
+ )
+ if rulebase and rule:
+ rulebase.rules[row["rule_uid"]] = RuleNormalized.parse_obj(rule)
+
+ def _try_add_rulebase(self, config, row, import_id, mgm_uid):
+ rulebase = self.rulebase_dict_from_row(row, import_id, mgm_uid)
+ if rulebase:
+ config.rulebases.append(Rulebase.parse_obj(rulebase))
+
+ def obj_dict_from_row(self, row, import_id, import_state):
+ if row["obj_create"] > import_id or (row.get("removed") and row["removed"] <= import_id):
+ return None
+ obj_dict = {}
+ for key, value in row.items():
+ if key == "obj_id":
+ continue
+ if key == "obj_color_id":
+ obj_dict["obj_color"] = import_state.lookupColorStr(value)
+ elif key == "obj_typ_id":
+ obj_dict["obj_typ"] = self.tables["stm_obj_typ"].get(value, {}).get("obj_typ_name", "unknown")
+ else:
+ obj_dict[key] = value
+ return obj_dict
+
+ def service_dict_from_row(self, row, import_id, import_state):
+ if row["svc_create"] > import_id or (row.get("removed") and row["removed"] <= import_id):
+ return None
+ svc_dict = {}
+ for key, value in row.items():
+ if key == "svc_id":
+ continue
+ if key == "svc_color_id":
+ svc_dict["svc_color"] = import_state.lookupColorStr(value)
+ elif key == "svc_typ_id":
+ svc_dict["svc_typ"] = self.tables["stm_svc_typ"].get(value, {}).get("svc_typ_name", "unknown")
+ elif key == "ip_proto_id":
+ svc_dict["ip_proto"] = row["ip_proto_id"]
+ else:
+ svc_dict[key] = value
+ return svc_dict
+
+ def user_dict_from_row(self, row, import_id):
+ if row["user_create"] > import_id or (row.get("removed") and row["removed"] <= import_id):
+ return None
+ usr_dict = {}
+ for key, value in row.items():
+ if key in ["user_id", "mgm_id", "user_create", "user_last_seen", "active", "removed"]:
+ continue
+ if key == "usr_typ_id":
+ usr_dict["user_typ"] = self.tables["stm_usr_typ"].get(value, {}).get("usr_typ_name", "unknown")
+ elif value is None:
+ continue
+ else:
+ usr_dict[key] = value
+ return usr_dict
+
+ def rulebase_dict_from_row(self, row, import_id, mgm_uid):
+ if (row.get("created") and row["created"] > import_id) or (row.get("removed") and row["removed"] <= import_id):
+ return None
+ rb_dict = {}
+ for key, value in row.items():
+ if key == "id":
+ continue
+ if key == "mgm_id":
+ rb_dict["mgm_uid"] = mgm_uid
+ rb_dict[key] = value
+ rb_dict["rules"] = {}
+ return rb_dict
+
+ def rule_dict_from_row(self, row, import_id, mgm_uid):
+ if row["rule_create"] > import_id or (row.get("removed") and row["removed"] <= import_id):
+ return None
+ rule_dict = {}
+ for key, value in row.items():
+ if key == "rule_id":
+ continue
+ if key == "mgm_id":
+ rule_dict["mgm_uid"] = mgm_uid
+ rule_dict[key] = value
+ return rule_dict
+
+ def get_table(self, table_name):
+ """
+ Returns the table data for the given table name.
+ """
+ return self.tables.get(table_name, {})
+
+ def get_nwobj_uid(self, obj_id):
+ """
+ Returns the UID of a network object by its ID.
+ """
+ nwobj_uid = self.tables.get("object", {}).get(obj_id, {}).get("obj_uid", None)
+ if nwobj_uid is None:
+ raise KeyError(f"Network object ID {obj_id} not found in database.")
+ return nwobj_uid
+
+ def get_svc_uid(self, svc_id):
+ """
+ Returns the UID of a service object by its ID.
+ """
+ svc_uid = self.tables.get("service", {}).get(svc_id, {}).get("svc_uid", None)
+ if svc_uid is None:
+ raise KeyError(f"Service ID {svc_id} not found in database.")
+ return svc_uid
+
+ def get_user_uid(self, user_id):
+ """
+ Returns the UID of a user by its ID.
+ """
+ user_uid = self.tables.get("usr", {}).get(user_id, {}).get("user_uid", None)
+ if user_uid is None:
+ raise KeyError(f"User ID {user_id} not found in database.")
+ return user_uid
+
+ def get_rule_uid(self, rule_id):
+ """
+ Returns the UID of a rule by its ID.
+ """
+ rule_uid = self.tables.get("rule", {}).get(rule_id, {}).get("rule_uid", None)
+ if rule_uid is None:
+ raise KeyError(f"Rule ID {rule_id} not found in database.")
+ return rule_uid
+
+ def get_nwobj_member_mappings(self):
+ member_uids_db = {}
+ for objgrp in self.get_table("objgrp").values():
+ if not objgrp.get("active", True):
+ continue
+ objgrp_id = objgrp["objgrp_id"]
+ uid = self.get_nwobj_uid(objgrp_id)
+ if uid not in member_uids_db:
+ member_uids_db[uid] = set()
+ member_id = objgrp["objgrp_member_id"]
+ member_uid_db = self.get_nwobj_uid(member_id)
+ member_uids_db[uid].add(member_uid_db)
+ return member_uids_db
+
+ def get_svc_member_mappings(self):
+ member_uids_db = {}
+ for svcgrp in self.get_table("svcgrp").values():
+ if not svcgrp.get("active", True):
+ continue
+ svcgrp_id = svcgrp["svcgrp_id"]
+ uid = self.get_svc_uid(svcgrp_id)
+ if uid not in member_uids_db:
+ member_uids_db[uid] = set()
+ member_id = svcgrp["svcgrp_member_id"]
+ member_uid_db = self.get_svc_uid(member_id)
+ member_uids_db[uid].add(member_uid_db)
+ return member_uids_db
+
+ def get_user_member_mappings(self):
+ member_uids_db = {}
+ for usergrp in self.get_table("usergrp").values():
+ if not usergrp.get("active", True):
+ continue
+ usergrp_id = usergrp["usergrp_id"]
+ uid = self.get_user_uid(usergrp_id)
+ if uid not in member_uids_db:
+ member_uids_db[uid] = set()
+ member_id = usergrp["usergrp_member_id"]
+ member_uid_db = self.get_user_uid(member_id)
+ member_uids_db[uid].add(member_uid_db)
+ return member_uids_db
+
+ def get_nwobj_flat_member_mappings(self):
+ flat_member_uids_db = {}
+ for objgrp_flat in self.get_table("objgrp_flat").values():
+ if not objgrp_flat.get("active", True):
+ continue
+ objgrp_flat_id = objgrp_flat["objgrp_flat_id"]
+ uid = self.get_nwobj_uid(objgrp_flat_id)
+ if uid not in flat_member_uids_db:
+ flat_member_uids_db[uid] = set()
+ member_id = objgrp_flat["objgrp_flat_member_id"]
+ member_uid_db = self.get_nwobj_uid(member_id)
+ flat_member_uids_db[uid].add(member_uid_db)
+
+ return flat_member_uids_db
+
+ def get_svc_flat_member_mappings(self):
+ flat_member_uids_db = {}
+ for svcgrp_flat in self.get_table("svcgrp_flat").values():
+ if not svcgrp_flat.get("active", True):
+ continue
+ svcgrp_flat_id = svcgrp_flat["svcgrp_flat_id"]
+ uid = self.get_svc_uid(svcgrp_flat_id)
+ if uid not in flat_member_uids_db:
+ flat_member_uids_db[uid] = set()
+ member_id = svcgrp_flat["svcgrp_flat_member_id"]
+ member_uid_db = self.get_svc_uid(member_id)
+ flat_member_uids_db[uid].add(member_uid_db)
+
+ return flat_member_uids_db
+
+ def get_user_flat_member_mappings(self):
+ flat_member_uids_db = {}
+ for usergrp_flat in self.get_table("usergrp_flat").values():
+ if not usergrp_flat.get("active", True):
+ continue
+ usergrp_flat_id = usergrp_flat["usergrp_flat_id"]
+ uid = self.get_user_uid(usergrp_flat_id)
+ if uid not in flat_member_uids_db:
+ flat_member_uids_db[uid] = set()
+ member_id = usergrp_flat["usergrp_flat_member_id"]
+ member_uid_db = self.get_user_uid(member_id)
+ flat_member_uids_db[uid].add(member_uid_db)
+
+ return flat_member_uids_db
+
+ def get_rule_from_mappings(self):
+ rule_froms_db = {}
+ for rule_from in self.get_table("rule_from").values():
+ if not rule_from.get("active", True):
+ continue
+ rule_id = rule_from["rule_id"]
+ uid = self.get_rule_uid(rule_id)
+ if uid not in rule_froms_db:
+ rule_froms_db[uid] = set()
+ obj_id = rule_from["obj_id"]
+ obj_uid = self.get_nwobj_uid(obj_id)
+ member_string_db = obj_uid
+ user_id = rule_from["user_id"]
+ if user_id is not None:
+ user_uid = self.get_user_uid(user_id)
+ member_string_db += fwo_const.USER_DELIMITER + user_uid
+
+ rule_froms_db[uid].add(member_string_db)
+ return rule_froms_db
+
+ def get_rule_to_mappings(self):
+ rule_tos_db = {}
+ for rule_to in self.get_table("rule_to").values():
+ if not rule_to.get("active", True):
+ continue
+ rule_id = rule_to["rule_id"]
+ uid = self.get_rule_uid(rule_id)
+ if uid not in rule_tos_db:
+ rule_tos_db[uid] = set()
+ obj_id = rule_to["obj_id"]
+ obj_uid = self.get_nwobj_uid(obj_id)
+ member_string_db = obj_uid
+ user_id = rule_to["user_id"]
+ if user_id is not None:
+ user_uid = self.get_user_uid(user_id)
+ member_string_db += fwo_const.USER_DELIMITER + user_uid
+
+ rule_tos_db[uid].add(member_string_db)
+ return rule_tos_db
+
+ def get_rule_svc_mappings(self):
+ rule_svcs_db = {}
+ for rule_svc in self.get_table("rule_service").values():
+ if not rule_svc.get("active", True):
+ continue
+ rule_id = rule_svc["rule_id"]
+ uid = self.get_rule_uid(rule_id)
+ if uid not in rule_svcs_db:
+ rule_svcs_db[uid] = set()
+ svc_id = rule_svc["svc_id"]
+ svc_uid = self.get_svc_uid(svc_id)
+ rule_svcs_db[uid].add(svc_uid)
+ return rule_svcs_db
+
+ def get_rule_nwobj_resolved_mappings(self):
+ rule_nwobj_resolved_db = {}
+ for rule_nwobj in self.get_table("rule_nwobj_resolved").values():
+ if not rule_nwobj.get("active", True):
+ continue
+ rule_id = rule_nwobj["rule_id"]
+ uid = self.get_rule_uid(rule_id)
+ if uid not in rule_nwobj_resolved_db:
+ rule_nwobj_resolved_db[uid] = set()
+ obj_id = rule_nwobj["obj_id"]
+ obj_uid = self.get_nwobj_uid(obj_id)
+ rule_nwobj_resolved_db[uid].add(obj_uid)
+ return rule_nwobj_resolved_db
+
+ def get_rule_svc_resolved_mappings(self):
+ rule_svc_resolved_db = {}
+ for rule_svc_resolved in self.get_table("rule_svc_resolved").values():
+ if not rule_svc_resolved.get("active", True):
+ continue
+ rule_id = rule_svc_resolved["rule_id"]
+ uid = self.get_rule_uid(rule_id)
+ if uid not in rule_svc_resolved_db:
+ rule_svc_resolved_db[uid] = set()
+ svc_id = rule_svc_resolved["svc_id"]
+ svc_uid = self.get_svc_uid(svc_id)
+ rule_svc_resolved_db[uid].add(svc_uid)
+ return rule_svc_resolved_db
+
+ def store_latest_config(self, config, import_state):
+ """
+ Stores the latest configuration in the mock database.
+ """
+ if "latest_config" not in self.tables:
+ self.tables["latest_config"] = {}
+ import_id = import_state.ImportId
+ mgm_id = import_state.MgmDetails.Id
+ self.tables["latest_config"][import_id] = {
+ "import_id": import_id,
+ "mgm_id": mgm_id,
+ "config": config.json(),
+ }
diff --git a/roles/importer/files/importer/test/mocking/mock_import_state.py b/roles/importer/files/importer/test/mocking/mock_import_state.py
new file mode 100644
index 0000000000..99358696bf
--- /dev/null
+++ b/roles/importer/files/importer/test/mocking/mock_import_state.py
@@ -0,0 +1,146 @@
+from fwo_api_call import FwoApiCall
+from model_controllers.fworch_config_controller import FworchConfigController
+from model_controllers.import_state_controller import ImportStateController
+from models.action import Action
+from models.import_state import ImportState
+from models.track import Track
+
+from .mock_fwo_api_oo import MockFwoApi
+
+try:
+ from .mock_management_controller import MockManagementController
+except ModuleNotFoundError:
+ from .mock_management_controller import MockManagementController
+
+
+def make_hashable(obj):
+ if isinstance(obj, dict):
+ return tuple(sorted((k, make_hashable(v)) for k, v in obj.items()))
+ if isinstance(obj, (list, set, tuple)):
+ return tuple(make_hashable(i) for i in obj)
+ return obj
+
+
+class MockImportStateController(ImportStateController):
+ """
+ Mock class for ImportState.
+ """
+
+ _stub_setCoreData: bool = False
+
+ def __init__(self, import_id: int = 0, stub_setCoreData: bool = False):
+ """
+ Initializes without calling base init. This avoids the necessity to provide JWT and management details.
+ """
+ self._stub_setCoreData = stub_setCoreData
+
+ self.state = ImportState()
+ self.call_log = []
+ self.stub_responses = {}
+ self.import_version = 9
+ self.state.import_id = import_id
+ self.state.fwo_config = FworchConfigController(
+ fwo_api_url="",
+ fwo_user_mgmt_api_uri="",
+ importer_pwd="",
+ api_fetch_size=500,
+ )
+
+ self.state.mgm_details = MockManagementController()
+ self.api_connection = MockFwoApi()
+ self.api_call = FwoApiCall(self.api_connection)
+ self.set_core_data()
+
+ self.state.tracks = {"ordered": 2, "inline": 3, "concatenated": 4, "domain": 5}
+ self.state.link_types = {
+ "ordered": 2,
+ "inline": 3,
+ "concatenated": 4,
+ "domain": 5,
+ }
+
+ @property
+ def stub_setCoreData(self) -> bool:
+ """
+ Indicates whether to stub setCoreData.
+ """
+ return self._stub_setCoreData
+
+ @stub_setCoreData.setter
+ def stub_setCoreData(self, value: bool):
+ self._stub_setCoreData = value
+
+ def set_core_data(self):
+ if self._stub_setCoreData:
+ return
+ super().set_core_data()
+
+ def call(self, *args, **kwargs):
+ self.call_log.append((args, kwargs))
+ key = (make_hashable(args), make_hashable(kwargs))
+
+ if key in self.stub_responses:
+ return self.stub_responses[key]
+
+ return self.api_connection.call(*args, **kwargs)
+
+ def setup_response(self, args, kwargs, response):
+ key = (args, make_hashable(kwargs))
+ self.stub_responses[key] = response
+
+ dummy_track = Track(
+ track_id=0,
+ track_name="Dummy Track",
+ )
+
+ def lookup_track(self, trackStr):
+ if trackStr not in self.track_id_map:
+ self.track_id_map[trackStr] = len(self.track_id_map) + 1
+ return self.track_id_map[trackStr]
+
+ def lookupTrackStr(self, track_id):
+ for track_name, id_ in self.track_id_map.items():
+ if id_ == track_id:
+ return track_name
+ return None
+
+ dummy_action = Action(action_id=0, action_name="Dummy Action", allowed=True)
+
+ def lookup_action(self, actionStr):
+ if actionStr not in self.action_id_map:
+ self.action_id_map[actionStr] = len(self.action_id_map) + 1
+ return self.action_id_map[actionStr]
+
+ def lookupActionStr(self, action_id):
+ for action_name, id_ in self.action_id_map.items():
+ if id_ == action_id:
+ return action_name
+ return None
+
+ def lookup_link_type(self, linkUid):
+ if linkUid not in self.track_id_map:
+ self.track_id_map[linkUid] = len(self.track_id_map) + 1
+ return self.track_id_map[linkUid]
+
+ def lookupLinkTypeUid(self, linkId):
+ for linkUid, id_ in self.track_id_map.items():
+ if id_ == linkId:
+ return linkUid
+ return None
+
+ def lookup_gateway_id(self, gwUid):
+ if gwUid not in self.network_object_id_map:
+ self.network_object_id_map[gwUid] = len(self.network_object_id_map) + 1
+ return self.network_object_id_map[gwUid]
+
+ def lookupGatewayUid(self, gwId):
+ for gwUid, id_ in self.network_object_id_map.items():
+ if id_ == gwId:
+ return gwUid
+ return None
+
+ def lookupColorStr(self, color_id):
+ for color_str, id_ in self.ColorMap.items():
+ if id_ == color_id:
+ return color_str
+ return None
diff --git a/roles/importer/files/importer/test/mocking/mock_management_controller.py b/roles/importer/files/importer/test/mocking/mock_management_controller.py
new file mode 100644
index 0000000000..76f2c99150
--- /dev/null
+++ b/roles/importer/files/importer/test/mocking/mock_management_controller.py
@@ -0,0 +1,30 @@
+from model_controllers.management_controller import (
+ ConnectionInfo,
+ CredentialInfo,
+ DeviceInfo,
+ DomainInfo,
+ ManagementController,
+ ManagerInfo,
+)
+
+
+class MockManagementController(ManagementController):
+ def __init__(self, is_super_manager: bool = False):
+ """
+ Initializes with minimal required data for testing.
+ """
+ # Call parent constructor with required parameters
+ super().__init__(
+ mgm_id=3,
+ uid="mock-uid",
+ devices=[],
+ device_info=DeviceInfo(name="Mock Management", type_name="MockDevice", type_version="1.0"),
+ connection_info=ConnectionInfo(hostname="mock.example.com", port=443),
+ importer_hostname="mock-importer",
+ credential_info=CredentialInfo(
+ secret="mock-secret", import_user="mock-user", cloud_client_id="", cloud_client_secret=""
+ ),
+ manager_info=ManagerInfo(is_super_manager=is_super_manager, sub_manager_ids=[], sub_managers=[]),
+ domain_info=DomainInfo(domain_name="mock-domain", domain_uid="mock-domain-uid"),
+ import_disabled=False,
+ )
diff --git a/roles/importer/files/importer/test/mocking/mock_rulebase.py b/roles/importer/files/importer/test/mocking/mock_rulebase.py
new file mode 100644
index 0000000000..fcde0f0166
--- /dev/null
+++ b/roles/importer/files/importer/test/mocking/mock_rulebase.py
@@ -0,0 +1,17 @@
+class MockRulebase:
+ id = None
+ uid = ""
+ name = ""
+ mgm_uid = ""
+ is_global = False
+ rules = {}
+
+ def to_dict(self):
+ return {
+ "id": self.id,
+ "uid": self.uid,
+ "name": self.name,
+ "mgm_uid": self.mgm_uid,
+ "is_global": self.is_global,
+ "rules": self.rules,
+ }
diff --git a/roles/importer/files/importer/test/mocking/uid_manager.py b/roles/importer/files/importer/test/mocking/uid_manager.py
new file mode 100644
index 0000000000..929432830b
--- /dev/null
+++ b/roles/importer/files/importer/test/mocking/uid_manager.py
@@ -0,0 +1,18 @@
+import uuid
+
+
+class UidManager:
+ def __init__(self):
+ self.used_uids = []
+
+ def create_uid(self):
+ need_new_uid = True
+ new_uid = ""
+
+ while need_new_uid:
+ new_uid = str(uuid.uuid4())
+ if new_uid not in self.used_uids:
+ self.used_uids.append(new_uid)
+ need_new_uid = False
+
+ return new_uid
diff --git a/roles/importer/files/importer/test/test_fOS_normalize_access_rules.py b/roles/importer/files/importer/test/test_fOS_normalize_access_rules.py
new file mode 100644
index 0000000000..0fd0fac68d
--- /dev/null
+++ b/roles/importer/files/importer/test/test_fOS_normalize_access_rules.py
@@ -0,0 +1,65 @@
+import unittest
+from unittest.mock import MagicMock, patch
+
+
+class TestNormalizeAccessRules(unittest.TestCase):
+ @patch("fortiosmanagementREST.fOS_zone.add_zone_if_missing", return_value="zone_1")
+ @patch("fortiosmanagementREST.fOS_rule.resolve_objects", side_effect=lambda name, **kwargs: f"ref_{name}")
+ @patch("fortiosmanagementREST.fOS_common.add_users_to_rule")
+ @patch("fwo_log.getFwoLogger")
+ @unittest.skip("Temporary deactivated, because test is deprecated.")
+ def test_basic_rule_normalization(self, mock_logger, mock_add_users, mock_resolve, mock_add_zone):
+ global list_delimiter
+ list_delimiter = ","
+
+ full_config = {
+ "rules": {
+ "rules": [
+ {
+ "policyid": 1,
+ "uuid": "abc-123",
+ "action": "accept",
+ "status": "enable",
+ "logtraffic": "utm",
+ "_last_hit": 1722796800, # 2024-08-05
+ "srcaddr": [{"name": "src1"}],
+ "dstaddr": [{"name": "dst1"}],
+ "service": [{"name": "svc1"}],
+ "srcaddr6": [],
+ "dstaddr6": [],
+ "srcintf": [{"name": "if1"}],
+ "dstintf": [{"name": "if2"}],
+ "srcaddr-negate": "disable",
+ "dstaddr-negate": "disable",
+ "service-negate": "disable",
+ "comments": "some comment",
+ }
+ ]
+ },
+ "nw_obj_lookup_dict": {},
+ }
+
+ config2import = {}
+ mgm_details = MagicMock()
+ mgm_details.Devices = [{"name": "firewall1"}]
+
+ rules = config2import.get("rules")
+ self.assertEqual(len(rules), 1)
+ rule = rules[0]
+
+ self.assertEqual(rule["rule_ruleid"], 1)
+ self.assertEqual(rule["rule_uid"], "abc-123")
+ self.assertEqual(rule["rule_action"], "Accept")
+ self.assertEqual(rule["rule_disabled"], False)
+ self.assertEqual(rule["rule_track"], "Log")
+ self.assertEqual(rule["last_hit"], "2024-08-05")
+ self.assertEqual(rule["rule_src_refs"], "ref_src1")
+ self.assertEqual(rule["rule_dst_refs"], "ref_dst1")
+ self.assertEqual(rule["rule_svc_refs"], "svc1")
+ self.assertEqual(rule["rule_from_zone"], "zone_1")
+ self.assertEqual(rule["rule_to_zone"], "zone_1")
+ self.assertEqual(rule["rule_comment"], "some comment")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/roles/importer/files/importer/test/test_fwconfig_import_consistency.py b/roles/importer/files/importer/test/test_fwconfig_import_consistency.py
new file mode 100644
index 0000000000..27993a9dc1
--- /dev/null
+++ b/roles/importer/files/importer/test/test_fwconfig_import_consistency.py
@@ -0,0 +1,590 @@
+import os
+import sys
+import unittest
+
+sys.path.append(os.path.join(os.path.dirname(__file__), "../importer"))
+
+import fwo_const
+from model_controllers.fwconfig_import import FwConfigImport
+from services.enums import Lifetime, Services # type: ignore
+from services.global_state import GlobalState # type: ignore
+from services.group_flats_mapper import GroupFlatsMapper # type: ignore
+from services.service_provider import ServiceProvider # type: ignore
+from services.uid2id_mapper import Uid2IdMapper # type: ignore
+from test.mocking.mock_config import MockFwConfigNormalizedBuilder
+from test.mocking.mock_import_state import MockImportStateController
+from test.tools.set_up_test import set_up_config_for_import_consistency_test
+
+
+def find_first_diff(a, b, path="root"):
+ if type(a) is not type(b):
+ return f"Type mismatch at {path}: {type(a)} != {type(b)}"
+ if isinstance(a, dict):
+ return _find_first_diff_in_dict(a, b, path)
+ if isinstance(a, list):
+ return _find_first_diff_in_list(a, b, path)
+ if a != b:
+ return f"Value mismatch at {path}: {a} != {b}"
+ return None
+
+
+def _find_first_diff_in_list(a, b, path="root"):
+ for i, (x, y) in enumerate(zip(a, b, strict=False)):
+ res = find_first_diff(x, y, f"{path}[{i}]")
+ if res:
+ return res
+ if len(a) != len(b):
+ return f"list length mismatch at {path}: {len(a)} != {len(b)}"
+ return None
+
+
+def _find_first_diff_in_dict(a, b, path="root"):
+ for k in a:
+ if k not in b:
+ return f"Key '{k}' missing in second object at {path}"
+ res = find_first_diff(a[k], b[k], f"{path}.{k}")
+ if res:
+ return res
+ for k in b:
+ if k not in a:
+ return f"Key '{k}' missing in first object at {path}"
+ return None
+
+
+def reset_importer_with_new_config(config, mock_api, import_id=0) -> tuple[FwConfigImport, MockImportStateController]:
+ service_provider = ServiceProvider()
+
+ import_state = MockImportStateController(import_id, True)
+ if mock_api:
+ import_state.api_connection = mock_api
+ global_state = GlobalState()
+ global_state.import_state = import_state
+ global_state.normalized_config = config
+
+ try:
+ service_provider.dispose_service(Services.GLOBAL_STATE)
+ service_provider.dispose_service(Services.GROUP_FLATS_MAPPER)
+ service_provider.dispose_service(Services.PREV_GROUP_FLATS_MAPPER)
+ service_provider.dispose_service(Services.UID2ID_MAPPER)
+ except ValueError:
+ pass
+ service_provider.register(Services.GLOBAL_STATE, lambda: global_state, Lifetime.SINGLETON)
+ service_provider.register(Services.GROUP_FLATS_MAPPER, lambda: GroupFlatsMapper(), Lifetime.IMPORT)
+ service_provider.register(Services.PREV_GROUP_FLATS_MAPPER, lambda: GroupFlatsMapper(), Lifetime.IMPORT)
+ service_provider.register(Services.UID2ID_MAPPER, lambda: Uid2IdMapper(), Lifetime.IMPORT)
+ config_importer = FwConfigImport()
+
+ return config_importer, import_state
+
+
+def get_nwobj_member_mapping(config):
+ return {
+ obj.obj_uid: set(obj.obj_member_refs.split(fwo_const.LIST_DELIMITER))
+ for obj in config.network_objects.values()
+ if obj.obj_typ == "group" and obj.obj_member_refs
+ }
+
+
+def get_svc_member_mapping(config):
+ return {
+ svc.svc_uid: set(svc.svc_member_refs.split(fwo_const.LIST_DELIMITER))
+ for svc in config.service_objects.values()
+ if svc.svc_typ == "group" and svc.svc_member_refs
+ }
+
+
+def get_nwobj_flat_member_mapping(config, group_flats_mapper):
+ return {
+ obj.obj_uid: set(group_flats_mapper.get_network_object_flats([obj.obj_uid]))
+ for obj in config.network_objects.values()
+ if obj.obj_typ == "group"
+ }
+
+
+def get_svc_flat_member_mapping(config, group_flats_mapper):
+ return {
+ svc.svc_uid: set(group_flats_mapper.get_service_object_flats([svc.svc_uid]))
+ for svc in config.service_objects.values()
+ if svc.svc_typ == "group"
+ }
+
+
+def get_rule_from_mapping(config):
+ return {
+ rule.rule_uid: set(rule.rule_src_refs.split(fwo_const.LIST_DELIMITER))
+ for rulebase in config.rulebases
+ for rule in rulebase.rules.values()
+ }
+
+
+def get_rule_svc_mapping(config):
+ return {
+ rule.rule_uid: set(rule.rule_svc_refs.split(fwo_const.LIST_DELIMITER))
+ for rulebase in config.rulebases
+ for rule in rulebase.rules.values()
+ }
+
+
+def get_rule_nwobj_resolved_mapping(config, group_flats_mapper):
+ return {
+ rule.rule_uid: set(
+ group_flats_mapper.get_network_object_flats(
+ [
+ ref.split(fwo_const.USER_DELIMITER)[0]
+ for ref in rule.rule_src_refs.split(fwo_const.LIST_DELIMITER)
+ + rule.rule_dst_refs.split(fwo_const.LIST_DELIMITER)
+ ]
+ )
+ )
+ for rulebase in config.rulebases
+ for rule in rulebase.rules.values()
+ }
+
+
+def get_rule_svc_resolved_mapping(config, group_flats_mapper):
+ return {
+ rule.rule_uid: set(
+ group_flats_mapper.get_service_object_flats(rule.rule_svc_refs.split(fwo_const.LIST_DELIMITER))
+ )
+ for rulebase in config.rulebases
+ for rule in rulebase.rules.values()
+ }
+
+
+class TestFwoConfigImportConsistency(unittest.TestCase):
+ @unittest.skip("Temporary deactivated, because test is deprecated.")
+ def test_fwconfig_compare_config_against_db_state(self):
+ # Arrange
+ config = set_up_config_for_import_consistency_test()
+
+ config_importer, import_state = reset_importer_with_new_config(config, None, import_id=0)
+
+ service_provider = ServiceProvider()
+
+ # Act
+ config_importer.import_single_config()
+ mock_api = import_state.api_connection
+ config_from_api = mock_api.build_config_from_db(import_state, config.rulebases[0].mgm_uid, config.gateways)
+
+ service_provider.dispose_service(Services.GLOBAL_STATE)
+ service_provider.dispose_service(Services.GROUP_FLATS_MAPPER)
+ service_provider.dispose_service(Services.UID2ID_MAPPER)
+
+ # check if config objects are equal, if a field is not equal, it will raise an AssertionError
+ self.assertEqual(
+ config,
+ config_from_api,
+ f"Config objects are not equal: {find_first_diff(config.dict(), config_from_api.dict())}",
+ )
+
+ @unittest.skip("Temporary deactivated, because test is deprecated.")
+ def test_fwconfig_check_db_member_tables(self):
+ # Arrange
+ config = set_up_config_for_import_consistency_test()
+ config_importer, import_state = reset_importer_with_new_config(config, None, import_id=0)
+ service_provider = ServiceProvider()
+
+ # Act
+ config_importer.import_single_config()
+ mock_api = import_state.api_connection
+
+ group_flats_mapper = service_provider.get_group_flats_mapper()
+
+ service_provider.dispose_service(Services.GLOBAL_STATE)
+ service_provider.dispose_service(Services.GROUP_FLATS_MAPPER)
+ service_provider.dispose_service(Services.UID2ID_MAPPER)
+
+ try:
+ member_uids_config = get_nwobj_member_mapping(config)
+ member_uids_db = mock_api.get_nwobj_member_mappings()
+
+ flat_member_uids_config = get_nwobj_flat_member_mapping(config, group_flats_mapper)
+ flat_member_uids_db = mock_api.get_nwobj_flat_member_mappings()
+
+ rule_froms_config = get_rule_from_mapping(config)
+ rule_froms_db = mock_api.get_rule_from_mappings()
+
+ rule_nwobj_resolveds_config = get_rule_nwobj_resolved_mapping(config, group_flats_mapper)
+ rule_nwobj_resolveds_db = mock_api.get_rule_nwobj_resolved_mappings()
+ except Exception as e:
+ self.fail(f"Failed to retrieve member mappings from the database: {e}")
+
+ self.assertEqual(
+ member_uids_config,
+ member_uids_db,
+ f"Member UIDs in config and DB do not match: {find_first_diff(member_uids_config, member_uids_db)}",
+ )
+
+ self.assertEqual(
+ flat_member_uids_config,
+ flat_member_uids_db,
+ f"Flat member UIDs in config and DB do not match: {find_first_diff(flat_member_uids_config, flat_member_uids_db)}",
+ )
+
+ self.assertEqual(
+ rule_froms_config,
+ rule_froms_db,
+ f"Rule froms in config and DB do not match: {find_first_diff(rule_froms_config, rule_froms_db)}",
+ )
+
+ self.assertEqual(
+ rule_nwobj_resolveds_config,
+ rule_nwobj_resolveds_db,
+ f"Rule resolveds in config and DB do not match: {find_first_diff(rule_nwobj_resolveds_config, rule_nwobj_resolveds_db)}",
+ )
+
+ @unittest.skip("Temporary deactivated, because test is deprecated.")
+ def test_fwconfig_check_db_member_tables_after_deletes(self):
+ # Arrange
+ config = set_up_config_for_import_consistency_test()
+ config_importer, import_state = reset_importer_with_new_config(config, None, import_id=0)
+ service_provider = ServiceProvider()
+
+ # Act
+ config_importer.import_single_config()
+ config_importer.storeLatestConfig()
+ mock_api = import_state.api_connection
+
+ config_builder = MockFwConfigNormalizedBuilder()
+
+ config_builder.change_rule_with_nested_groups(config, change_type="delete", change_obj="from")
+ config_importer, import_state = reset_importer_with_new_config(config, mock_api, import_id=0)
+ config_importer.import_single_config()
+ config_importer.storeLatestConfig()
+
+ config_builder.change_rule_with_nested_groups(config, change_type="delete", change_obj="svc")
+ config_importer, import_state = reset_importer_with_new_config(config, mock_api, import_id=1)
+ config_importer.import_single_config()
+ config_importer.storeLatestConfig()
+
+ config_builder.change_rule_with_nested_groups(config, change_type="delete", change_obj="member")
+ config_importer, import_state = reset_importer_with_new_config(config, mock_api, import_id=2)
+ config_importer.import_single_config()
+ config_importer.storeLatestConfig()
+
+ config_builder.change_rule_with_nested_groups(config, change_type="delete", change_obj="member_svc")
+ config_importer, import_state = reset_importer_with_new_config(config, mock_api, import_id=3)
+ config_importer.import_single_config()
+ config_importer.storeLatestConfig()
+
+ config_builder.change_rule_with_nested_groups(config, change_type="delete", change_obj="nested_member")
+ config_importer, import_state = reset_importer_with_new_config(config, mock_api, import_id=4)
+ config_importer.import_single_config()
+ config_importer.storeLatestConfig()
+
+ group_flats_mapper = service_provider.get_group_flats_mapper()
+
+ service_provider.dispose_service(Services.GLOBAL_STATE)
+ service_provider.dispose_service(Services.GROUP_FLATS_MAPPER)
+ service_provider.dispose_service(Services.UID2ID_MAPPER)
+
+ member_uids_config = get_nwobj_member_mapping(config)
+ member_uids_db = mock_api.get_nwobj_member_mappings()
+
+ svc_member_uids_config = get_svc_member_mapping(config)
+ svc_member_uids_db = mock_api.get_svc_member_mappings()
+
+ flat_member_uids_config = get_nwobj_flat_member_mapping(config, group_flats_mapper)
+ flat_member_uids_db = mock_api.get_nwobj_flat_member_mappings()
+
+ svc_flat_member_uids_config = get_svc_flat_member_mapping(config, group_flats_mapper)
+ svc_flat_member_uids_db = mock_api.get_svc_flat_member_mappings()
+
+ rule_froms_config = get_rule_from_mapping(config)
+ rule_froms_db = mock_api.get_rule_from_mappings()
+
+ rule_svcs_config = get_rule_svc_mapping(config)
+ rule_svcs_db = mock_api.get_rule_svc_mappings()
+
+ rule_nwobj_resolveds_config = get_rule_nwobj_resolved_mapping(config, group_flats_mapper)
+ rule_nwobj_resolveds_db = mock_api.get_rule_nwobj_resolved_mappings()
+
+ rule_svc_resolveds_config = get_rule_svc_resolved_mapping(config, group_flats_mapper)
+ rule_svc_resolveds_db = mock_api.get_rule_svc_resolved_mappings()
+
+ self.assertEqual(
+ member_uids_config,
+ member_uids_db,
+ f"Member UIDs in config and DB do not match: {find_first_diff(member_uids_config, member_uids_db)}",
+ )
+ self.assertEqual(
+ svc_member_uids_config,
+ svc_member_uids_db,
+ f"Service member UIDs in config and DB do not match: {find_first_diff(svc_member_uids_config, svc_member_uids_db)}",
+ )
+ self.assertEqual(
+ flat_member_uids_config,
+ flat_member_uids_db,
+ f"Flat member UIDs in config and DB do not match: {find_first_diff(flat_member_uids_config, flat_member_uids_db)}",
+ )
+ self.assertEqual(
+ svc_flat_member_uids_config,
+ svc_flat_member_uids_db,
+ f"Service flat member UIDs in config and DB do not match: {find_first_diff(svc_flat_member_uids_config, svc_flat_member_uids_db)}",
+ )
+ self.assertEqual(
+ rule_froms_config,
+ rule_froms_db,
+ f"Rule froms in config and DB do not match: {find_first_diff(rule_froms_config, rule_froms_db)}",
+ )
+ self.assertEqual(
+ rule_svcs_config,
+ rule_svcs_db,
+ f"Rule services in config and DB do not match: {find_first_diff(rule_svcs_config, rule_svcs_db)}",
+ )
+ self.assertEqual(
+ rule_nwobj_resolveds_config,
+ rule_nwobj_resolveds_db,
+ f"Rule resolveds in config and DB do not match: {find_first_diff(rule_nwobj_resolveds_config, rule_nwobj_resolveds_db)}",
+ )
+ self.assertEqual(
+ rule_svc_resolveds_config,
+ rule_svc_resolveds_db,
+ f"Rule service resolveds in config and DB do not match: {find_first_diff(rule_svc_resolveds_config, rule_svc_resolveds_db)}",
+ )
+
+ config_from_api = mock_api.build_config_from_db(import_state, config.rulebases[0].mgm_uid, config.gateways)
+
+ self.assertEqual(
+ config,
+ config_from_api,
+ f"Config objects are not equal after import with deletions: {find_first_diff(config.dict(), config_from_api.dict())}",
+ )
+
+ @unittest.skip("Temporary deactivated, because test is deprecated.")
+ def test_fwconfig_check_db_member_tables_after_adds(self):
+ # Arrange
+ config = set_up_config_for_import_consistency_test()
+ config_importer, import_state = reset_importer_with_new_config(config, None, import_id=0)
+ service_provider = ServiceProvider()
+
+ # Act
+ config_importer.import_single_config()
+ config_importer.storeLatestConfig()
+ mock_api = import_state.api_connection
+
+ config_builder = MockFwConfigNormalizedBuilder()
+
+ config_builder.change_rule_with_nested_groups(config, change_type="add", change_obj="from")
+ config_importer, import_state = reset_importer_with_new_config(config, mock_api, import_id=1)
+ config_importer.import_single_config()
+ config_importer.storeLatestConfig()
+
+ config_builder.change_rule_with_nested_groups(config, change_type="add", change_obj="svc")
+ config_importer, import_state = reset_importer_with_new_config(config, mock_api, import_id=2)
+ config_importer.import_single_config()
+ config_importer.storeLatestConfig()
+
+ config_builder.change_rule_with_nested_groups(config, change_type="add", change_obj="member")
+ config_importer, import_state = reset_importer_with_new_config(config, mock_api, import_id=3)
+ config_importer.import_single_config()
+ config_importer.storeLatestConfig()
+
+ config_builder.change_rule_with_nested_groups(config, change_type="add", change_obj="member_svc")
+ config_importer, import_state = reset_importer_with_new_config(config, mock_api, import_id=4)
+ config_importer.import_single_config()
+ config_importer.storeLatestConfig()
+
+ config_builder.change_rule_with_nested_groups(config, change_type="add", change_obj="nested_member")
+ config_importer, import_state = reset_importer_with_new_config(config, mock_api, import_id=5)
+ config_importer.import_single_config()
+ config_importer.storeLatestConfig()
+
+ config_builder.change_rule_with_nested_groups(config, change_type="add", change_obj="nested_member_svc")
+ config_importer, import_state = reset_importer_with_new_config(config, mock_api, import_id=6)
+ config_importer.import_single_config()
+ config_importer.storeLatestConfig()
+
+ group_flats_mapper = service_provider.get_group_flats_mapper()
+
+ service_provider.dispose_service(Services.GLOBAL_STATE)
+ service_provider.dispose_service(Services.GROUP_FLATS_MAPPER)
+ service_provider.dispose_service(Services.UID2ID_MAPPER)
+
+ member_uids_config = get_nwobj_member_mapping(config)
+ member_uids_db = mock_api.get_nwobj_member_mappings()
+
+ svc_member_uids_config = get_svc_member_mapping(config)
+ svc_member_uids_db = mock_api.get_svc_member_mappings()
+
+ flat_member_uids_config = get_nwobj_flat_member_mapping(config, group_flats_mapper)
+ flat_member_uids_db = mock_api.get_nwobj_flat_member_mappings()
+
+ svc_flat_member_uids_config = get_svc_flat_member_mapping(config, group_flats_mapper)
+ svc_flat_member_uids_db = mock_api.get_svc_flat_member_mappings()
+
+ rule_froms_config = get_rule_from_mapping(config)
+ rule_froms_db = mock_api.get_rule_from_mappings()
+
+ rule_svcs_config = get_rule_svc_mapping(config)
+ rule_svcs_db = mock_api.get_rule_svc_mappings()
+
+ rule_nwobj_resolveds_config = get_rule_nwobj_resolved_mapping(config, group_flats_mapper)
+ rule_nwobj_resolveds_db = mock_api.get_rule_nwobj_resolved_mappings()
+
+ rule_svc_resolveds_config = get_rule_svc_resolved_mapping(config, group_flats_mapper)
+ rule_svc_resolveds_db = mock_api.get_rule_svc_resolved_mappings()
+
+ self.assertEqual(
+ member_uids_config,
+ member_uids_db,
+ f"Member UIDs in config and DB do not match: {find_first_diff(member_uids_config, member_uids_db)}",
+ )
+ self.assertEqual(
+ svc_member_uids_config,
+ svc_member_uids_db,
+ f"Service member UIDs in config and DB do not match: {find_first_diff(svc_member_uids_config, svc_member_uids_db)}",
+ )
+ self.assertEqual(
+ flat_member_uids_config,
+ flat_member_uids_db,
+ f"Flat member UIDs in config and DB do not match: {find_first_diff(flat_member_uids_config, flat_member_uids_db)}",
+ )
+ self.assertEqual(
+ svc_flat_member_uids_config,
+ svc_flat_member_uids_db,
+ f"Service flat member UIDs in config and DB do not match: {find_first_diff(svc_flat_member_uids_config, svc_flat_member_uids_db)}",
+ )
+ self.assertEqual(
+ rule_froms_config,
+ rule_froms_db,
+ f"Rule froms in config and DB do not match: {find_first_diff(rule_froms_config, rule_froms_db)}",
+ )
+ self.assertEqual(
+ rule_svcs_config,
+ rule_svcs_db,
+ f"Rule services in config and DB do not match: {find_first_diff(rule_svcs_config, rule_svcs_db)}",
+ )
+ self.assertEqual(
+ rule_nwobj_resolveds_config,
+ rule_nwobj_resolveds_db,
+ f"Rule resolveds in config and DB do not match: {find_first_diff(rule_nwobj_resolveds_config, rule_nwobj_resolveds_db)}",
+ )
+ self.assertEqual(
+ rule_svc_resolveds_config,
+ rule_svc_resolveds_db,
+ f"Rule service resolveds in config and DB do not match: {find_first_diff(rule_svc_resolveds_config, rule_svc_resolveds_db)}",
+ )
+
+ config_from_api = mock_api.build_config_from_db(import_state, config.rulebases[0].mgm_uid, config.gateways)
+ self.assertEqual(
+ config,
+ config_from_api,
+ f"Config objects are not equal after import with additions: {find_first_diff(config.dict(), config_from_api.dict())}",
+ )
+
+ @unittest.skip("Temporary deactivated, because test is deprecated.")
+ def test_fwconfig_check_db_member_tables_after_changes(self):
+ # Arrange
+ config = set_up_config_for_import_consistency_test()
+ config_importer, import_state = reset_importer_with_new_config(config, None, import_id=0)
+ service_provider = ServiceProvider()
+
+ # Act
+ mock_api = import_state.api_connection
+
+ config_importer.import_single_config()
+ config_importer.storeLatestConfig()
+
+ config_builder = MockFwConfigNormalizedBuilder()
+
+ config_builder.change_rule_with_nested_groups(config, change_type="change", change_obj="from")
+ config_importer, import_state = reset_importer_with_new_config(config, mock_api, import_id=1)
+ config_importer.import_single_config()
+ config_importer.storeLatestConfig()
+ config_builder.change_rule_with_nested_groups(config, change_type="change", change_obj="svc")
+ config_importer, import_state = reset_importer_with_new_config(config, mock_api, import_id=2)
+ config_importer.import_single_config()
+ config_importer.storeLatestConfig()
+ config_builder.change_rule_with_nested_groups(config, change_type="change", change_obj="member")
+ config_importer, import_state = reset_importer_with_new_config(config, mock_api, import_id=3)
+ config_importer.import_single_config()
+ config_importer.storeLatestConfig()
+ config_builder.change_rule_with_nested_groups(config, change_type="change", change_obj="member_svc")
+ config_importer, import_state = reset_importer_with_new_config(config, mock_api, import_id=4)
+ config_importer.import_single_config()
+ config_importer.storeLatestConfig()
+ config_builder.change_rule_with_nested_groups(config, change_type="change", change_obj="nested_member")
+ config_importer, import_state = reset_importer_with_new_config(config, mock_api, import_id=5)
+ config_importer.import_single_config()
+ config_importer.storeLatestConfig()
+ config_builder.change_rule_with_nested_groups(
+ config, change_type="change", change_obj="member_svc"
+ ) # change again
+ config_importer, import_state = reset_importer_with_new_config(config, mock_api, import_id=6)
+ config_importer.import_single_config()
+ config_importer.storeLatestConfig()
+ group_flats_mapper = service_provider.get_group_flats_mapper()
+ service_provider.dispose_service(Services.GLOBAL_STATE)
+ service_provider.dispose_service(Services.GROUP_FLATS_MAPPER)
+ service_provider.dispose_service(Services.UID2ID_MAPPER)
+ member_uids_config = get_nwobj_member_mapping(config)
+ member_uids_db = mock_api.get_nwobj_member_mappings()
+
+ svc_member_uids_config = get_svc_member_mapping(config)
+ svc_member_uids_db = mock_api.get_svc_member_mappings()
+
+ flat_member_uids_config = get_nwobj_flat_member_mapping(config, group_flats_mapper)
+ flat_member_uids_db = mock_api.get_nwobj_flat_member_mappings()
+
+ svc_flat_member_uids_config = get_svc_flat_member_mapping(config, group_flats_mapper)
+ svc_flat_member_uids_db = mock_api.get_svc_flat_member_mappings()
+
+ rule_froms_config = get_rule_from_mapping(config)
+ rule_froms_db = mock_api.get_rule_from_mappings()
+
+ rule_svcs_config = get_rule_svc_mapping(config)
+ rule_svcs_db = mock_api.get_rule_svc_mappings()
+
+ rule_nwobj_resolveds_config = get_rule_nwobj_resolved_mapping(config, group_flats_mapper)
+ rule_nwobj_resolveds_db = mock_api.get_rule_nwobj_resolved_mappings()
+
+ rule_svc_resolveds_config = get_rule_svc_resolved_mapping(config, group_flats_mapper)
+ rule_svc_resolveds_db = mock_api.get_rule_svc_resolved_mappings()
+
+ self.assertEqual(
+ member_uids_config,
+ member_uids_db,
+ f"Member UIDs in config and DB do not match: {find_first_diff(member_uids_config, member_uids_db)}",
+ )
+ self.assertEqual(
+ svc_member_uids_config,
+ svc_member_uids_db,
+ f"Service member UIDs in config and DB do not match: {find_first_diff(svc_member_uids_config, svc_member_uids_db)}",
+ )
+ self.assertEqual(
+ flat_member_uids_config,
+ flat_member_uids_db,
+ f"Flat member UIDs in config and DB do not match: {find_first_diff(flat_member_uids_config, flat_member_uids_db)}",
+ )
+ self.assertEqual(
+ svc_flat_member_uids_config,
+ svc_flat_member_uids_db,
+ f"Service flat member UIDs in config and DB do not match: {find_first_diff(svc_flat_member_uids_config, svc_flat_member_uids_db)}",
+ )
+ self.assertEqual(
+ rule_froms_config,
+ rule_froms_db,
+ f"Rule froms in config and DB do not match: {find_first_diff(rule_froms_config, rule_froms_db)}",
+ )
+ self.assertEqual(
+ rule_svcs_config,
+ rule_svcs_db,
+ f"Rule services in config and DB do not match: {find_first_diff(rule_svcs_config, rule_svcs_db)}",
+ )
+ self.assertEqual(
+ rule_nwobj_resolveds_config,
+ rule_nwobj_resolveds_db,
+ f"Rule resolveds in config and DB do not match: {find_first_diff(rule_nwobj_resolveds_config, rule_nwobj_resolveds_db)}",
+ )
+ self.assertEqual(
+ rule_svc_resolveds_config,
+ rule_svc_resolveds_db,
+ f"Rule service resolveds in config and DB do not match: {find_first_diff(rule_svc_resolveds_config, rule_svc_resolveds_db)}",
+ )
+ config_from_api = mock_api.build_config_from_db(import_state, config.rulebases[0].mgm_uid, config.gateways)
+ self.assertEqual(
+ config,
+ config_from_api,
+ f"Config objects are not equal after import with changes: {find_first_diff(config.dict(), config_from_api.dict())}",
+ )
diff --git a/roles/importer/files/importer/test/test_fwconfig_import_ruleorder.py b/roles/importer/files/importer/test/test_fwconfig_import_ruleorder.py
new file mode 100644
index 0000000000..8310e65092
--- /dev/null
+++ b/roles/importer/files/importer/test/test_fwconfig_import_ruleorder.py
@@ -0,0 +1,260 @@
+import copy
+import unittest
+
+import fwo_local_settings
+from fwo_base import init_service_provider, register_global_state
+from fwo_const import RULE_NUM_NUMERIC_STEPS
+from fwo_log import FWOLogger
+from models.rule import RuleNormalized
+from services.enums import Services
+from services.service_provider import ServiceProvider
+from test.mocking.mock_config import MockFwConfigNormalizedBuilder
+from test.mocking.mock_fwconfig_import_rule import MockFwConfigImportRule
+from test.mocking.mock_import_state import MockImportStateController
+from test.tools.set_up_test import (
+ insert_rule_in_config,
+ move_rule_in_config,
+ remove_rule_from_rulebase,
+ update_rule_map_and_rulebase_map,
+ update_rule_num_numerics,
+)
+
+
+class TestFwConfigImportRuleOrder(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ """
+ Gets invoked one time before running any tests.
+ """
+ FWOLogger(2)
+ init_service_provider()
+ cls._service_provider = ServiceProvider()
+ register_global_state(MockImportStateController(import_id=1, stub_setCoreData=True))
+ cls._global_state = cls._service_provider.get_service(Services.GLOBAL_STATE)
+ cls._debug_level: int = 1
+
+ def setUp(self):
+ """
+ Gets invoked one time per test method before running it.
+ """
+ self._config_builder = MockFwConfigNormalizedBuilder()
+ self._config_builder.set_up()
+ self._previous_config, self._mgm_uid = self._config_builder.build_config(
+ {"rule_config": [10, 10, 10], "network_object_config": 10, "service_config": 10, "user_config": 10}
+ )
+ self._global_state.normalized_config = copy.deepcopy(self._previous_config)
+ self._global_state.previous_config = self._previous_config
+ self._fwconfig_import_rule = MockFwConfigImportRule()
+ self._fwconfig_import_rule.normalized_config = self._global_state.normalized_config
+ self._import_state = self._fwconfig_import_rule.import_details
+ self._normalized_config = self._fwconfig_import_rule.normalized_config
+
+ self._global_state.import_details = self._import_state
+ self._rule_order_service = self._service_provider.get_service(Services.RULE_ORDER_SERVICE, 1)
+
+ update_rule_num_numerics(self._previous_config)
+ update_rule_map_and_rulebase_map(self._previous_config, self._import_state)
+
+ def tearDown(self):
+ """
+ Gets invoked one time per test method after running it.
+ """
+ if fwo_local_settings.python_unit_tests_verbose:
+ outcome = self._outcome
+
+ if len(outcome.result.failures) > 0:
+ self._print_rule_num_numerics()
+
+ def _print_rule_num_numerics(self):
+ print("\nPrevious Config Rule Num Numerics:")
+ for rb in self._previous_config.rulebases:
+ for rule in rb.rules.values():
+ print(f"Rule UID: {rule.rule_uid}, rule_num_numeric: {rule.rule_num_numeric}")
+ print("\nNormalized Config Rule Num Numerics:")
+ for rb in self._normalized_config.rulebases:
+ for rule in rb.rules.values():
+ print(f"Rule UID: {rule.rule_uid}, rule_num_numeric: {rule.rule_num_numeric}")
+
+ def test_initialize_on_initial_import(self):
+ # Arrange
+
+ self._previous_config = self._config_builder.empty_config()
+ self._global_state.previous_config = self._previous_config
+
+ # Act
+
+ self._rule_order_service.update_rule_order_diffs()
+
+ # Assert
+ for rulebase in self._normalized_config.rulebases:
+ for index, rule_uid in enumerate(rulebase.rules):
+ expected_rule_num_numeric = (index + 1) * RULE_NUM_NUMERIC_STEPS
+ actual_rule_num_numeric = rulebase.rules[rule_uid].rule_num_numeric
+ self.assertTrue(
+ actual_rule_num_numeric == expected_rule_num_numeric,
+ f"Rule UID: {rule_uid}, actual rule_num_numeric: {actual_rule_num_numeric}, expected: {expected_rule_num_numeric}",
+ )
+
+ def test_initialize_on_insert_delete_and_move(self):
+ # Arrange
+
+ rulebase = self._normalized_config.rulebases[0]
+ rule_uids = list(rulebase.rules.keys())
+ removed_rule_uid = rule_uids[0]
+
+ remove_rule_from_rulebase(self._normalized_config, rulebase.uid, removed_rule_uid, rule_uids)
+ inserted_rule_uid = insert_rule_in_config(
+ self._normalized_config, rulebase.uid, 0, rule_uids, self._config_builder
+ )
+ moved_rule_uid = move_rule_in_config(self._normalized_config, rulebase.uid, 9, 0, rule_uids)
+
+ # Act
+
+ self._rule_order_service.update_rule_order_diffs()
+
+ # # Assert
+
+ self.assertTrue(
+ self._get_rule(0, inserted_rule_uid).rule_num_numeric == RULE_NUM_NUMERIC_STEPS,
+ f"Inserted rule_num_numeric is {self._normalized_config.rulebases[0].rules[inserted_rule_uid].rule_num_numeric}, expected {RULE_NUM_NUMERIC_STEPS}",
+ )
+ self.assertTrue(
+ self._get_rule(0, moved_rule_uid).rule_num_numeric == RULE_NUM_NUMERIC_STEPS / 2,
+ f"Moved rule_num_numeric is {self._normalized_config.rulebases[0].rules[moved_rule_uid].rule_num_numeric}, expected {RULE_NUM_NUMERIC_STEPS / 2}",
+ )
+
+ def test_initialize_on_consecutive_insertions(self):
+ # Arrange
+
+ rulebase = self._normalized_config.rulebases[0]
+ rule_uids = list(rulebase.rules.keys())
+
+ # Inserting three new rules at the beginning of the rulebase
+ rule_1_1_uid = insert_rule_in_config(self._normalized_config, rulebase.uid, 0, rule_uids, self._config_builder)
+ rule_1_2_uid = insert_rule_in_config(self._normalized_config, rulebase.uid, 1, rule_uids, self._config_builder)
+ rule_1_3_uid = insert_rule_in_config(self._normalized_config, rulebase.uid, 2, rule_uids, self._config_builder)
+
+ # Inserting three new rules in the middle of the rulebase
+ rule_1_6_uid = insert_rule_in_config(self._normalized_config, rulebase.uid, 5, rule_uids, self._config_builder)
+ rule_1_7_uid = insert_rule_in_config(self._normalized_config, rulebase.uid, 6, rule_uids, self._config_builder)
+ rule_1_8_uid = insert_rule_in_config(self._normalized_config, rulebase.uid, 7, rule_uids, self._config_builder)
+
+ # Inserting three new rules at the end of the rulebase
+ rule_1_17_uid = insert_rule_in_config(
+ self._normalized_config, rulebase.uid, 16, rule_uids, self._config_builder
+ )
+ rule_1_18_uid = insert_rule_in_config(
+ self._normalized_config, rulebase.uid, 17, rule_uids, self._config_builder
+ )
+ rule_1_19_uid = insert_rule_in_config(
+ self._normalized_config, rulebase.uid, 18, rule_uids, self._config_builder
+ )
+
+ # Act
+
+ self._rule_order_service.update_rule_order_diffs()
+
+ # Assert
+
+ self.assertTrue(
+ self._get_rule(0, rule_1_1_uid).rule_num_numeric == RULE_NUM_NUMERIC_STEPS / 2,
+ f"Rule 1.1 rule_num_numeric: {self._get_rule(0, rule_1_1_uid).rule_num_numeric}, expected {RULE_NUM_NUMERIC_STEPS / 2}",
+ )
+ self.assertTrue(
+ self._get_rule(0, rule_1_2_uid).rule_num_numeric == 3 * RULE_NUM_NUMERIC_STEPS / 4,
+ f"Rule 1.2 rule_num_numeric: {self._get_rule(0, rule_1_2_uid).rule_num_numeric}, expected {3 * RULE_NUM_NUMERIC_STEPS / 4}",
+ )
+ self.assertTrue(
+ self._get_rule(0, rule_1_3_uid).rule_num_numeric == 7 * RULE_NUM_NUMERIC_STEPS / 8,
+ f"Rule 1.3 rule_num_numeric: {self._get_rule(0, rule_1_3_uid).rule_num_numeric}, expected {7 * RULE_NUM_NUMERIC_STEPS / 8}",
+ )
+
+ self.assertTrue(
+ self._get_rule(0, rule_1_6_uid).rule_num_numeric == 5 * RULE_NUM_NUMERIC_STEPS / 2,
+ f"Rule 1.6 rule_num_numeric: {self._get_rule(0, rule_1_6_uid).rule_num_numeric}, expected {5 * RULE_NUM_NUMERIC_STEPS / 2}",
+ )
+ self.assertTrue(
+ self._get_rule(0, rule_1_7_uid).rule_num_numeric == 11 * RULE_NUM_NUMERIC_STEPS / 4,
+ f"Rule 1.7 rule_num_numeric: {self._get_rule(0, rule_1_7_uid).rule_num_numeric}, expected {11 * RULE_NUM_NUMERIC_STEPS / 4}",
+ )
+ self.assertTrue(
+ self._get_rule(0, rule_1_8_uid).rule_num_numeric == 23 * RULE_NUM_NUMERIC_STEPS / 8,
+ f"Rule 1.8 rule_num_numeric: {self._get_rule(0, rule_1_8_uid).rule_num_numeric}, expected {23 * RULE_NUM_NUMERIC_STEPS / 8}",
+ )
+
+ self.assertTrue(
+ self._get_rule(0, rule_1_17_uid).rule_num_numeric == 11 * RULE_NUM_NUMERIC_STEPS,
+ f"Rule 1.17 rule_num_numeric: {self._get_rule(0, rule_1_17_uid).rule_num_numeric}, expected {11 * RULE_NUM_NUMERIC_STEPS}",
+ )
+ self.assertTrue(
+ self._get_rule(0, rule_1_18_uid).rule_num_numeric == 12 * RULE_NUM_NUMERIC_STEPS,
+ f"Rule 1.18 rule_num_numeric: {self._get_rule(0, rule_1_18_uid).rule_num_numeric}, expected {12 * RULE_NUM_NUMERIC_STEPS}",
+ )
+ self.assertTrue(
+ self._get_rule(0, rule_1_19_uid).rule_num_numeric == 13 * RULE_NUM_NUMERIC_STEPS,
+ f"Rule 1.19 rule_num_numeric: {self._get_rule(0, rule_1_19_uid).rule_num_numeric}, expected {13 * RULE_NUM_NUMERIC_STEPS}",
+ )
+
+ def test_initialize_on_move_across_rulebases(self):
+ # Arrange
+
+ source_rulebase = self._fwconfig_import_rule.normalized_config.rulebases[0]
+ source_rulebase_uids = list(source_rulebase.rules.keys())
+ target_rulebase = self._fwconfig_import_rule.normalized_config.rulebases[1]
+ target_rulebase_uids = list(target_rulebase.rules.keys())
+
+ deleted_rule = remove_rule_from_rulebase(
+ self._normalized_config, source_rulebase.uid, source_rulebase_uids[0], source_rulebase_uids
+ )
+ insert_rule_in_config(
+ self._normalized_config, target_rulebase.uid, 0, target_rulebase_uids, self._config_builder, deleted_rule
+ )
+
+ # Act
+
+ self._rule_order_service.update_rule_order_diffs()
+
+ # Assert
+
+ self.assertTrue(
+ self._get_rule(1, deleted_rule.rule_uid).rule_num_numeric == RULE_NUM_NUMERIC_STEPS / 2,
+ f"Moved rule_num_numeric is {self._normalized_config.rulebases[1].rules[deleted_rule.rule_uid].rule_num_numeric}, expected {RULE_NUM_NUMERIC_STEPS / 2}",
+ )
+
+ def test_update_rulebase_diffs_on_moves_to_beginning_middle_and_end_of_rulebase(self):
+ # Arrange
+
+ rulebase = self._normalized_config.rulebases[0]
+ rule_uids = list(rulebase.rules.keys())
+
+ beginning_rule_uid = move_rule_in_config(
+ self._normalized_config, rulebase.uid, 5, 0, rule_uids
+ ) # Move to beginning
+ middle_rule_uid = move_rule_in_config(self._normalized_config, rulebase.uid, 1, 4, rule_uids) # Move to middle
+ end_rule_uid = move_rule_in_config(self._normalized_config, rulebase.uid, 2, 9, rule_uids) # Move to end
+
+ # Act
+
+ self._rule_order_service.update_rule_order_diffs()
+
+ # Assert
+
+ self.assertTrue(
+ self._get_rule(0, beginning_rule_uid).rule_num_numeric == RULE_NUM_NUMERIC_STEPS / 2,
+ f"Beginning moved rule_num_numeric is {self._normalized_config.rulebases[0].rules[beginning_rule_uid].rule_num_numeric}, expected {RULE_NUM_NUMERIC_STEPS / 2}",
+ )
+ self.assertTrue(
+ self._get_rule(0, middle_rule_uid).rule_num_numeric == 4608,
+ f"Middle moved rule_num_numeric is {self._normalized_config.rulebases[0].rules[middle_rule_uid].rule_num_numeric}, expected 4608",
+ )
+ self.assertTrue(
+ self._get_rule(0, end_rule_uid).rule_num_numeric == 11 * RULE_NUM_NUMERIC_STEPS,
+ f"End moved rule_num_numeric is {self._normalized_config.rulebases[0].rules[end_rule_uid].rule_num_numeric}, expected {11 * RULE_NUM_NUMERIC_STEPS}",
+ )
+
+ def _get_rule(self, rulebase_index: int, rule_uid: str) -> RuleNormalized:
+ """
+ Helper method to get a rule from the normalized config.
+ """
+ rulebase = self._normalized_config.rulebases[rulebase_index]
+ return rulebase.rules.get(rule_uid, None)
diff --git a/roles/importer/files/importer/test/test_fwo_base.py b/roles/importer/files/importer/test/test_fwo_base.py
new file mode 100644
index 0000000000..2f3f3d2304
--- /dev/null
+++ b/roles/importer/files/importer/test/test_fwo_base.py
@@ -0,0 +1,86 @@
+import os
+import sys
+import unittest
+
+sys.path.append(os.path.join(os.path.dirname(__file__), "../importer"))
+
+from fwo_base import compute_min_moves
+
+
+class TestFwoBase(unittest.TestCase):
+ def test_compute_min_moves_on_insert(self):
+ # arrange
+ source_sequence = ["element a", "element b", "element c"]
+ target_sequence = list(source_sequence)
+ new_element = "element d"
+ insert_position = 2
+ target_sequence.insert(insert_position, new_element)
+ expected_result = {
+ "moves": 1,
+ "operations": [f"Insert element '{new_element}' at target position {insert_position!s}."],
+ "insertions": [(insert_position, new_element)],
+ "deletions": [],
+ "reposition_moves": [],
+ }
+
+ # act
+ compute_min_moves_result = compute_min_moves(source_sequence, target_sequence)
+
+ # assert
+ self.assertEqual(compute_min_moves_result["moves"], expected_result["moves"])
+ self.assertEqual(compute_min_moves_result["operations"], expected_result["operations"])
+ self.assertEqual(compute_min_moves_result["insertions"], expected_result["insertions"])
+ self.assertEqual(compute_min_moves_result["deletions"], expected_result["deletions"])
+ self.assertEqual(compute_min_moves_result["reposition_moves"], expected_result["reposition_moves"])
+
+ def test_compute_min_moves_on_delete(self):
+ # arrange
+ source_sequence = ["element a", "element c"]
+ target_sequence = list(source_sequence)
+ delete_position = 1
+ deleted_element = target_sequence.pop(delete_position)
+ expected_result = {
+ "moves": 1,
+ "operations": [f"Delete element '{deleted_element}' at source index {delete_position!s}."],
+ "insertions": [],
+ "deletions": [(delete_position, deleted_element)],
+ "reposition_moves": [],
+ }
+
+ # act
+ compute_min_moves_result = compute_min_moves(source_sequence, target_sequence)
+
+ # assert
+ self.assertEqual(compute_min_moves_result["moves"], expected_result["moves"])
+ self.assertEqual(compute_min_moves_result["operations"], expected_result["operations"])
+ self.assertEqual(compute_min_moves_result["insertions"], expected_result["insertions"])
+ self.assertEqual(compute_min_moves_result["deletions"], expected_result["deletions"])
+ self.assertEqual(compute_min_moves_result["reposition_moves"], expected_result["reposition_moves"])
+
+ def test_compute_min_moves_on_move(self):
+ # arrange
+ source_sequence = ["element a", "element b", "element c"]
+ target_sequence = list(source_sequence)
+ move_source_position = 2
+ move_target_position = 1
+ moved_element = target_sequence.pop(move_source_position)
+ target_sequence.insert(move_target_position, moved_element)
+ expected_result = {
+ "moves": 1,
+ "operations": [
+ f"Pop element '{moved_element}' from source index {move_source_position!s} and reinsert at target position {move_target_position!s}."
+ ],
+ "insertions": [],
+ "deletions": [],
+ "reposition_moves": [(move_source_position, moved_element, move_target_position)],
+ }
+
+ # act
+ compute_min_moves_result = compute_min_moves(source_sequence, target_sequence)
+
+ # assert
+ self.assertEqual(compute_min_moves_result["moves"], expected_result["moves"])
+ self.assertEqual(compute_min_moves_result["operations"], expected_result["operations"])
+ self.assertEqual(compute_min_moves_result["insertions"], expected_result["insertions"])
+ self.assertEqual(compute_min_moves_result["deletions"], expected_result["deletions"])
+ self.assertEqual(compute_min_moves_result["reposition_moves"], expected_result["reposition_moves"])
diff --git a/roles/importer/files/importer/test/test_update_rulebase_diffs.py b/roles/importer/files/importer/test/test_update_rulebase_diffs.py
new file mode 100644
index 0000000000..9ddf21965c
--- /dev/null
+++ b/roles/importer/files/importer/test/test_update_rulebase_diffs.py
@@ -0,0 +1,273 @@
+import copy
+import unittest
+
+from fwo_base import init_service_provider, register_global_state
+from fwo_log import FWOLogger
+from models.fwconfig_normalized import FwConfigNormalized
+from services.enums import Services
+from services.service_provider import ServiceProvider
+from test.mocking.mock_config import MockFwConfigNormalizedBuilder
+from test.mocking.mock_fwconfig_import_rule import MockFwConfigImportRule
+from test.mocking.mock_import_state import MockImportStateController
+from test.tools.set_up_test import (
+ insert_rule_in_config,
+ move_rule_in_config,
+ remove_rule_from_rulebase,
+ update_rule_map_and_rulebase_map,
+ update_rule_num_numerics,
+)
+
+
+class TestUpdateRulebaseDiffs(unittest.TestCase):
+ _config_builder: MockFwConfigNormalizedBuilder
+ _fwconfig_import_rule: MockFwConfigImportRule
+ _mgm_uid: str
+ _previous_config: FwConfigNormalized
+ _normalized_config: FwConfigNormalized
+ _import_state: MockImportStateController
+ _import_id: int
+ _debug_level: int
+
+ @classmethod
+ def setUpClass(cls):
+ """
+ Gets invoked once before running any test of this class.
+ """
+ FWOLogger(2)
+ cls._config_builder = MockFwConfigNormalizedBuilder()
+ init_service_provider()
+ register_global_state(MockImportStateController(import_id=1, stub_setCoreData=True))
+ cls._service_provider = ServiceProvider()
+ cls._global_state = cls._service_provider.get_service(Services.GLOBAL_STATE)
+ cls._import_id = 0
+ cls._debug_level = 1
+
+ def setUp(self):
+ """
+ Gets invoked one time per test method before running it.
+ """
+ self._config_builder.set_up()
+
+ self._previous_config, self._mgm_uid = self._config_builder.build_config(
+ {"rule_config": [10, 10, 10], "network_object_config": 10, "service_config": 10, "user_config": 10}
+ )
+ self._global_state.normalized_config = copy.deepcopy(self._previous_config)
+ self._global_state.previous_config = self._previous_config
+
+ self._fwconfig_import_rule = MockFwConfigImportRule()
+ self._import_id += 1
+ self._fwconfig_import_rule.import_details.state.import_id = self._import_id
+ self._fwconfig_import_rule.normalized_config = self._global_state.normalized_config
+ self._import_state = self._fwconfig_import_rule.import_details
+ self._normalized_config = self._fwconfig_import_rule.normalized_config
+
+ self._global_state.import_details = self._import_state
+
+ update_rule_num_numerics(self._previous_config)
+ update_rule_map_and_rulebase_map(self._previous_config, self._import_state)
+
+ def test_update_rulebase_diffs_on_insert_delete_and_move(self):
+ # Arrange
+
+ rulebase = self._normalized_config.rulebases[0]
+ rule_uids = list(rulebase.rules.keys())
+ rule_uid = rule_uids[0]
+
+ remove_rule_from_rulebase(self._normalized_config, rulebase.uid, rule_uid, rule_uids)
+ insert_rule_in_config(self._normalized_config, rulebase.uid, 0, rule_uids, self._config_builder)
+ move_rule_in_config(self._normalized_config, rulebase.uid, 9, 0, rule_uids)
+
+ # Act
+
+ self._fwconfig_import_rule.update_rulebase_diffs(self._previous_config)
+
+ # Assert
+
+ # The order of the entries in normalized_config
+ self.assertEqual(rule_uids, list(rulebase.rules.keys()))
+
+ sorted_rulebase_rules = sorted(rulebase.rules.values(), key=lambda r: r.rule_num_numeric)
+ sorted_rulebase_rules_uids = [r.rule_uid for r in sorted_rulebase_rules]
+
+ # The sequence of the rule_num_numeric values
+ self.assertEqual(rule_uids, sorted_rulebase_rules_uids)
+
+ # Insert, delete and move recognized in ImportDetails
+ self.assertEqual(self._import_state.state.stats.statistics.rule_add_count, 1)
+ self.assertEqual(self._import_state.state.stats.statistics.rule_delete_count, 1)
+ self.assertEqual(self._import_state.state.stats.statistics.rule_change_count, 1)
+ self.assertEqual(self._import_state.state.stats.statistics.rule_move_count, 1)
+
+ def test_update_rulebase_diffs_on_consecutive_insertions(self):
+ # Arrange
+
+ rulebase = self._normalized_config.rulebases[0]
+ rule_uids = list(rulebase.rules.keys())
+
+ # Inserting three new rules at the beginning of the rulebase
+ insert_rule_in_config(self._normalized_config, rulebase.uid, 0, rule_uids, self._config_builder)
+ insert_rule_in_config(self._normalized_config, rulebase.uid, 0, rule_uids, self._config_builder)
+ insert_rule_in_config(self._normalized_config, rulebase.uid, 0, rule_uids, self._config_builder)
+
+ # Inserting three new rules in the middle of the rulebase
+ insert_rule_in_config(
+ self._normalized_config, rulebase.uid, (len(rule_uids) - 3) // 2, rule_uids, self._config_builder
+ )
+ insert_rule_in_config(
+ self._normalized_config, rulebase.uid, (len(rule_uids) - 3) // 2, rule_uids, self._config_builder
+ )
+ insert_rule_in_config(
+ self._normalized_config, rulebase.uid, (len(rule_uids) - 3) // 2, rule_uids, self._config_builder
+ )
+
+ # Inserting three new rules at the end of the rulebase
+ insert_rule_in_config(self._normalized_config, rulebase.uid, len(rule_uids), rule_uids, self._config_builder)
+ insert_rule_in_config(self._normalized_config, rulebase.uid, len(rule_uids), rule_uids, self._config_builder)
+ insert_rule_in_config(self._normalized_config, rulebase.uid, len(rule_uids), rule_uids, self._config_builder)
+
+ # Act
+
+ self._fwconfig_import_rule.update_rulebase_diffs(self._previous_config)
+
+ # Assert
+
+ # The order of the entries in normalized_config
+ self.assertEqual(rule_uids, list(rulebase.rules.keys()))
+
+ sorted_rulebase_rules = sorted(rulebase.rules.values(), key=lambda r: r.rule_num_numeric)
+ sorted_rulebase_rules_uids = [r.rule_uid for r in sorted_rulebase_rules]
+
+ # The sequence of the rule_num_numeric values
+ self.assertEqual(rule_uids, sorted_rulebase_rules_uids)
+
+ # Insertions recognized in ImportDetails
+ self.assertEqual(self._import_state.state.stats.statistics.rule_add_count, 9)
+ self.assertEqual(self._import_state.state.stats.statistics.rule_delete_count, 0)
+ self.assertEqual(self._import_state.state.stats.statistics.rule_change_count, 0)
+ self.assertEqual(self._import_state.state.stats.statistics.rule_move_count, 0)
+
+ def test_update_rulebase_diffs_on_move_across_rulebases(self):
+ # Arrange
+
+ source_rulebase = self._fwconfig_import_rule.normalized_config.rulebases[0]
+ source_rulebase_uids = list(source_rulebase.rules.keys())
+ target_rulebase = self._fwconfig_import_rule.normalized_config.rulebases[1]
+ target_rulebase_uids = list(target_rulebase.rules.keys())
+
+ deleted_rule = remove_rule_from_rulebase(
+ self._normalized_config, source_rulebase.uid, source_rulebase_uids[0], source_rulebase_uids
+ )
+ insert_rule_in_config(
+ self._normalized_config, target_rulebase.uid, 0, target_rulebase_uids, self._config_builder, deleted_rule
+ )
+
+ # Act
+
+ self._fwconfig_import_rule.update_rulebase_diffs(self._previous_config)
+
+ # Assert
+
+ # The order of the entries in normalized_config
+ self.assertEqual(source_rulebase_uids, list(source_rulebase.rules.keys()))
+ self.assertEqual(target_rulebase_uids, list(target_rulebase.rules.keys()))
+
+ sorted_source_rulebase_rules = sorted(source_rulebase.rules.values(), key=lambda r: r.rule_num_numeric)
+ sorted_source_rulebase_rules_uids = [r.rule_uid for r in sorted_source_rulebase_rules]
+
+ sorted_target_rulebase_rules = sorted(target_rulebase.rules.values(), key=lambda r: r.rule_num_numeric)
+ sorted_target_rulebase_rules_uids = [r.rule_uid for r in sorted_target_rulebase_rules]
+
+ # The sequence of the rule_num_numeric values
+ self.assertEqual(source_rulebase_uids, sorted_source_rulebase_rules_uids)
+ self.assertEqual(target_rulebase_uids, sorted_target_rulebase_rules_uids)
+
+ # Move across rulebases recognized in ImportDetails
+ self.assertEqual(self._import_state.state.stats.statistics.rule_add_count, 0)
+ self.assertEqual(self._import_state.state.stats.statistics.rule_delete_count, 0)
+ self.assertEqual(self._import_state.state.stats.statistics.rule_change_count, 1)
+ self.assertEqual(self._import_state.state.stats.statistics.rule_move_count, 1)
+
+ def test_update_rulebase_diffs_on_moves_to_beginning_middle_and_end_of_rulebase(self):
+ # Arrange
+
+ rulebase = self._normalized_config.rulebases[0]
+ rule_uids = list(rulebase.rules.keys())
+
+ move_rule_in_config(
+ self._normalized_config, rulebase.uid, (len(rule_uids) - 1) // 2, 0, rule_uids
+ ) # Move to beginning
+ move_rule_in_config(
+ self._normalized_config, rulebase.uid, 1, (len(rule_uids) - 1) // 2, rule_uids
+ ) # Move to middle
+ move_rule_in_config(self._normalized_config, rulebase.uid, 2, len(rule_uids) - 1, rule_uids) # Move to end
+
+ # Act
+
+ self._fwconfig_import_rule.update_rulebase_diffs(self._previous_config)
+
+ # Assert
+
+ # The order of the entries in normalized_config
+ self.assertEqual(rule_uids, list(rulebase.rules.keys()))
+
+ sorted_rulebase_rules = sorted(rulebase.rules.values(), key=lambda r: r.rule_num_numeric)
+ sorted_rulebase_rules_uids = [r.rule_uid for r in sorted_rulebase_rules]
+
+ # The sequence of the rule_num_numeric values
+ self.assertEqual(rule_uids, sorted_rulebase_rules_uids)
+
+ # Move to beginning, middle and end recognized in ImportDetails
+ self.assertEqual(self._import_state.state.stats.statistics.rule_add_count, 0)
+ self.assertEqual(self._import_state.state.stats.statistics.rule_delete_count, 0)
+ self.assertEqual(self._import_state.state.stats.statistics.rule_change_count, 3)
+ self.assertEqual(self._import_state.state.stats.statistics.rule_move_count, 3)
+
+ def test_update_rulebase_diffs_on_delete_section_header(self):
+ # Arrange
+
+ # Move last five rules of last rulebase to new rulebase (previous config).
+
+ last_rulebase = self._previous_config.rulebases[-1]
+ last_five_rules_uids = list(last_rulebase.rules.keys())[-5:]
+
+ new_rulebase = self._config_builder.add_rulebase(self._previous_config, self._mgm_uid)
+
+ for rule_uid in last_five_rules_uids:
+ rule = last_rulebase.rules.pop(rule_uid)
+ self._config_builder.add_rule(self._previous_config, new_rulebase.uid, rule.model_dump())
+
+ # Create rulebase link for cp_section header (previous config)
+
+ last_rulebase_last_rule_uid = list(last_rulebase.rules.keys())[-1]
+ gateway = self._previous_config.gateways[0]
+ self._config_builder.add_cp_section_header(
+ gateway, last_rulebase.uid, new_rulebase.uid, last_rulebase_last_rule_uid
+ )
+
+ update_rule_map_and_rulebase_map(self._previous_config, self._import_state)
+ update_rule_num_numerics(self._previous_config)
+
+ rule_uids = [r for rb in self._normalized_config.rulebases for r in rb.rules]
+
+ # Act
+
+ self._fwconfig_import_rule.update_rulebase_diffs(self._previous_config)
+
+ # Assert
+
+ # The order of the entries in normalized_config (across rulebases)
+ self.assertEqual(rule_uids, [r for rb in self._normalized_config.rulebases for r in rb.rules])
+
+ sorted_rules = []
+ for rulebase in self._normalized_config.rulebases:
+ sorted_rules.extend(sorted(rulebase.rules.values(), key=lambda r: r.rule_num_numeric))
+ sorted_rules_uids = [r.rule_uid for r in sorted_rules]
+
+ # The sequence of the rule_num_numeric values
+ self.assertEqual(rule_uids, sorted_rules_uids)
+
+ # Move to beginning, middle and end recognized in ImportDetails
+ self.assertEqual(self._import_state.state.stats.statistics.rule_add_count, 0)
+ self.assertEqual(self._import_state.state.stats.statistics.rule_delete_count, 0)
+ self.assertEqual(self._import_state.state.stats.statistics.rule_change_count, 5)
+ self.assertEqual(self._import_state.state.stats.statistics.rule_move_count, 5)
\ No newline at end of file
diff --git a/roles/importer/files/importer/test/test_update_rulebase_link_diffs.py b/roles/importer/files/importer/test/test_update_rulebase_link_diffs.py
new file mode 100644
index 0000000000..fbb5745cf5
--- /dev/null
+++ b/roles/importer/files/importer/test/test_update_rulebase_link_diffs.py
@@ -0,0 +1,279 @@
+import copy
+import unittest
+
+from fwo_base import init_service_provider, register_global_state
+from fwo_log import FWOLogger
+from models.fwconfig_normalized import FwConfigNormalized
+from services.service_provider import ServiceProvider
+from test.mocking.mock_config import MockFwConfigNormalizedBuilder
+from test.mocking.mock_fwconfig_import_gateway import MockFwConfigImportGateway
+from test.mocking.mock_import_state import MockImportStateController
+from test.tools.set_up_test import (
+ lookup_ids_for_rulebase_link,
+ update_rb_links,
+ update_rule_map_and_rulebase_map,
+ update_rule_num_numerics,
+)
+
+
+class TestUpdateRulebaseLinkDiffs(unittest.TestCase):
+ _config_builder: MockFwConfigNormalizedBuilder
+ _fwconfig_import_gateway: MockFwConfigImportGateway
+ _service_provider: ServiceProvider
+ _mgm_uid: str
+ _previous_config: FwConfigNormalized
+ _normalized_config: FwConfigNormalized
+ _import_state: MockImportStateController
+
+ @classmethod
+ def setUpClass(cls):
+ """
+ Gets invoked once before running any test of this class.
+ """
+ FWOLogger(2)
+ cls._service_provider = init_service_provider()
+ register_global_state(MockImportStateController(import_id=1, stub_setCoreData=True))
+ cls._config_builder = MockFwConfigNormalizedBuilder()
+
+ def setUp(self):
+ """
+ Gets invoked one time per test method before running it.
+ """
+ self._config_builder.set_up()
+ self._fwconfig_import_gateway = MockFwConfigImportGateway()
+
+ self._previous_config, self._mgm_uid = self._config_builder.build_config(
+ {"rule_config": [10, 10, 10], "network_object_config": 10, "service_config": 10, "user_config": 10}
+ )
+
+ self._normalized_config = copy.deepcopy(self._previous_config)
+
+ self._fwconfig_import_gateway._global_state.normalized_config = self._normalized_config
+ self._fwconfig_import_gateway._global_state.previous_config = self._previous_config
+ self._import_state = self._fwconfig_import_gateway._global_state.import_state
+ self._import_state.state.gateway_map[3] = {self._normalized_config.gateways[0].Uid or "": 1}
+
+ def test_add_cp_section_header_at_the_bottom(self):
+ # Arrange
+
+ last_rulebase = self._normalized_config.rulebases[-1]
+ last_rulebase_last_rule_uid = list(last_rulebase.rules.keys())[-1]
+ new_rulebase = self._config_builder.add_rulebase(self._normalized_config, self._mgm_uid)
+ gateway = self._normalized_config.gateways[0]
+ self._config_builder.add_cp_section_header(
+ gateway, last_rulebase.uid, new_rulebase.uid, last_rulebase_last_rule_uid
+ )
+
+ update_rule_map_and_rulebase_map(self._normalized_config, self._import_state)
+ to_rulebase_id = self._import_state.state.lookup_rulebase_id(new_rulebase.uid)
+ from_rulebase_id = self._import_state.state.lookup_rulebase_id(last_rulebase.uid)
+ update_rb_links(gateway.RulebaseLinks, 1, self._fwconfig_import_gateway)
+
+ # Act
+
+ new_links, _ = self._fwconfig_import_gateway.update_rulebase_link_diffs()
+
+ # Assert
+
+ self.assertTrue(len(new_links) == 1, f"expected {1} new rulebase link, got {len(new_links)}")
+ self.assertTrue(
+ new_links[0]["from_rulebase_id"] == from_rulebase_id,
+ f"expected last rulebase link to have from_rulebase_id {from_rulebase_id}, got {new_links[0]['from_rulebase_id']}",
+ )
+ self.assertTrue(
+ new_links[0]["to_rulebase_id"] == to_rulebase_id,
+ f"expected last rulebase link to point to new rulebase id {to_rulebase_id}, got {new_links[0]['to_rulebase_id']}",
+ )
+ self.assertTrue(new_links[0]["is_section"], "expected last rulebase link to have is_section true, got false")
+
+ def test_add_cp_section_header_in_existing_rulebase(self):
+ # Arrange
+
+ last_rulebase = self._normalized_config.rulebases[-1]
+ last_rulebase_last_rule_uid = list(last_rulebase.rules.keys())[-1]
+ last_rulebase_last_rule = last_rulebase.rules.pop(last_rulebase_last_rule_uid)
+
+ new_rulebase = self._config_builder.add_rulebase(self._normalized_config, self._mgm_uid)
+ self._config_builder.add_rule(self._normalized_config, new_rulebase.uid, last_rulebase_last_rule.model_dump())
+ gateway = self._normalized_config.gateways[0]
+ self._config_builder.add_cp_section_header(
+ gateway, last_rulebase.uid, new_rulebase.uid, last_rulebase_last_rule_uid
+ )
+
+ update_rule_map_and_rulebase_map(self._normalized_config, self._import_state)
+ to_rulebase_id = self._import_state.state.lookup_rulebase_id(new_rulebase.uid)
+ from_rulebase_id = self._import_state.state.lookup_rulebase_id(last_rulebase.uid)
+ update_rb_links(gateway.RulebaseLinks, 1, self._fwconfig_import_gateway)
+
+ # Act
+
+ new_links, _ = self._fwconfig_import_gateway.update_rulebase_link_diffs()
+
+ # Assert
+
+ self.assertTrue(len(new_links) == 1, f"expected {1} new rulebase link, got {len(new_links)}")
+ self.assertTrue(
+ new_links[0]["from_rulebase_id"] == from_rulebase_id,
+ f"expected last rulebase link to have from_rulebase_id {from_rulebase_id}, got {new_links[0]['from_rulebase_id']}",
+ )
+ self.assertTrue(
+ new_links[0]["to_rulebase_id"] == to_rulebase_id,
+ f"expected last rulebase link to point to new rulebase id {to_rulebase_id}, got {new_links[0]['to_rulebase_id']}",
+ )
+ self.assertTrue(new_links[0]["is_section"], "expected last rulebase link to have is_section true, got false")
+
+ def test_delete_cp_section_header(self):
+ # Arrange
+
+ # Move last five rules of last rulebase to new rulebase (previous config).
+
+ last_rulebase = self._previous_config.rulebases[-1]
+ last_five_rules_uids = list(last_rulebase.rules.keys())[-5:]
+
+ new_rulebase = self._config_builder.add_rulebase(self._previous_config, self._mgm_uid)
+
+ for rule_uid in last_five_rules_uids:
+ rule = last_rulebase.rules.pop(rule_uid)
+ self._config_builder.add_rule(self._previous_config, new_rulebase.uid, rule.model_dump())
+
+ # Create rulebase link for cp_section header (previous config)
+
+ last_rulebase_last_rule_uid = list(last_rulebase.rules.keys())[-1]
+ gateway = self._previous_config.gateways[0]
+ self._config_builder.add_cp_section_header(
+ gateway, last_rulebase.uid, new_rulebase.uid, last_rulebase_last_rule_uid
+ )
+
+ update_rule_map_and_rulebase_map(self._previous_config, self._import_state)
+ update_rule_num_numerics(self._previous_config)
+ update_rb_links(gateway.RulebaseLinks, 1, self._fwconfig_import_gateway)
+
+ # Act
+
+ _, deleted_links_ids = self._fwconfig_import_gateway.update_rulebase_link_diffs()
+
+ # Assert
+
+ self.assertTrue(deleted_links_ids[0] == self._fwconfig_import_gateway._rb_link_controller.rb_links[-1].id)
+
+ def test_add_inline_layer(self):
+ # Arrange
+
+ from_rulebase = self._normalized_config.rulebases[-1]
+ from_rule = next(iter(from_rulebase.rules.values()))
+
+ added_rulebase = self._config_builder.add_rulebase(self._normalized_config, self._mgm_uid)
+ self._config_builder.add_rule(self._normalized_config, added_rulebase.uid)
+
+ gateway = self._normalized_config.gateways[0]
+ self._config_builder.add_inline_layer(gateway, from_rulebase.uid, from_rule.rule_uid, added_rulebase.uid)
+
+ update_rule_map_and_rulebase_map(self._normalized_config, self._import_state)
+ from_rule_id, from_rulebase_id, to_rulebase_id = lookup_ids_for_rulebase_link(
+ self._import_state, from_rule.rule_uid, from_rulebase.uid, added_rulebase.uid
+ )
+ update_rb_links(gateway.RulebaseLinks, 1, self._fwconfig_import_gateway)
+
+ # Act
+
+ new_links, _ = self._fwconfig_import_gateway.update_rulebase_link_diffs()
+
+ # Assert
+
+ self.assertTrue(len(new_links) == 1, f"expected {1} new rulebase link, got {len(new_links)}")
+ self.assertTrue(
+ new_links[0]["from_rule_id"] == from_rule_id,
+ f"expected last rulebase link to have from_rule_id {from_rule_id}, got {new_links[0]['from_rule_id']}",
+ )
+ self.assertTrue(
+ new_links[0]["from_rulebase_id"] == from_rulebase_id,
+ f"expected last rulebase link to have from_rulebase_id {from_rulebase_id}, got {new_links[0]['from_rulebase_id']}",
+ )
+ self.assertTrue(
+ new_links[0]["to_rulebase_id"] == to_rulebase_id,
+ f"expected last rulebase link to point to new rulebase id {to_rulebase_id}, got {new_links[0]['to_rulebase_id']}",
+ )
+ self.assertTrue(
+ not new_links[0]["is_section"], "expected last rulebase link to have is_section false, got true"
+ )
+
+ def test_delete_inline_layer(self):
+ # Arrange
+
+ from_rulebase = self._previous_config.rulebases[-1]
+ from_rule = next(iter(from_rulebase.rules.values()))
+
+ added_rulebase = self._config_builder.add_rulebase(self._previous_config, self._mgm_uid)
+ self._config_builder.add_rule(self._previous_config, added_rulebase.uid)
+
+ gateway = self._previous_config.gateways[0]
+ self._config_builder.add_inline_layer(gateway, from_rulebase.uid, from_rule.rule_uid, added_rulebase.uid)
+
+ update_rule_map_and_rulebase_map(self._previous_config, self._import_state)
+ _from_rule_id, _from_rulebase_id, _to_rulebase_id = lookup_ids_for_rulebase_link(
+ self._import_state, from_rule.rule_uid, from_rulebase.uid, added_rulebase.uid
+ )
+ update_rb_links(gateway.RulebaseLinks, 1, self._fwconfig_import_gateway)
+
+ # Act
+
+ _, deleted_links_ids = self._fwconfig_import_gateway.update_rulebase_link_diffs()
+
+ # Assert
+
+ self.assertTrue(len(deleted_links_ids) == 1, f"expected {1} new rulebase link, got {len(deleted_links_ids)}")
+ self.assertTrue(deleted_links_ids[0] == self._fwconfig_import_gateway._rb_link_controller.rb_links[-1].id)
+
+ def test_move_inline_layer(self):
+ # Arrange
+
+ from_rulebase_previous = self._previous_config.rulebases[-1]
+ from_rule_previous = next(iter(from_rulebase_previous.rules.values()))
+
+ from_rulebase_normalized = self._normalized_config.rulebases[0]
+ from_rule_normalized = next(iter(from_rulebase_normalized.rules.values()))
+
+ added_rulebase = self._config_builder.add_rulebase(self._previous_config, self._mgm_uid)
+ self._config_builder.add_rule(self._previous_config, added_rulebase.uid)
+ added_rulebase_copy = copy.deepcopy(added_rulebase)
+ self._config_builder.add_rulebase(self._normalized_config, self._mgm_uid, added_rulebase_copy)
+
+ gateway_previous = self._previous_config.gateways[0]
+ self._config_builder.add_inline_layer(
+ gateway_previous, from_rulebase_previous.uid, from_rule_previous.rule_uid, added_rulebase.uid
+ )
+ gateway_normalized = self._normalized_config.gateways[0]
+ self._config_builder.add_inline_layer(
+ gateway_normalized, from_rulebase_normalized.uid, from_rule_normalized.rule_uid, added_rulebase_copy.uid
+ )
+
+ update_rule_map_and_rulebase_map(self._previous_config, self._import_state)
+ from_rule_id, from_rulebase_id, to_rulebase_id = lookup_ids_for_rulebase_link(
+ self._import_state, from_rule_normalized.rule_uid, from_rulebase_normalized.uid, added_rulebase_copy.uid
+ )
+ update_rb_links(gateway_previous.RulebaseLinks, 1, self._fwconfig_import_gateway)
+
+ # Act
+
+ new_links, deleted_links_ids = self._fwconfig_import_gateway.update_rulebase_link_diffs()
+
+ # Assert
+
+ self.assertTrue(len(new_links) == 1, f"expected {1} new rulebase link, got {len(new_links)}")
+ self.assertTrue(
+ new_links[0]["from_rule_id"] == from_rule_id,
+ f"expected last rulebase link to have from_rule_id {from_rule_id}, got {new_links[0]['from_rule_id']}",
+ )
+ self.assertTrue(
+ new_links[0]["from_rulebase_id"] == from_rulebase_id,
+ f"expected last rulebase link to have from_rulebase_id {from_rulebase_id}, got {new_links[0]['from_rulebase_id']}",
+ )
+ self.assertTrue(
+ new_links[0]["to_rulebase_id"] == to_rulebase_id,
+ f"expected last rulebase link to point to new rulebase id {to_rulebase_id}, got {new_links[0]['to_rulebase_id']}",
+ )
+ self.assertTrue(
+ not new_links[0]["is_section"], "expected last rulebase link to have is_section false, got true"
+ )
+ self.assertTrue(len(deleted_links_ids) == 1, f"expected {1} new rulebase link, got {len(deleted_links_ids)}")
+ self.assertTrue(deleted_links_ids[0] == self._fwconfig_import_gateway._rb_link_controller.rb_links[-1].id)
diff --git a/roles/importer/files/importer/test/tools/__init__.py b/roles/importer/files/importer/test/tools/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/roles/importer/files/importer/test/tools/create_mock_config_file.py b/roles/importer/files/importer/test/tools/create_mock_config_file.py
new file mode 100644
index 0000000000..925ef92daa
--- /dev/null
+++ b/roles/importer/files/importer/test/tools/create_mock_config_file.py
@@ -0,0 +1,45 @@
+from model_controllers.fwconfigmanagerlist_controller import FwConfigManagerListController
+from models.fwconfigmanager import FwConfigManager
+from test.mocking.mock_config import MockFwConfigNormalizedBuilder
+from test.mocking.mock_import_state import MockImportStateController
+
+
+def main(
+ rule_config=(10, 10, 10),
+ network_object_config=10,
+ service_config=10,
+ user_config=10,
+ gateway_uid="cbdd1e35-b6e9-4ead-b13f-fd6389e34987",
+ gateway_name="sting-gw",
+ manager_uid="6ae3760206b9bfbd2282b5964f6ea07869374f427533c72faa7418c28f7a77f2",
+ manager_name="sting-mgmt",
+ domain_uid="domain uid",
+ domain_name="domain name",
+):
+ mock_config_builder = MockFwConfigNormalizedBuilder()
+ mock_config, _ = mock_config_builder.build_config(
+ {
+ "rule_config": list(rule_config),
+ "network_object_config": network_object_config,
+ "service_config": service_config,
+ "user_config": user_config,
+ "gateway_uid": gateway_uid,
+ "gateway_name": gateway_name,
+ }
+ )
+
+ fw_mock_import_state = MockImportStateController(stub_setCoreData=True)
+
+ fw_config_manager_list_controller = FwConfigManagerListController()
+ fw_config_manager = FwConfigManager(
+ manager_uid=manager_uid, manager_name=manager_name, domain_uid=domain_uid, domain_name=domain_name
+ )
+ fw_config_manager.configs.append(mock_config)
+ fw_config_manager_list_controller.ManagerSet.append(fw_config_manager)
+ file_path = fw_config_manager_list_controller.store_full_normalized_config_to_file(fw_mock_import_state)
+
+ print(f"MockConfig: File saved to '{file_path}'")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/roles/importer/files/importer/test/tools/set_up_test.py b/roles/importer/files/importer/test/tools/set_up_test.py
new file mode 100644
index 0000000000..1b800a43b4
--- /dev/null
+++ b/roles/importer/files/importer/test/tools/set_up_test.py
@@ -0,0 +1,196 @@
+import copy
+
+from fwo_const import RULE_NUM_NUMERIC_STEPS
+from models.fwconfig_normalized import FwConfigNormalized
+from models.rulebase_link import RulebaseLink, RulebaseLinkUidBased
+from test.mocking.mock_config import MockFwConfigNormalizedBuilder
+from test.mocking.mock_fwconfig_import_rule import MockFwConfigImportRule
+from test.mocking.mock_import_state import MockImportStateController
+
+
+def set_up_config_for_import_consistency_test():
+ config_builder = MockFwConfigNormalizedBuilder()
+ config, _ = config_builder.build_config(
+ {"rule_config": [10, 10, 10], "network_object_config": 10, "service_config": 10, "user_config": 10}
+ )
+ config_builder.add_rule_with_nested_groups(config)
+
+ return config
+
+
+def set_up_test_for_ruleorder_test_with_defaults():
+ config_builder = MockFwConfigNormalizedBuilder()
+ previous_config, mgm_uid = config_builder.build_config(
+ {"rule_config": [10, 10, 10], "network_object_config": 10, "service_config": 10, "user_config": 10}
+ )
+
+ fwconfig_import_rule = MockFwConfigImportRule()
+ fwconfig_import_rule.normalized_config = copy.deepcopy(previous_config)
+
+ update_rule_num_numerics(previous_config)
+ update_rule_map_and_rulebase_map(previous_config, fwconfig_import_rule.import_details)
+
+ return previous_config, fwconfig_import_rule, config_builder, mgm_uid
+
+
+def reorder_rulebase_rules_dict(config: FwConfigNormalized, rulebase_uid, rule_uids):
+ """
+ Imitates the changes in order in the config dict.
+ """
+ rulebase = next((rb for rb in config.rulebases if rb.uid == rulebase_uid), None)
+
+ if rulebase:
+ rules = copy.deepcopy(rulebase.rules)
+ rulebase.rules = {}
+ for rule_uid in rule_uids:
+ rulebase.rules[rule_uid] = rules[rule_uid]
+
+
+def remove_rule_from_rulebase(
+ config: FwConfigNormalized, rulebase_uid: str, rule_uid: str, uid_sequence: list[str] | None = None
+):
+ """
+ Imitates the deletion of a rule in the config dict.
+ """
+ rulebase = next((rb for rb in config.rulebases if rb.uid == rulebase_uid), None)
+
+ if rulebase:
+ rule = rulebase.rules.pop(rule_uid)
+
+ if uid_sequence:
+ uid_sequence[:] = [uid for uid in uid_sequence if uid != rule_uid]
+
+ return rule
+
+
+def insert_rule_in_config(
+ config: FwConfigNormalized, rulebase_uid, rule_position, rule_uids, config_builder, rule=None
+):
+ """
+ Imitates the insertion of a rule in the config dict.
+ """
+ rulebase = next((rb for rb in config.rulebases if rb.uid == rulebase_uid), None)
+ inserted_rule_uid = ""
+
+ if rulebase:
+ if rule is None:
+ inserted_rule = config_builder.add_rule(config, rulebase_uid)
+ else:
+ inserted_rule = rule
+ rulebase.rules[inserted_rule.rule_uid] = inserted_rule
+
+ rule_uids.insert(rule_position, inserted_rule.rule_uid)
+
+ reorder_rulebase_rules_dict(config, rulebase_uid, rule_uids)
+
+ inserted_rule_uid = inserted_rule.rule_uid
+
+ return inserted_rule_uid
+
+
+def move_rule_in_config(config: FwConfigNormalized, rulebase_uid, source_position, target_position, rule_uids):
+ """
+ Imitates the moving of a rule in the config dict.
+ """
+ rulebase = next((rb for rb in config.rulebases if rb.uid == rulebase_uid), None)
+ moved_rule_uid = ""
+
+ if rulebase:
+ rule_uid = list(rulebase.rules.keys())[source_position]
+ rule = rulebase.rules.pop(rule_uid)
+ rulebase.rules[rule_uid] = rule
+ rule_uids.pop(source_position)
+ rule_uids.insert(target_position, rule_uid)
+
+ reorder_rulebase_rules_dict(config, rulebase.uid, rule_uids)
+
+ moved_rule_uid = rule_uid
+
+ return moved_rule_uid
+
+
+def update_rule_map_and_rulebase_map(config, import_state: MockImportStateController):
+ import_state.state.rulebase_map = {}
+ import_state.state.rule_map = {}
+
+ rulebase_id = 1
+ rule_id = 1
+
+ for rulebase in config.rulebases:
+ import_state.state.rulebase_map[rulebase.uid] = rulebase_id
+ rulebase_id += 1
+ for rule in rulebase.rules.values():
+ import_state.state.rule_map[rule.rule_uid] = rule_id
+ rule_id += 1
+
+
+def update_rule_num_numerics(config):
+ for rulebase in config.rulebases:
+ new_num_numeric = 0
+ for rule in rulebase.rules.values():
+ new_num_numeric += RULE_NUM_NUMERIC_STEPS
+ rule.rule_num_numeric = new_num_numeric
+
+
+def update_rb_links(rulebase_links: list[RulebaseLinkUidBased], gateway_id, fwconfig_import_gateway):
+ new_rb_links: list[RulebaseLink] = []
+ link_id = 0
+
+ for link in rulebase_links:
+ link_id += 1
+
+ link_type = 0
+ match link.link_type:
+ case "ordered":
+ link_type = 2
+ case "inline":
+ link_type = 3
+ case "concatenated":
+ link_type = 4
+ case "domain":
+ link_type = 5
+ case _:
+ link_type = 0
+
+ new_rb_links.append(
+ RulebaseLink(
+ id=link_id,
+ gw_id=gateway_id,
+ from_rule_id=fwconfig_import_gateway._global_state.import_state.state.lookup_rule(link.from_rule_uid),
+ from_rulebase_id=fwconfig_import_gateway._global_state.import_state.state.lookup_rulebase_id(
+ link.from_rulebase_uid
+ )
+ if link.from_rulebase_uid
+ else None,
+ to_rulebase_id=fwconfig_import_gateway._global_state.import_state.state.lookup_rulebase_id(
+ link.to_rulebase_uid
+ ),
+ link_type=link_type,
+ is_initial=link.is_initial,
+ is_global=link.is_global,
+ is_section=link.is_section,
+ created=0,
+ )
+ )
+
+ fwconfig_import_gateway._rb_link_controller.rb_links = new_rb_links
+
+
+def lookup_ids_for_rulebase_link(
+ import_state: MockImportStateController,
+ from_rule_uid: str = "",
+ from_rulebase_uid: str = "",
+ to_rulebase_uid: str = "",
+):
+ from_rule_id = None
+ from_rulebase_id = None
+ to_rulebase_id = None
+
+ if from_rule_uid != "":
+ from_rule_id = import_state.state.lookup_rule(from_rule_uid)
+ if from_rulebase_uid != "":
+ from_rulebase_id = import_state.state.lookup_rulebase_id(from_rulebase_uid)
+ if to_rulebase_uid != "":
+ to_rulebase_id = import_state.state.lookup_rulebase_id(to_rulebase_uid)
+
+ return from_rule_id, from_rulebase_id, to_rulebase_id
diff --git a/roles/importer/files/importer/test/tools/stopwatch.py b/roles/importer/files/importer/test/tools/stopwatch.py
new file mode 100644
index 0000000000..46d3b78a89
--- /dev/null
+++ b/roles/importer/files/importer/test/tools/stopwatch.py
@@ -0,0 +1,22 @@
+class Stopwatch:
+ def __init__(self):
+ self.start_time = None
+ self.end_time = None
+ self.elapsed_time = 0
+
+ def start(self):
+ """Startet die Stoppuhr."""
+ self.start_time = time.time()
+
+ def stop(self):
+ """Stoppt die Stoppuhr und berechnet die verstrichene Zeit."""
+ if self.start_time is None:
+ raise ValueError("Die Stoppuhr wurde nicht gestartet.")
+ self.end_time = time.time()
+ self.elapsed_time = self.end_time - self.start_time
+
+ def get_elapsed_time(self):
+ """Gibt die verstrichene Zeit in Sekunden zurück."""
+ if self.elapsed_time == 0:
+ raise ValueError("Die Stoppuhr wurde noch nicht gestoppt.")
+ return self.elapsed_time
diff --git a/roles/importer/files/importer/test/tools/testhelper.py b/roles/importer/files/importer/test/tools/testhelper.py
new file mode 100644
index 0000000000..4cb24e00ae
--- /dev/null
+++ b/roles/importer/files/importer/test/tools/testhelper.py
@@ -0,0 +1,11 @@
+import importlib.util
+import os
+
+
+def load_module(module_name, file_name):
+ """Loads a module dynamically from the importer directory. Only supported from direct child directories."""
+ module_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "importer", file_name))
+ spec = importlib.util.spec_from_file_location(module_name, module_path)
+ module = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(module)
+ return module
diff --git a/roles/importer/files/importer/utils/__init__.py b/roles/importer/files/importer/utils/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/roles/importer/files/importer/utils/conversion_utils.py b/roles/importer/files/importer/utils/conversion_utils.py
new file mode 100644
index 0000000000..e6a15d1641
--- /dev/null
+++ b/roles/importer/files/importer/utils/conversion_utils.py
@@ -0,0 +1,14 @@
+from typing import Any
+
+from fwo_log import FWOLogger
+
+
+def convert_list_to_dict(list_in: list[Any], id_field: str) -> dict[Any, Any]:
+ result: dict[Any, Any] = {}
+ for item in list_in:
+ if id_field in item:
+ key = item[id_field]
+ result[key] = item
+ else:
+ FWOLogger.error(f"dict {item!s} does not contain id field {id_field}")
+ return result # { listIn[idField]: listIn for listIn in listIn }
diff --git a/roles/importer/files/importer/utils/fwconfig_json_encoder.py b/roles/importer/files/importer/utils/fwconfig_json_encoder.py
new file mode 100644
index 0000000000..919350adcb
--- /dev/null
+++ b/roles/importer/files/importer/utils/fwconfig_json_encoder.py
@@ -0,0 +1,26 @@
+import json
+
+from fwo_base import ConfFormat, ConfigAction
+from models.rulebase import Rulebase
+from netaddr import IPNetwork
+
+
+class FwConfigJsonEncoder(json.JSONEncoder):
+ def default(self, o: object) -> object:
+ if isinstance(o, (ConfigAction, ConfFormat)):
+ return o.name
+
+ if isinstance(o, Rulebase):
+ return o.to_json()
+
+ if isinstance(o, IPNetwork):
+ return str(o)
+
+ return json.JSONEncoder.default(self, o)
+
+
+"""
+ the configuraton of a firewall management to import
+ could be normalized or native config
+ management could be standard of super manager (MDS, fortimanager)
+"""
diff --git a/roles/importer/tasks/main.yml b/roles/importer/tasks/main.yml
index 56577772ba..7a0fd2fc3c 100644
--- a/roles/importer/tasks/main.yml
+++ b/roles/importer/tasks/main.yml
@@ -19,12 +19,21 @@
delegate_to: "{{ inventory_hostname }}"
when: importer_dir_check.stat.exists and installation_mode == "upgrade"
- - name: remove importer dir
+ - name: remove importer dir for cleaning up all old code
file:
state: absent
path: "{{ fworch_home }}/importer"
when: installation_mode == "upgrade"
+ - name: copy importer files
+ synchronize:
+ src: "importer"
+ dest: "{{ fworch_home }}"
+ rsync_opts:
+ - "--chmod=0755"
+ - "--chown={{ fworch_user }}:{{ fworch_group }}"
+ tags: [ 'test' ]
+
- name: Install importer perl modules
package: name={{ item }} state=present
loop:
@@ -36,22 +45,22 @@
- libsys-syslog-perl
- libexpect-perl
- libcgi-pm-perl
- - python3-jsonpickle
- - python3-gnupg
- - name: Install importer python modules
- package: name={{ item }} state=present
- loop:
- - python3-netaddr
+ - name: Install importer python3-venv package (for Ubuntu/Debian)
+ package:
+ name: python3-venv
+ state: present
- - name: copy importer files
- synchronize:
- src: "importer"
- dest: "{{ fworch_home }}"
- rsync_opts:
- - "--chmod=0755"
- - "--chown={{ fworch_user }}:{{ fworch_group }}"
- tags: [ 'test' ]
+ - name: Create Python virtual environment
+ ansible.builtin.command:
+ cmd: python3 -m venv {{ importer_venv_dir }}
+ creates: "{{ importer_venv_dir }}"
+
+ - name: Install Python packages from requirements.txt
+ ansible.builtin.pip:
+ requirements: "{{ fworch_home }}/importer/requirements.txt"
+ virtualenv: "{{ importer_venv_dir }}"
+ environment: "{{ proxy_env }}"
- name: set x-flag for importer executables (top level only)
file:
diff --git a/roles/importer/tasks/upgrade/9.0.0.yml b/roles/importer/tasks/upgrade/9.0.0.yml
new file mode 100644
index 0000000000..80c6aa5aeb
--- /dev/null
+++ b/roles/importer/tasks/upgrade/9.0.0.yml
@@ -0,0 +1,5 @@
+- name: Install importer python modules
+ package: name={{ item }} state=present
+ loop:
+ - python3-pydantic
+ become: true
diff --git a/roles/importer/templates/fworch-importer-api.service.j2 b/roles/importer/templates/fworch-importer-api.service.j2
index 84af817f54..7c5f60eed2 100644
--- a/roles/importer/templates/fworch-importer-api.service.j2
+++ b/roles/importer/templates/fworch-importer-api.service.j2
@@ -12,15 +12,20 @@ After=network.target remote-fs.target nss-lookup.target
{%- endif %}
[Service]
+Type=simple
WorkingDirectory={{ importer_home }}
ExecStartPre=/bin/sleep 10
-ExecStart={{ importer_home }}/import-main-loop.py
+ExecStart={{ importer_venv_dir }}/bin/python {{ importer_home}}/import-main-loop.py
+Environment="PATH={{ importer_venv_dir }}/bin:/usr/bin:/bin"
+Environment="VIRTUAL_ENV={{ importer_venv_dir }}"
TimeoutStopSec=300min
StandardOutput=journal
StandardError=journal
SyslogIdentifier={{ product_name }}-importer-api
User={{ fworch_user }}
KillSignal=SIGINT
+Group={{ fworch_group }}
+Restart=always
[Install]
WantedBy=multi-user.target
diff --git a/roles/lib/files/FWO.Api.Client/APIConnection.cs b/roles/lib/files/FWO.Api.Client/APIConnection.cs
index 5c63227069..2476e811bc 100644
--- a/roles/lib/files/FWO.Api.Client/APIConnection.cs
+++ b/roles/lib/files/FWO.Api.Client/APIConnection.cs
@@ -1,4 +1,6 @@
-namespace FWO.Api.Client
+using FWO.Logging;
+
+namespace FWO.Api.Client
{
public abstract class ApiConnection : IDisposable
{
@@ -6,6 +8,8 @@ public abstract class ApiConnection : IDisposable
public event EventHandler? OnAuthHeaderChanged;
+ public Basics.Interfaces.ILogger Logger = new Logger();
+
protected List subscriptions = [];
protected void InvokeOnAuthHeaderChanged(object? sender, string newAuthHeader)
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/compliance/addNetworkZone.graphql b/roles/lib/files/FWO.Api.Client/APIcalls/compliance/addNetworkZone.graphql
deleted file mode 100644
index 8b81935486..0000000000
--- a/roles/lib/files/FWO.Api.Client/APIcalls/compliance/addNetworkZone.graphql
+++ /dev/null
@@ -1,25 +0,0 @@
-mutation insert_compliance_network_zone ($name: String!, $description: String!, $ip_ranges: [compliance_ip_range_insert_input!]!, $super_network_zone_id: bigint,
-$communication_sources: [compliance_network_zone_communication_insert_input!]!, $communication_destinations: [compliance_network_zone_communication_insert_input!]!,
-$sub_network_zones: [compliance_network_zone_insert_input!]!) {
- insert_compliance_network_zone_one (
- object: {
- super_network_zone_id: $super_network_zone_id,
- name: $name,
- description: $description,
- ip_ranges: {
- data: $ip_ranges
- },
- network_zone_communication_destinations: {
- data: $communication_destinations
- },
- network_zone_communication_sources: {
- data: $communication_sources
- },
- sub_network_zones: {
- data: $sub_network_zones
- }
- }
- ) {
- id
- }
-}
\ No newline at end of file
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/compliance/deleteNetworkZone.graphql b/roles/lib/files/FWO.Api.Client/APIcalls/compliance/deleteNetworkZone.graphql
deleted file mode 100644
index 7800da5be1..0000000000
--- a/roles/lib/files/FWO.Api.Client/APIcalls/compliance/deleteNetworkZone.graphql
+++ /dev/null
@@ -1,7 +0,0 @@
-mutation delete_compliance_network_zone ($id: bigint!) {
- delete_compliance_network_zone_by_pk (
- id: $id
- ) {
- id
- }
-}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/compliance/getNetworkZones.graphql b/roles/lib/files/FWO.Api.Client/APIcalls/compliance/getNetworkZones.graphql
deleted file mode 100644
index cca37df145..0000000000
--- a/roles/lib/files/FWO.Api.Client/APIcalls/compliance/getNetworkZones.graphql
+++ /dev/null
@@ -1,31 +0,0 @@
-query get_compliance_network_zones {
- compliance_network_zone (order_by: {name: asc}) {
- id
- name
- description
- ip_ranges {
- ip_range_start
- ip_range_end
- }
- super_network_zone {
- id
- name
- }
- sub_network_zones {
- id
- name
- }
- network_zone_communication_destinations {
- to_network_zone {
- id
- name
- }
- }
- network_zone_communication_sources {
- from_network_zone {
- id
- name
- }
- }
- }
-}
\ No newline at end of file
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/compliance/updateNetworkZone.graphql b/roles/lib/files/FWO.Api.Client/APIcalls/compliance/updateNetworkZone.graphql
deleted file mode 100644
index 3b25ce7fb4..0000000000
--- a/roles/lib/files/FWO.Api.Client/APIcalls/compliance/updateNetworkZone.graphql
+++ /dev/null
@@ -1,68 +0,0 @@
-mutation update_compliance_network_zone ($network_zone_id: bigint!, $name: String!, $description: String!, $super_network_zone_id: bigint,
- $add_ip_ranges: [compliance_ip_range_insert_input!]!, $delete_ip_ranges_exp: [compliance_ip_range_bool_exp!]!,
- $add_zone_communication: [compliance_network_zone_communication_insert_input!]!, $delete_zone_communication_exp: [compliance_network_zone_communication_bool_exp!]!,
- $add_sub_zones_exp: [compliance_network_zone_bool_exp!]!, $delete_sub_zones_exp: [compliance_network_zone_bool_exp!]!)
-{
- update_compliance_network_zone (
- where: {id: {_eq: $network_zone_id}}
- _set: {
- name: $name,
- description: $description,
- super_network_zone_id: $super_network_zone_id
- }
- ) {
- affected_rows
- }
-
- delete_compliance_ip_range (
- where: {
- network_zone_id: {_eq: $network_zone_id},
- _or: $delete_ip_ranges_exp
- }
- ) {
- affected_rows
- }
-
- insert_compliance_ip_range (
- objects: $add_ip_ranges
- ) {
- affected_rows
- }
-
- delete_compliance_network_zone_communication (
- where: {
- _or: $delete_zone_communication_exp
- }
- ) {
- affected_rows
- }
-
- insert_compliance_network_zone_communication (
- objects: $add_zone_communication
- ) {
- affected_rows
- }
-
- update_compliance_network_zone_many (
- updates: [
- {
- where: {
- _or: $delete_sub_zones_exp
- }
- _set: {
- super_network_zone_id: null
- }
- },
- {
- where: {
- _or: $add_sub_zones_exp
- }
- _set: {
- super_network_zone_id: $network_zone_id
- }
- }
- ]
- ) {
- affected_rows
- }
-}
\ No newline at end of file
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/compliance/updateNetworkZoneCommunication.graphql b/roles/lib/files/FWO.Api.Client/APIcalls/compliance/updateNetworkZoneCommunication.graphql
deleted file mode 100644
index 54aed3e5f2..0000000000
--- a/roles/lib/files/FWO.Api.Client/APIcalls/compliance/updateNetworkZoneCommunication.graphql
+++ /dev/null
@@ -1,18 +0,0 @@
-mutation update_compliance_network_zone_communication(
- $delete_zone_communication_exp: [compliance_network_zone_communication_bool_exp!]!,
- $add_zone_communication: [compliance_network_zone_communication_insert_input!]!,)
-{
- delete_compliance_network_zone_communication (
- where: {
- _or: $delete_zone_communication_exp
- }
- ) {
- affected_rows
- }
-
- insert_compliance_network_zone_communication (
- objects: $add_zone_communication
- ) {
- affected_rows
- }
-}
\ No newline at end of file
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getAppServerForAppRole.graphql b/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getAppServerForAppRole.graphql
deleted file mode 100644
index c1cdb3c683..0000000000
--- a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/getAppServerForAppRole.graphql
+++ /dev/null
@@ -1,7 +0,0 @@
-query getAppServerForAppRole ($nwGroupId: bigint!){
- modelling_nwobject_nwgroup (where: { nwgroup_id: { _eq: $nwGroupId } } order_by: { name: asc }){
- owner_network{
- ...appServerDetails
- }
- }
-}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/networkObject/getNetworkObjectsForManagement.graphql b/roles/lib/files/FWO.Api.Client/APIcalls/networkObject/getNetworkObjectsForManagement.graphql
deleted file mode 100644
index 6fcf832d96..0000000000
--- a/roles/lib/files/FWO.Api.Client/APIcalls/networkObject/getNetworkObjectsForManagement.graphql
+++ /dev/null
@@ -1,11 +0,0 @@
-query getNetworkObjectsForManagement (
- $mgmId: Int
- $objTypeIds: [Int!]
- $active: Boolean
- $import_id_start: bigint
- $import_id_end: bigint
-){
- object(where: {mgm_id: { _eq: $mgmId }, active: {_eq: true}, obj_typ_id: {_in: $objTypeIds}, obj_create: {_lte: $import_id_end}, obj_last_seen: {_gte: $import_id_start}}) {
- ...networkObjectDetailsForVariance
- }
-}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/recertification/getOpenRecertsForOwners.graphql b/roles/lib/files/FWO.Api.Client/APIcalls/recertification/getOpenRecertsForOwners.graphql
deleted file mode 100644
index 47357e44ab..0000000000
--- a/roles/lib/files/FWO.Api.Client/APIcalls/recertification/getOpenRecertsForOwners.graphql
+++ /dev/null
@@ -1,17 +0,0 @@
-query getOpenRecertsForOwners($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 }
- ) {
- id
- rule_metadata_id
- rule_id
- ip_match
- owner_id
- user_dn
- recertified
- next_recert_date
- recert_date
- comment
- }
-}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/getRelevantImportIdsInTimeRange.graphql b/roles/lib/files/FWO.Api.Client/APIcalls/report/getRelevantImportIdsInTimeRange.graphql
deleted file mode 100644
index 558b8898bd..0000000000
--- a/roles/lib/files/FWO.Api.Client/APIcalls/report/getRelevantImportIdsInTimeRange.graphql
+++ /dev/null
@@ -1,30 +0,0 @@
-query getRelevantImportIdsInTimeRange(
- $start_time: timestamp!
- $end_time: timestamp!
- $mgmIds: [Int!]
- $ruleChangesFound: Boolean
-) {
- import_control(
- where: {
- _and: [
- { stop_time: { _gte: $start_time } }
- { stop_time: { _lte: $end_time } }
- ]
- successful_import: { _eq: true }
- management: {
- mgm_id: { _in: $mgmIds }
- hide_in_gui: { _eq: false }
- stm_dev_typ: {
- dev_typ_is_multi_mgmt: { _eq: false }
- is_pure_routing_device: { _eq: false }
- }
- }
- any_changes_found: { _eq: true }
- rule_changes_found: { _eq: $ruleChangesFound }
- }
- order_by: { stop_time: asc }
- ) {
- mgm_id
- control_id
- }
-}
\ No newline at end of file
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/getUsageDataCount.graphql b/roles/lib/files/FWO.Api.Client/APIcalls/report/getUsageDataCount.graphql
deleted file mode 100644
index a76006fea1..0000000000
--- a/roles/lib/files/FWO.Api.Client/APIcalls/report/getUsageDataCount.graphql
+++ /dev/null
@@ -1,8 +0,0 @@
-
-query getUsageDataCount($devId: Int) {
- rule_aggregate(where: {_and: [ {dev_id: {_eq: $devId } }, { rule_metadatum: {rule_last_hit: { _is_null: false } } } ] }) {
- aggregate {
- count
- }
- }
-}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/unused_addReportTemplatePlusPermissions.graphql b/roles/lib/files/FWO.Api.Client/APIcalls/report/unused_addReportTemplatePlusPermissions.graphql
deleted file mode 100644
index 6cc0ea43ee..0000000000
--- a/roles/lib/files/FWO.Api.Client/APIcalls/report/unused_addReportTemplatePlusPermissions.graphql
+++ /dev/null
@@ -1,24 +0,0 @@
-mutation addReportTemplateWithPermissions(
- $reportTemplateName: String!
- $reportFilterLine: String!
- $userId: Int
- $tenantId: Int
- $reportParameters: json
-) {
- insert_report_template(
- objects: [
- {
- report_template_name: $reportTemplateName
- report_filter: $reportFilterLine
- report_template_owner: $userId
- report_template_viewable_by_users: { data: { uiuser_id: $userId } }
- report_template_viewable_by_tenants: { data: { tenant_id: $tenantId } }
- report_parameters: $reportParameters
- }
- ]
- ) {
- returning {
- report_template_id
- }
- }
-}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/unused_getAllChangeDetails.graphql b/roles/lib/files/FWO.Api.Client/APIcalls/report/unused_getAllChangeDetails.graphql
deleted file mode 100644
index 0573da6047..0000000000
--- a/roles/lib/files/FWO.Api.Client/APIcalls/report/unused_getAllChangeDetails.graphql
+++ /dev/null
@@ -1,96 +0,0 @@
-query changeReport($start: timestamp, $stop: timestamp) {
- management(order_by: {mgm_name: asc}) {
- mgm_id
- mgm_name
- devices (order_by: {dev_name: asc}) {
- dev_id
- dev_name
- changelog_rules(
- where: {
- _and: [
- { import_control: { stop_time: { _gte: $start } } }
- { import_control: { stop_time: { _lte: $stop } } }
- ]
- change_type_id: { _eq: 3 }
- security_relevant: { _eq: true }
- }
- order_by: { control_id: asc }
- ) {
- change_time
- change_action
- old: ruleByOldRuleId {
- ...ruleOverview
- }
- new: rule {
- ...ruleOverview
- }
- }
- }
- changelog_objects(
- where: {
- _and: [
- { import_control: { stop_time: { _gte: $start } } }
- { import_control: { stop_time: { _lte: $stop } } }
- ]
- change_type_id: { _eq: 3 }
- security_relevant: { _eq: true }
- }
- order_by: { control_id: asc }
- ) {
- change_time
- security_relevant
- change_action
- change_type_id
- old: objectByOldObjId {
- ...networkObjectOverview
- }
- new: object {
- ...networkObjectOverview
- }
- }
- changelog_services(
- where: {
- _and: [
- { import_control: { stop_time: { _gte: $start } } }
- { import_control: { stop_time: { _lte: $stop } } }
- ]
- change_type_id: { _eq: 3 }
- security_relevant: { _eq: true }
- }
- order_by: { control_id: asc }
- ) {
- change_time
- security_relevant
- change_action
- change_type_id
- old: serviceByOldSvcId {
- ...networkServiceOverview
- }
- new: service {
- ...networkServiceOverview
- }
- }
- changelog_users(
- where: {
- _and: [
- { import_control: { stop_time: { _gte: $start } } }
- { import_control: { stop_time: { _lte: $stop } } }
- ]
- change_type_id: { _eq: 3 }
- security_relevant: { _eq: true }
- }
- order_by: { control_id: asc }
- ) {
- change_time
- security_relevant
- change_action
- change_type_id
- old: usrByOldUserId {
- ...userOverview
- }
- new: usr {
- ...userOverview
- }
- }
- }
-}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/report/unused_getRuleChangeDetails.graphql b/roles/lib/files/FWO.Api.Client/APIcalls/report/unused_getRuleChangeDetails.graphql
deleted file mode 100644
index 8e60a96dd1..0000000000
--- a/roles/lib/files/FWO.Api.Client/APIcalls/report/unused_getRuleChangeDetails.graphql
+++ /dev/null
@@ -1,30 +0,0 @@
-query changeRuleReport($start: timestamp, $stop: timestamp) {
- management(order_by: {mgm_name: asc}) {
- mgm_id
- mgm_name
- devices (order_by: {dev_name: asc}) {
- dev_id
- dev_name
- changelog_rules(
- where: {
- _and: [
- { import_control: { stop_time: { _gte: $start } } }
- { import_control: { stop_time: { _lte: $stop } } }
- ]
- change_type_id: { _eq: 3 }
- security_relevant: { _eq: true }
- }
- order_by: { control_id: asc }
- ) {
- change_time
- change_action
- old: ruleByOldRuleId {
- ...ruleOverview
- }
- new: rule {
- ...ruleOverview
- }
- }
- }
- }
-}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/rule/getNatRuleDetails.graphql b/roles/lib/files/FWO.Api.Client/APIcalls/rule/getNatRuleDetails.graphql
deleted file mode 100644
index 5733669ae7..0000000000
--- a/roles/lib/files/FWO.Api.Client/APIcalls/rule/getNatRuleDetails.graphql
+++ /dev/null
@@ -1,44 +0,0 @@
-query getNatRuleDetails(
- $management_id: [Int!]
- $device_id: [Int!]
- $rule_id: [bigint!]
- $rule_uid: [String!]
- $ruleSrcName: [String!]
- $ruleSrcIp: [cidr!]
- $active: Boolean
- $import_id_start: bigint
- $import_id_end: bigint
- $limit: Int
- $offset: Int
-) {
- management(
- where: { mgm_id: { _in: $management_id } }
- order_by: { mgm_name: asc }
- ) {
- id: mgm_id
- name: mgm_name
- devices(
- where: { dev_id: { _in: $device_id } }
- order_by: { dev_name: asc }
- ) {
- dev_id
- dev_name
- rules(
- limit: $limit
- offset: $offset
- where: {
- rule_id: { _in: $rule_id }
- rule_uid: { _in: $rule_uid }
- active: { _eq: true }
- rule_src: { _in: $ruleSrcName }
- rule_froms: { object: { obj_ip: { _in: $ruleSrcIp } } }
- nat_rule: { _eq: true }
- }
- order_by: { rule_num_numeric: asc }
- ) {
- mgm_id: mgm_id
- ...natRuleDetails
- }
- }
- }
-}
diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/rule/getRuleDetails.graphql b/roles/lib/files/FWO.Api.Client/APIcalls/rule/getRuleDetails.graphql
deleted file mode 100644
index 02bb85d1b4..0000000000
--- a/roles/lib/files/FWO.Api.Client/APIcalls/rule/getRuleDetails.graphql
+++ /dev/null
@@ -1,46 +0,0 @@
-query getRuleDetails(
- $management_id: [Int!]
- $device_id: [Int!]
- $rule_id: [bigint!]
- $rule_uid: [String!]
- $ruleSrcName: [String!]
- $ruleSrcIp: [cidr!]
- $active: Boolean
- $import_id_start: bigint
- $import_id_end: bigint
- $limit: Int
- $offset: Int
-) {
- management(
- where: { mgm_id: { _in: $management_id } }
- order_by: { mgm_name: asc }
- ) {
- id: mgm_id
- name: mgm_name
- devices(
- where: { dev_id: { _in: $device_id } }
- order_by: { dev_name: asc }
- ) {
- dev_id
- dev_name
- rules(
- limit: $limit
- offset: $offset
- where: {
- rule_id: { _in: $rule_id }
- rule_uid: { _in: $rule_uid }
- active: { _eq: $active }
- rule_last_seen: { _gte: $import_id_start }
- rule_create: { _lte: $import_id_end }
- rule_src: { _in: $ruleSrcName }
- rule_froms: { object: { obj_ip: { _in: $ruleSrcIp } } }
- access_rule: { _eq: true }
- }
- order_by: { rule_num_numeric: asc }
- ) {
- mgm_id: mgm_id
- ...ruleDetails
- }
- }
- }
-}
diff --git a/roles/lib/files/FWO.Api.Client/GraphQlApiConnection.cs b/roles/lib/files/FWO.Api.Client/GraphQlApiConnection.cs
index 19631728c1..0539d195d0 100644
--- a/roles/lib/files/FWO.Api.Client/GraphQlApiConnection.cs
+++ b/roles/lib/files/FWO.Api.Client/GraphQlApiConnection.cs
@@ -18,6 +18,8 @@ public class GraphQlApiConnection : ApiConnection
private string prevRole = "";
+#pragma warning disable CS8618 // C# does not notice that the constructor initializes these properties
+
private void Initialize(string ApiServerUri)
{
// Save Server URI
@@ -40,7 +42,7 @@ private void Initialize(string ApiServerUri)
// 1 hour timeout
graphQlClient.HttpClient.Timeout = new TimeSpan(1, 0, 0);
}
-
+
public GraphQlApiConnection(string ApiServerUri, string jwt)
{
Initialize(ApiServerUri);
@@ -51,6 +53,7 @@ public GraphQlApiConnection(string ApiServerUri)
{
Initialize(ApiServerUri);
}
+#pragma warning restore CS8618
public override void SetAuthHeader(string jwt)
{
@@ -132,7 +135,7 @@ public override async Task SendQueryAsync(
try
{
Log.WriteDebug("API call", $"Sending API call {operationName} in role {GetActRole()}: {query.Substring(0, Math.Min(query.Length, 70)).Replace(Environment.NewLine, "")}... " +
- ( variables != null ? $"with variables: {JsonSerializer.Serialize(variables).Substring(0, Math.Min(JsonSerializer.Serialize(variables).Length, 50)).Replace(Environment.NewLine, "")}..." : "" ));
+ (variables != null ? $"with variables: {JsonSerializer.Serialize(variables).Substring(0, Math.Min(JsonSerializer.Serialize(variables).Length, 50)).Replace(Environment.NewLine, "")}..." : ""));
GraphQLResponse response = await graphQlClient.SendQueryAsync(query, variables, operationName);
// Log.WriteDebug("API call", "API response received.");
@@ -171,7 +174,7 @@ public override async Task SendQueryAsync(
else
{
JObject data = (JObject)response.Data;
- JProperty prop = (JProperty)( data.First ?? throw new Exception($"Could not retrieve unique result attribute from Json.\nJson: {response.Data}") );
+ JProperty prop = (JProperty)(data.First ?? throw new Exception($"Could not retrieve unique result attribute from Json.\nJson: {response.Data}"));
JToken result = prop.Value;
QueryResponseType returnValue = result.ToObject() ??
throw new Exception($"Could not convert result from Json to {typeof(QueryResponseType)}.\nJson: {response.Data}");
@@ -182,7 +185,7 @@ public override async Task SendQueryAsync(
catch (Exception exception)
{
- Log.WriteError("API Connection", $"Error while sending query to GraphQL API. Query: {( query != null ? query : "" )}, variables: {( variables != null ? JsonSerializer.Serialize(variables) : "" )}", exception);
+ Log.WriteError("API Connection", $"Error while sending query to GraphQL API. Query: {(query != null ? query : "")}, variables: {(variables != null ? JsonSerializer.Serialize(variables) : "")}", exception);
throw;
}
}
diff --git a/roles/lib/files/FWO.Api.Client/Queries/AuthQueries.cs b/roles/lib/files/FWO.Api.Client/Queries/AuthQueries.cs
index 3ded63a37f..2679a53a9c 100644
--- a/roles/lib/files/FWO.Api.Client/Queries/AuthQueries.cs
+++ b/roles/lib/files/FWO.Api.Client/Queries/AuthQueries.cs
@@ -46,46 +46,50 @@ static AuthQueries()
{
try
{
- getTenantId = File.ReadAllText(QueryPath + "auth/getTenantId.graphql");
- getTenants = File.ReadAllText(QueryPath + "auth/getTenants.graphql");
- addTenant = File.ReadAllText(QueryPath + "auth/addTenant.graphql");
- updateTenant = File.ReadAllText(QueryPath + "auth/updateTenant.graphql");
- deleteTenant = File.ReadAllText(QueryPath + "auth/deleteTenant.graphql");
- addDeviceToTenant = File.ReadAllText(QueryPath + "auth/addDeviceToTenant.graphql");
- addTenantToManagement = File.ReadAllText(QueryPath + "auth/addTenantToManagement.graphql");
- addTenantToGateway = File.ReadAllText(QueryPath + "auth/addTenantToGateway.graphql");
- deleteAllGatewaysOfTenant = File.ReadAllText(QueryPath + "auth/deleteAllGatewaysOfTenant.graphql");
- deleteAllManagementsOfTenant = File.ReadAllText(QueryPath + "auth/deleteAllManagementsOfTenant.graphql");
- getVisibleDeviceIdsPerTenant = File.ReadAllText(QueryPath + "auth/getTenantVisibleDeviceIds.graphql");
- getVisibleManagementIdsPerTenant = File.ReadAllText(QueryPath + "auth/getTenantVisibleManagementIds.graphql");
- getTenantNetworks = File.ReadAllText(QueryPath + "auth/getTenantNetworks.graphql");
- addTenantNetwork = File.ReadAllText(QueryPath + "auth/addTenantNetwork.graphql");
- deleteTenantNetwork = File.ReadAllText(QueryPath + "auth/deleteTenantNetwork.graphql");
+ getTenantId = GetQueryText("auth/getTenantId.graphql");
+ getTenants = GetQueryText("auth/getTenants.graphql");
+ addTenant = GetQueryText("auth/addTenant.graphql");
+ updateTenant = GetQueryText("auth/updateTenant.graphql");
+ deleteTenant = GetQueryText("auth/deleteTenant.graphql");
+ addDeviceToTenant = GetQueryText("auth/addDeviceToTenant.graphql");
+ addTenantToManagement = GetQueryText("auth/addTenantToManagement.graphql");
+ addTenantToGateway = GetQueryText("auth/addTenantToGateway.graphql");
+ deleteAllGatewaysOfTenant = GetQueryText("auth/deleteAllGatewaysOfTenant.graphql");
+ deleteAllManagementsOfTenant = GetQueryText("auth/deleteAllManagementsOfTenant.graphql");
+ getVisibleDeviceIdsPerTenant = GetQueryText("auth/getTenantVisibleDeviceIds.graphql");
+ getVisibleManagementIdsPerTenant = GetQueryText("auth/getTenantVisibleManagementIds.graphql");
+ getTenantNetworks = GetQueryText("auth/getTenantNetworks.graphql");
+ addTenantNetwork = GetQueryText("auth/addTenantNetwork.graphql");
+ deleteTenantNetwork = GetQueryText("auth/deleteTenantNetwork.graphql");
- getUsers = File.ReadAllText(QueryPath + "auth/getUsers.graphql");
- getUserEmails = File.ReadAllText(QueryPath + "auth/getUserEmails.graphql");
- getUserByDn = File.ReadAllText(QueryPath + "auth/getUserByDn.graphql");
- getUserByDbId = File.ReadAllText(QueryPath + "auth/getUserByDbId.graphql");
- upsertUiUser = File.ReadAllText(QueryPath + "auth/upsertUiUser.graphql");
- updateUserEmail = File.ReadAllText(QueryPath + "auth/updateUserEmail.graphql");
- updateUserLanguage = File.ReadAllText(QueryPath + "auth/updateUserLanguage.graphql");
- updateUserLastLogin = File.ReadAllText(QueryPath + "auth/updateUserLastLogin.graphql");
- updateUserPasswordChange = File.ReadAllText(QueryPath + "auth/updateUserPasswordChange.graphql");
- deleteUser = File.ReadAllText(QueryPath + "auth/deleteUser.graphql");
- assertUserExists = File.ReadAllText(QueryPath + "auth/assertUserExists.graphql");
+ getUsers = GetQueryText("auth/getUsers.graphql");
+ getUserEmails = GetQueryText("auth/getUserEmails.graphql");
+ getUserByDn = GetQueryText("auth/getUserByDn.graphql");
+ getUserByDbId = GetQueryText("auth/getUserByDbId.graphql");
+ upsertUiUser = GetQueryText("auth/upsertUiUser.graphql");
+ updateUserEmail = GetQueryText("auth/updateUserEmail.graphql");
+ updateUserLanguage = GetQueryText("auth/updateUserLanguage.graphql");
+ updateUserLastLogin = GetQueryText("auth/updateUserLastLogin.graphql");
+ updateUserPasswordChange = GetQueryText("auth/updateUserPasswordChange.graphql");
+ deleteUser = GetQueryText("auth/deleteUser.graphql");
+ assertUserExists = GetQueryText("auth/assertUserExists.graphql");
- getLdapConnections = File.ReadAllText(QueryPath + "auth/getLdapConnections.graphql");
- getAllLdapConnections = File.ReadAllText(QueryPath + "auth/getAllLdapConnections.graphql");
- getLdapConnectionsSubscription = File.ReadAllText(QueryPath + "auth/getLdapConnectionsSubscription.graphql");
- getLdapConnectionForUserSearchById = File.ReadAllText(QueryPath + "auth/getLdapConnectionForUserSearchById.graphql");
- newLdapConnection = File.ReadAllText(QueryPath + "auth/newLdapConnection.graphql");
- updateLdapConnection = File.ReadAllText(QueryPath + "auth/updateLdapConnection.graphql");
- deleteLdapConnection = File.ReadAllText(QueryPath + "auth/deleteLdapConnection.graphql");
+ getLdapConnections = GetQueryText("auth/getLdapConnections.graphql");
+ getAllLdapConnections = GetQueryText("auth/getAllLdapConnections.graphql");
+ getLdapConnectionsSubscription = GetQueryText("auth/getLdapConnectionsSubscription.graphql");
+ getLdapConnectionForUserSearchById = GetQueryText("auth/getLdapConnectionForUserSearchById.graphql");
+ newLdapConnection = GetQueryText("auth/newLdapConnection.graphql");
+ updateLdapConnection = GetQueryText("auth/updateLdapConnection.graphql");
+ deleteLdapConnection = GetQueryText("auth/deleteLdapConnection.graphql");
}
catch (Exception exception)
{
Log.WriteError("Initialize AuthQueries", "Api AuthQueries could not be loaded.", exception);
+#if RELEASE
Environment.Exit(-1);
+#else
+ throw;
+#endif
}
}
}
diff --git a/roles/lib/files/FWO.Api.Client/Queries/ComplianceQueries.cs b/roles/lib/files/FWO.Api.Client/Queries/ComplianceQueries.cs
index a31b87581f..337460bc5e 100644
--- a/roles/lib/files/FWO.Api.Client/Queries/ComplianceQueries.cs
+++ b/roles/lib/files/FWO.Api.Client/Queries/ComplianceQueries.cs
@@ -5,25 +5,80 @@ namespace FWO.Api.Client.Queries
public class ComplianceQueries : Queries
{
public static readonly string addNetworkZone;
- public static readonly string deleteNetworkZone;
- public static readonly string getNetworkZones;
- public static readonly string updateNetworkZones;
- public static readonly string modifyNetworkZoneCommunication;
+ public static readonly string removeNetworkZone;
+ public static readonly string getNetworkZonesForMatrix;
+ public static readonly string updateNetworkZone;
+
+ public static readonly string updateNetworkZoneCommunication;
+
+ public static readonly string addViolations;
+ public static readonly string getViolations;
+ public static readonly string updateViolationById;
+ public static readonly string removeViolations;
+ public static readonly string getViolationsChunk;
+ public static readonly string getViolationsByRuleID;
+ public static readonly string getViolationsByRuleUid;
+
+ public static readonly string addPolicy;
+ public static readonly string disablePolicy;
+ public static readonly string getPolicies;
+ public static readonly string getPolicyById;
+
+ public static readonly string addCriterion;
+ public static readonly string removeCriterion;
+ public static readonly string updateCriterionMetadata;
+ public static readonly string getCriteria;
+ public static readonly string getMatrices;
+ public static readonly string getMatrixByName;
+
+ public static readonly string addCritToPolicy;
+ public static readonly string removeCritFromPolicy;
+ public static readonly string getPolicyIdsForCrit;
static ComplianceQueries()
{
try
{
- addNetworkZone = File.ReadAllText(QueryPath + "compliance/addNetworkZone.graphql");
- deleteNetworkZone = File.ReadAllText(QueryPath + "compliance/deleteNetworkZone.graphql");
- getNetworkZones = File.ReadAllText(QueryPath + "compliance/getNetworkZones.graphql");
- updateNetworkZones = File.ReadAllText(QueryPath + "compliance/updateNetworkZone.graphql");
- modifyNetworkZoneCommunication = File.ReadAllText(QueryPath + "compliance/updateNetworkZoneCommunication.graphql");
+ addNetworkZone = GetQueryText("compliance/addNetworkZone.graphql");
+ removeNetworkZone = GetQueryText("compliance/removeNetworkZone.graphql");
+ getNetworkZonesForMatrix = GetQueryText("compliance/getNetworkZonesForMatrix.graphql");
+ updateNetworkZone = GetQueryText("compliance/updateNetworkZone.graphql");
+
+ updateNetworkZoneCommunication = GetQueryText("compliance/updateNetworkZoneCommunication.graphql");
+
+ addViolations = GetQueryText("compliance/addViolations.graphql");
+ getViolations = GetQueryText("compliance/getViolations.graphql");
+ updateViolationById = GetQueryText("compliance/updateViolationById.graphql");
+ removeViolations = GetQueryText("compliance/removeViolations.graphql");
+
+ addPolicy = GetQueryText("compliance/addPolicy.graphql");
+ disablePolicy = GetQueryText("compliance/disablePolicy.graphql");
+ getPolicies = GetQueryText("compliance/getPolicies.graphql");
+ getPolicyById = GetQueryText("compliance/getPolicyById.graphql");
+
+ addCriterion = GetQueryText("compliance/addCriterion.graphql");
+ removeCriterion = GetQueryText("compliance/removeCriterion.graphql");
+ updateCriterionMetadata = GetQueryText("compliance/updateCriterionMetadata.graphql");
+ getCriteria = GetQueryText("compliance/getCriteria.graphql");
+ getMatrices = GetQueryText("compliance/getMatrices.graphql");
+ getMatrixByName = GetQueryText("compliance/getMatrixByName.graphql");
+
+ addCritToPolicy = GetQueryText("compliance/addCritToPolicy.graphql");
+ removeCritFromPolicy = GetQueryText("compliance/removeCritFromPolicy.graphql");
+ getPolicyIdsForCrit = GetQueryText("compliance/getPolicyIdsForCrit.graphql");
+
+ getViolationsChunk = GetQueryText("compliance/getViolationsChunk.graphql");
+ getViolationsByRuleID = GetQueryText("compliance/getViolationsByRuleID.graphql");
+ getViolationsByRuleUid = GetQueryText("compliance/getViolationsByRuleUid.graphql");
}
catch (Exception exception)
{
Log.WriteError("Initialize Compliance Queries", "Api compliance queries could not be loaded.", exception);
+#if RELEASE
Environment.Exit(-1);
+#else
+ throw;
+#endif
}
}
}
diff --git a/roles/lib/files/FWO.Api.Client/Queries/ConfigQueries.cs b/roles/lib/files/FWO.Api.Client/Queries/ConfigQueries.cs
index 90241cebd6..44f6d7a9b6 100644
--- a/roles/lib/files/FWO.Api.Client/Queries/ConfigQueries.cs
+++ b/roles/lib/files/FWO.Api.Client/Queries/ConfigQueries.cs
@@ -24,37 +24,43 @@ public class ConfigQueries : Queries
public static readonly string subscribeImportIpDataConfigChanges;
public static readonly string subscribeImportNotifyConfigChanges;
public static readonly string subscribeVarianceAnalysisConfigChanges;
+ public static readonly string subscribeComplianceCheckConfigChanges;
static ConfigQueries()
{
try
{
- getLanguages = File.ReadAllText(QueryPath + "config/getLanguages.graphql");
- getAllTexts = File.ReadAllText(QueryPath + "config/getTexts.graphql");
- getTextsPerLanguage = File.ReadAllText(QueryPath + "config/getTextsPerLanguage.graphql");
- getCustomTextsPerLanguage = File.ReadAllText(QueryPath + "config/getCustomTextsPerLanguage.graphql");
- upsertCustomText = File.ReadAllText(QueryPath + "config/upsertCustomText.graphql");
- deleteCustomText = File.ReadAllText(QueryPath + "config/deleteCustomText.graphql");
- subscribeConfigChangesByUser = File.ReadAllText(QueryPath + "config/subscribeConfigChangesByUser.graphql");
- addConfigItem = File.ReadAllText(QueryPath + "config/addConfigItem.graphql");
- updateConfigItem = File.ReadAllText(QueryPath + "config/updateConfigItem.graphql");
- getConfigItemsByUser = File.ReadAllText(QueryPath + "config/getConfigItemsByUser.graphql");
- getConfigItemByKey = File.ReadAllText(QueryPath + "config/getConfigItemByKey.graphql");
- upsertConfigItem = File.ReadAllText(QueryPath + "config/upsertConfigItem.graphql");
- upsertConfigItems = File.ReadAllText(QueryPath + "config/upsertConfigItems.graphql");
- subscribeAutodiscoveryConfigChanges = File.ReadAllText(QueryPath + "config/subscribeAutodiscoveryConfigChanges.graphql");
- subscribeExternalRequestConfigChanges = File.ReadAllText(QueryPath + "config/subscribeExternalRequestConfigChanges.graphql");
- subscribeDailyCheckConfigChanges = File.ReadAllText(QueryPath + "config/subscribeDailyCheckConfigChanges.graphql");
- subscribeImportAppDataConfigChanges = File.ReadAllText(QueryPath + "config/subscribeImportAppDataConfigChanges.graphql");
- subscribeImportIpDataConfigChanges = File.ReadAllText(QueryPath + "config/subscribeImportSubnetDataConfigChanges.graphql");
- subscribeImportNotifyConfigChanges = File.ReadAllText(QueryPath + "config/subscribeImportNotifyConfigChanges.graphql");
- subscribeVarianceAnalysisConfigChanges = File.ReadAllText(QueryPath + "config/subscribeVarianceAnalysisConfigChanges.graphql");
+ getLanguages = GetQueryText("config/getLanguages.graphql");
+ getAllTexts = GetQueryText("config/getTexts.graphql");
+ getTextsPerLanguage = GetQueryText("config/getTextsPerLanguage.graphql");
+ getCustomTextsPerLanguage = GetQueryText("config/getCustomTextsPerLanguage.graphql");
+ upsertCustomText = GetQueryText("config/upsertCustomText.graphql");
+ deleteCustomText = GetQueryText("config/deleteCustomText.graphql");
+ subscribeConfigChangesByUser = GetQueryText("config/subscribeConfigChangesByUser.graphql");
+ addConfigItem = GetQueryText("config/addConfigItem.graphql");
+ updateConfigItem = GetQueryText("config/updateConfigItem.graphql");
+ getConfigItemsByUser = GetQueryText("config/getConfigItemsByUser.graphql");
+ getConfigItemByKey = GetQueryText("config/getConfigItemByKey.graphql");
+ upsertConfigItem = GetQueryText("config/upsertConfigItem.graphql");
+ upsertConfigItems = GetQueryText("config/upsertConfigItems.graphql");
+ subscribeAutodiscoveryConfigChanges = GetQueryText("config/subscribeAutodiscoveryConfigChanges.graphql");
+ subscribeExternalRequestConfigChanges = GetQueryText("config/subscribeExternalRequestConfigChanges.graphql");
+ subscribeDailyCheckConfigChanges = GetQueryText("config/subscribeDailyCheckConfigChanges.graphql");
+ subscribeImportAppDataConfigChanges = GetQueryText("config/subscribeImportAppDataConfigChanges.graphql");
+ subscribeImportIpDataConfigChanges = GetQueryText("config/subscribeImportSubnetDataConfigChanges.graphql");
+ subscribeImportNotifyConfigChanges = GetQueryText("config/subscribeImportNotifyConfigChanges.graphql");
+ subscribeVarianceAnalysisConfigChanges = GetQueryText("config/subscribeVarianceAnalysisConfigChanges.graphql");
+ subscribeComplianceCheckConfigChanges = GetQueryText("config/subscribeComplianceCheckConfigChanges.graphql");
}
catch (Exception exception)
{
Log.WriteError("Initialize ConfigQueries", "Api ConfigQueries could not be loaded.", exception);
+#if RELEASE
Environment.Exit(-1);
+#else
+ throw;
+#endif
}
}
}
diff --git a/roles/lib/files/FWO.Api.Client/Queries/DeviceQueries.cs b/roles/lib/files/FWO.Api.Client/Queries/DeviceQueries.cs
index 95cb92e19c..62151044ea 100644
--- a/roles/lib/files/FWO.Api.Client/Queries/DeviceQueries.cs
+++ b/roles/lib/files/FWO.Api.Client/Queries/DeviceQueries.cs
@@ -11,14 +11,18 @@ public class DeviceQueries : Queries
public static readonly string getDeviceTypeDetails;
public static readonly string newManagement;
public static readonly string updateManagement;
+ public static readonly string updateManagementUid;
+ public static readonly string updateManagementUids;
public static readonly string changeManagementState;
public static readonly string deleteManagement;
public static readonly string getDeviceDetails;
+ public static readonly string getDevicesWithRulebaseLinks;
public static readonly string newDevice;
+ public static readonly string getGatewayId;
public static readonly string updateDevice;
+ public static readonly string updateGatewayUid;
public static readonly string changeDeviceState;
public static readonly string deleteDevice;
- public static readonly string deleteImport;
public static readonly string getCredentials;
public static readonly string getCredentialsWithoutSecrets;
public static readonly string newCredential;
@@ -30,45 +34,53 @@ static DeviceQueries()
{
try
{
- getDevicesByManagement = File.ReadAllText(QueryPath + "device/getDevicesByManagement.graphql");
- getManagementNames = File.ReadAllText(QueryPath + "device/getManagementNames.graphql");
- getManagementsDetails = File.ReadAllText(QueryPath + "device/getManagementsDetails.graphql") + " "
- + File.ReadAllText(QueryPath + "device/fragments/managementDetails.graphql") + " "
- + File.ReadAllText(QueryPath + "device/fragments/deviceTypeDetails.graphql") + " "
- + File.ReadAllText(QueryPath + "device/fragments/importCredentials.graphql");
- getManagementDetailsWithoutSecrets = File.ReadAllText(QueryPath + "device/getManagementDetailsWithoutSecrets.graphql") + " "
- + File.ReadAllText(QueryPath + "device/fragments/managementDetailsWithoutSecrets.graphql") + " "
- + File.ReadAllText(QueryPath + "device/fragments/deviceTypeDetails.graphql") + " "
- + File.ReadAllText(QueryPath + "device/fragments/importCredentialsWithoutSecrets.graphql");
- getDeviceTypeDetails = File.ReadAllText(QueryPath + "device/getDeviceTypeDetails.graphql") + " "
- + File.ReadAllText(QueryPath + "device/fragments/deviceTypeDetails.graphql");
- newManagement = File.ReadAllText(QueryPath + "device/newManagement.graphql");
- updateManagement = File.ReadAllText(QueryPath + "device/updateManagement.graphql");
- changeManagementState = File.ReadAllText(QueryPath + "device/changeManagementState.graphql");
- deleteManagement = File.ReadAllText(QueryPath + "device/deleteManagement.graphql");
- getDeviceDetails = File.ReadAllText(QueryPath + "device/getDeviceDetails.graphql") + " "
- + File.ReadAllText(QueryPath + "device/fragments/deviceDetails.graphql") + " "
- + File.ReadAllText(QueryPath + "device/fragments/deviceTypeDetails.graphql");
+ getDevicesByManagement = GetQueryText("device/getDevicesByManagement.graphql");
+ getManagementNames = GetQueryText("device/getManagementNames.graphql");
+ getManagementsDetails = GetQueryText("device/getManagementsDetails.graphql")
+ + GetQueryText("device/fragments/subManagements.graphql")
+ + GetQueryText("device/fragments/managementDetails.graphql")
+ + GetQueryText("device/fragments/deviceTypeDetails.graphql")
+ + GetQueryText("device/fragments/importCredentials.graphql");
+ getManagementDetailsWithoutSecrets = GetQueryText("device/getManagementDetailsWithoutSecrets.graphql")
+ + GetQueryText("device/fragments/managementDetailsWithoutSecrets.graphql")
+ + GetQueryText("device/fragments/deviceTypeDetails.graphql")
+ + GetQueryText("device/fragments/importCredentialsWithoutSecrets.graphql");
+ getDeviceTypeDetails = GetQueryText("device/getDeviceTypeDetails.graphql")
+ + GetQueryText("device/fragments/deviceTypeDetails.graphql");
+ newManagement = GetQueryText("device/newManagement.graphql");
+ updateManagement = GetQueryText("device/updateManagement.graphql");
+ updateManagementUid = GetQueryText("device/updateManagementUid.graphql");
+ updateManagementUids = GetQueryText("device/updateManagementUids.graphql");
+ changeManagementState = GetQueryText("device/changeManagementState.graphql");
+ deleteManagement = GetQueryText("device/deleteManagement.graphql");
+ getDeviceDetails = GetQueryText("device/getDeviceDetails.graphql")
+ + GetQueryText("device/fragments/deviceDetails.graphql")
+ + GetQueryText("device/fragments/deviceTypeDetails.graphql");
+ getDevicesWithRulebaseLinks = GetQueryText("device/getDevicesWithRulebaseLinks.graphql");
+ newDevice = GetQueryText("device/newDevice.graphql");
+ updateDevice = GetQueryText("device/updateDevice.graphql");
+ updateGatewayUid = GetQueryText("device/updateGatewayUid.graphql");
+ getGatewayId = GetQueryText("device/getGatewayId.graphql");
+ changeDeviceState = GetQueryText("device/changeDeviceState.graphql");
+ deleteDevice = GetQueryText("device/deleteDevice.graphql");
- newDevice = File.ReadAllText(QueryPath + "device/newDevice.graphql");
- updateDevice = File.ReadAllText(QueryPath + "device/updateDevice.graphql");
- changeDeviceState = File.ReadAllText(QueryPath + "device/changeDeviceState.graphql");
- deleteDevice = File.ReadAllText(QueryPath + "device/deleteDevice.graphql");
- deleteImport = File.ReadAllText(QueryPath + "device/deleteImport.graphql");
-
- getCredentials = File.ReadAllText(QueryPath + "device/getCredentials.graphql") + " "
- + File.ReadAllText(QueryPath + "device/fragments/importCredentials.graphql");
- getCredentialsWithoutSecrets = File.ReadAllText(QueryPath + "device/getCredentialsWithoutSecrets.graphql") + " "
- + File.ReadAllText(QueryPath + "device/fragments/importCredentialsWithoutSecrets.graphql");
- newCredential = File.ReadAllText(QueryPath + "device/newCredential.graphql");
- updateCredential = File.ReadAllText(QueryPath + "device/updateCredential.graphql");
- deleteCredential = File.ReadAllText(QueryPath + "device/deleteCredential.graphql");
- getMgmtNumberUsingCred = File.ReadAllText(QueryPath + "device/getMgmtNumberUsingCred.graphql");
+ getCredentials = GetQueryText("device/getCredentials.graphql")
+ + GetQueryText("device/fragments/importCredentials.graphql");
+ getCredentialsWithoutSecrets = GetQueryText("device/getCredentialsWithoutSecrets.graphql")
+ + GetQueryText("device/fragments/importCredentialsWithoutSecrets.graphql");
+ newCredential = GetQueryText("device/newCredential.graphql");
+ updateCredential = GetQueryText("device/updateCredential.graphql");
+ deleteCredential = GetQueryText("device/deleteCredential.graphql");
+ getMgmtNumberUsingCred = GetQueryText("device/getMgmtNumberUsingCred.graphql");
}
catch (Exception exception)
{
Log.WriteError("Initialize DeviceQueries", "Api DeviceQueries could not be loaded.", exception);
+#if RELEASE
Environment.Exit(-1);
+#else
+ throw;
+#endif
}
}
}
diff --git a/roles/lib/files/FWO.Api.Client/Queries/ExtRequestQueries.cs b/roles/lib/files/FWO.Api.Client/Queries/ExtRequestQueries.cs
index 97466ec012..2da938785e 100644
--- a/roles/lib/files/FWO.Api.Client/Queries/ExtRequestQueries.cs
+++ b/roles/lib/files/FWO.Api.Client/Queries/ExtRequestQueries.cs
@@ -26,27 +26,31 @@ static ExtRequestQueries()
{
try
{
- extRequestDetailsFragment = File.ReadAllText(QueryPath + "extRequest/fragments/extRequestDetails.graphql");
+ extRequestDetailsFragment = GetQueryText("extRequest/fragments/extRequestDetails.graphql");
- addTicketId = File.ReadAllText(QueryPath + "extRequest/addTicketId.graphql");
- getLatestTicketIds = File.ReadAllText(QueryPath + "extRequest/getLatestTicketIds.graphql");
- getLatestTicketId = File.ReadAllText(QueryPath + "extRequest/getLatestTicketId.graphql");
- addExtRequest = File.ReadAllText(QueryPath + "extRequest/addExtRequest.graphql");
- getOpenRequests = extRequestDetailsFragment + File.ReadAllText(QueryPath + "extRequest/getOpenRequests.graphql");
- getAndLockOpenRequests = extRequestDetailsFragment + File.ReadAllText(QueryPath + "extRequest/getAndLockOpenRequests.graphql");
- getLastRequest = extRequestDetailsFragment + File.ReadAllText(QueryPath + "extRequest/getLastRequest.graphql");
- updateExtRequestCreation = File.ReadAllText(QueryPath + "extRequest/updateExtRequestCreation.graphql");
- updateExtRequestProcess = File.ReadAllText(QueryPath + "extRequest/updateExtRequestProcess.graphql");
- updateExtRequestFinal = File.ReadAllText(QueryPath + "extRequest/updateExtRequestFinal.graphql");
- updateExternalRequestWaitCycles = File.ReadAllText(QueryPath + "extRequest/updateExternalRequestWaitCycles.graphql");
- updateExternalRequestLock = File.ReadAllText(QueryPath + "extRequest/updateExternalRequestLock.graphql");
+ addTicketId = GetQueryText("extRequest/addTicketId.graphql");
+ getLatestTicketIds = GetQueryText("extRequest/getLatestTicketIds.graphql");
+ getLatestTicketId = GetQueryText("extRequest/getLatestTicketId.graphql");
+ addExtRequest = GetQueryText("extRequest/addExtRequest.graphql");
+ getOpenRequests = extRequestDetailsFragment + GetQueryText("extRequest/getOpenRequests.graphql");
+ getAndLockOpenRequests = extRequestDetailsFragment + GetQueryText("extRequest/getAndLockOpenRequests.graphql");
+ getLastRequest = extRequestDetailsFragment + GetQueryText("extRequest/getLastRequest.graphql");
+ updateExtRequestCreation = GetQueryText("extRequest/updateExtRequestCreation.graphql");
+ updateExtRequestProcess = GetQueryText("extRequest/updateExtRequestProcess.graphql");
+ updateExtRequestFinal = GetQueryText("extRequest/updateExtRequestFinal.graphql");
+ updateExternalRequestWaitCycles = GetQueryText("extRequest/updateExternalRequestWaitCycles.graphql");
+ updateExternalRequestLock = GetQueryText("extRequest/updateExternalRequestLock.graphql");
- subscribeExtRequestStateUpdate = File.ReadAllText(QueryPath + "extRequest/subscribeExtRequestStateUpdate.graphql");
+ subscribeExtRequestStateUpdate = GetQueryText("extRequest/subscribeExtRequestStateUpdate.graphql");
}
catch (Exception exception)
{
Log.WriteError("Initialize ExtRequestQueries", "Api ExtRequestQueries could not be loaded.", exception);
+#if RELEASE
Environment.Exit(-1);
+#else
+ throw;
+#endif
}
}
}
diff --git a/roles/lib/files/FWO.Api.Client/Queries/ImportQueries.cs b/roles/lib/files/FWO.Api.Client/Queries/ImportQueries.cs
new file mode 100644
index 0000000000..14020b9e9e
--- /dev/null
+++ b/roles/lib/files/FWO.Api.Client/Queries/ImportQueries.cs
@@ -0,0 +1,36 @@
+using FWO.Logging;
+
+namespace FWO.Api.Client.Queries
+{
+ public class ImportQueries : Queries
+ {
+
+ public static readonly string deleteImport;
+ public static readonly string rollbackImport;
+ public static readonly string deleteLatestConfigOfManagement;
+ public static readonly string getLastImport;
+ public static readonly string getMaxImportId;
+
+ static ImportQueries()
+ {
+ try
+ {
+ deleteImport = GetQueryText("import/deleteImport.graphql");
+ rollbackImport = GetQueryText("import/rollbackImport.graphql");
+ deleteLatestConfigOfManagement = GetQueryText("import/deleteLatestConfigOfManagement.graphql");
+ getLastImport = GetQueryText("import/getLastImport.graphql");
+ getMaxImportId = GetQueryText("import/getMaxImportId.graphql");
+
+ }
+ catch (Exception exception)
+ {
+ Log.WriteError("Initialize DeviceQueries", "Api DeviceQueries could not be loaded.", exception);
+#if RELEASE
+ Environment.Exit(-1);
+#else
+ throw;
+#endif
+ }
+ }
+ }
+}
diff --git a/roles/lib/files/FWO.Api.Client/Queries/ModellingQueries.cs b/roles/lib/files/FWO.Api.Client/Queries/ModellingQueries.cs
index 6b05ba0352..54e4a48326 100644
--- a/roles/lib/files/FWO.Api.Client/Queries/ModellingQueries.cs
+++ b/roles/lib/files/FWO.Api.Client/Queries/ModellingQueries.cs
@@ -1,4 +1,4 @@
-using FWO.Logging;
+using FWO.Logging;
namespace FWO.Api.Client.Queries
{
@@ -116,120 +116,123 @@ static ModellingQueries()
{
try
{
- appServerDetailsFragment = File.ReadAllText(QueryPath + "modelling/fragments/appServerDetails.graphql");
- appRoleDetailsFragment = File.ReadAllText(QueryPath + "modelling/fragments/appRoleDetails.graphql");
- areaDetailsFragment = File.ReadAllText(QueryPath + "modelling/fragments/areaDetails.graphql");
- serviceDetailsFragment = File.ReadAllText(QueryPath + "modelling/fragments/serviceDetails.graphql");
- serviceGroupDetailsFragment = File.ReadAllText(QueryPath + "modelling/fragments/serviceGroupDetails.graphql");
+ appServerDetailsFragment = GetQueryText("modelling/fragments/appServerDetails.graphql");
+ appRoleDetailsFragment = GetQueryText("modelling/fragments/appRoleDetails.graphql");
+ areaDetailsFragment = GetQueryText("modelling/fragments/areaDetails.graphql");
+ serviceDetailsFragment = GetQueryText("modelling/fragments/serviceDetails.graphql");
+ serviceGroupDetailsFragment = GetQueryText("modelling/fragments/serviceGroupDetails.graphql");
connectionDetailsFragment = appServerDetailsFragment + appRoleDetailsFragment + serviceDetailsFragment + serviceGroupDetailsFragment +
- File.ReadAllText(QueryPath + "modelling/fragments/connectionDetails.graphql");
+ GetQueryText("modelling/fragments/connectionDetails.graphql");
connectionResolvedDetailsFragment = appServerDetailsFragment + appRoleDetailsFragment + areaDetailsFragment + serviceDetailsFragment + serviceGroupDetailsFragment +
- File.ReadAllText(QueryPath + "modelling/fragments/connectionResolvedDetails.graphql");
-
- getAreas = areaDetailsFragment + File.ReadAllText(QueryPath + "modelling/getAreas.graphql");
- newArea = File.ReadAllText(QueryPath + "modelling/newArea.graphql");
- setNwGroupDeletedState = File.ReadAllText(QueryPath + "modelling/setNwGroupDeletedState.graphql");
- newAreaIpData = File.ReadAllText(QueryPath + "modelling/newAreaIpData.graphql");
- getConnectionIdsForNwGroup = File.ReadAllText(QueryPath + "modelling/getConnectionIdsForNwGroup.graphql");
- getConnectionsForNwGroup = connectionDetailsFragment + File.ReadAllText(QueryPath + "modelling/getConnectionsForNwGroup.graphql");
-
- getAppServersByIp = appServerDetailsFragment + File.ReadAllText(QueryPath + "modelling/getAppServersByIp.graphql");
- getAppServersByName = appServerDetailsFragment + File.ReadAllText(QueryPath + "modelling/getAppServersByName.graphql");
- getAppServersForOwner = appServerDetailsFragment + File.ReadAllText(QueryPath + "modelling/getAppServersForOwner.graphql");
- getAppServersBySource = appServerDetailsFragment + File.ReadAllText(QueryPath + "modelling/getAppServersBySource.graphql");
- getAllAppServers = appServerDetailsFragment + File.ReadAllText(QueryPath + "modelling/getAllAppServers.graphql");
- newAppServer = File.ReadAllText(QueryPath + "modelling/newAppServer.graphql");
- updateAppServer = File.ReadAllText(QueryPath + "modelling/updateAppServer.graphql");
- setAppServerDeletedState = File.ReadAllText(QueryPath + "modelling/setAppServerDeletedState.graphql");
- setAppServerName = File.ReadAllText(QueryPath + "modelling/setAppServerName.graphql");
- setAppServerType = File.ReadAllText(QueryPath + "modelling/setAppServerType.graphql");
- deleteAppServer = File.ReadAllText(QueryPath + "modelling/deleteAppServer.graphql");
- getAppRolesForAppServer = File.ReadAllText(QueryPath + "modelling/getAppRolesForAppServer.graphql");
- getConnectionIdsForAppServer = File.ReadAllText(QueryPath + "modelling/getConnectionIdsForAppServer.graphql");
-
- getPublishedInterfaces = connectionDetailsFragment + File.ReadAllText(QueryPath + "modelling/getPublishedInterfaces.graphql");
- getConnectionById = connectionDetailsFragment + File.ReadAllText(QueryPath + "modelling/getConnectionById.graphql");
- getConnections = connectionDetailsFragment + File.ReadAllText(QueryPath + "modelling/getConnections.graphql");
- getConnectionsResolved = connectionResolvedDetailsFragment + File.ReadAllText(QueryPath + "modelling/getConnectionsResolved.graphql");
- getConnectionsByTicketId = connectionDetailsFragment + File.ReadAllText(QueryPath + "modelling/getConnectionsByTicketId.graphql");
- getDeletedConnections = connectionDetailsFragment + File.ReadAllText(QueryPath + "modelling/getDeletedConnections.graphql");
- getInterfaceUsers = File.ReadAllText(QueryPath + "modelling/getInterfaceUsers.graphql");
- getCommonServices = connectionDetailsFragment + File.ReadAllText(QueryPath + "modelling/getCommonServices.graphql");
- newConnection = File.ReadAllText(QueryPath + "modelling/newConnection.graphql");
- updateConnection = File.ReadAllText(QueryPath + "modelling/updateConnection.graphql");
- updateProposedConnectionOwner = File.ReadAllText(QueryPath + "modelling/updateProposedConnectionOwner.graphql");
- updateConnectionPublish = File.ReadAllText(QueryPath + "modelling/updateConnectionPublish.graphql");
- updateConnectionProperties = File.ReadAllText(QueryPath + "modelling/updateConnectionProperties.graphql");
- replaceUsedInterface = File.ReadAllText(QueryPath + "modelling/replaceUsedInterface.graphql");
- updateConnectionFwRequested = File.ReadAllText(QueryPath + "modelling/updateConnectionFwRequested.graphql");
- updateConnectionRemove = File.ReadAllText(QueryPath + "modelling/updateConnectionRemove.graphql");
- updateConnectionDecommission = File.ReadAllText(QueryPath + "modelling/updateConnectionDecommission.graphql");
- deleteConnection = File.ReadAllText(QueryPath + "modelling/deleteConnection.graphql");
- addAppServerToConnection = File.ReadAllText(QueryPath + "modelling/addAppServerToConnection.graphql");
- removeAppServerFromConnection = File.ReadAllText(QueryPath + "modelling/removeAppServerFromConnection.graphql");
- removeAllAppServersFromConnection = File.ReadAllText(QueryPath + "modelling/removeAllAppServersFromConnection.graphql");
- updateNwObjectInConnection = File.ReadAllText(QueryPath + "modelling/updateNwObjectInConnection.graphql");
- addNwGroupToConnection = File.ReadAllText(QueryPath + "modelling/addNwGroupToConnection.graphql");
- removeNwGroupFromConnection = File.ReadAllText(QueryPath + "modelling/removeNwGroupFromConnection.graphql");
- removeAllNwGroupsFromConnection = File.ReadAllText(QueryPath + "modelling/removeAllNwGroupsFromConnection.graphql");
- addServiceToConnection = File.ReadAllText(QueryPath + "modelling/addServiceToConnection.graphql");
- removeServiceFromConnection = File.ReadAllText(QueryPath + "modelling/removeServiceFromConnection.graphql");
- removeAllServicesFromConnection = File.ReadAllText(QueryPath + "modelling/removeAllServicesFromConnection.graphql");
- addServiceGroupToConnection = File.ReadAllText(QueryPath + "modelling/addServiceGroupToConnection.graphql");
- removeServiceGroupFromConnection = File.ReadAllText(QueryPath + "modelling/removeServiceGroupFromConnection.graphql");
- removeAllServiceGroupsFromConnection = File.ReadAllText(QueryPath + "modelling/removeAllServiceGroupsFromConnection.graphql");
- getConnectionIdsForService = File.ReadAllText(QueryPath + "modelling/getConnectionIdsForService.graphql");
- getConnectionIdsForServiceGroup = File.ReadAllText(QueryPath + "modelling/getConnectionIdsForServiceGroup.graphql");
-
- getSelectedConnections = connectionDetailsFragment + File.ReadAllText(QueryPath + "modelling/getSelectedConnections.graphql");
- addSelectedConnection = File.ReadAllText(QueryPath + "modelling/addSelectedConnection.graphql");
- removeSelectedConnectionFromApp = File.ReadAllText(QueryPath + "modelling/removeSelectedConnectionFromApp.graphql");
- removeSelectedConnection = File.ReadAllText(QueryPath + "modelling/removeSelectedConnection.graphql");
-
- getNwGroupObjects = File.ReadAllText(QueryPath + "modelling/getNwGroupObjects.graphql");
- getSelectedNwGroupObjects = File.ReadAllText(QueryPath + "modelling/getSelectedNwGroupObjects.graphql");
- addSelectedNwGroupObject = File.ReadAllText(QueryPath + "modelling/addSelectedNwGroupObject.graphql");
- removeSelectedNwGroupObject = File.ReadAllText(QueryPath + "modelling/removeSelectedNwGroupObject.graphql");
- removeSelectedNwGroupObjectFromAllApps = File.ReadAllText(QueryPath + "modelling/removeSelectedNwGroupObjectFromAllApps.graphql");
-
- getAppRoles = appServerDetailsFragment + appRoleDetailsFragment + File.ReadAllText(QueryPath + "modelling/getAppRoles.graphql");
- getNewestAppRoles = File.ReadAllText(QueryPath + "modelling/getNewestAppRoles.graphql");
- getDummyAppRole = appServerDetailsFragment + appRoleDetailsFragment + File.ReadAllText(QueryPath + "modelling/getDummyAppRole.graphql");
- newAppRole = File.ReadAllText(QueryPath + "modelling/newAppRole.graphql");
- updateAppRole = File.ReadAllText(QueryPath + "modelling/updateAppRole.graphql");
- deleteNwGroup = File.ReadAllText(QueryPath + "modelling/deleteNwGroup.graphql");
- // getAppServerForAppRole = appServerDetailsFragment + File.ReadAllText(QueryPath + "modelling/getAppServerForAppRole.graphql");
- addNwObjectToNwGroup = File.ReadAllText(QueryPath + "modelling/addNwObjectToNwGroup.graphql");
- removeNwObjectFromNwGroup = File.ReadAllText(QueryPath + "modelling/removeNwObjectFromNwGroup.graphql");
- updateNwObjectInNwGroup = File.ReadAllText(QueryPath + "modelling/updateNwObjectInNwGroup.graphql");
-
- getServicesForApp = serviceDetailsFragment + File.ReadAllText(QueryPath + "modelling/getServicesForApp.graphql");
- getGlobalServices = serviceDetailsFragment + File.ReadAllText(QueryPath + "modelling/getGlobalServices.graphql");
- newService = File.ReadAllText(QueryPath + "modelling/newService.graphql");
- updateService = File.ReadAllText(QueryPath + "modelling/updateService.graphql");
- deleteService = File.ReadAllText(QueryPath + "modelling/deleteService.graphql");
-
- getServiceGroupsForApp = serviceDetailsFragment + serviceGroupDetailsFragment + File.ReadAllText(QueryPath + "modelling/getServiceGroupsForApp.graphql");
- getServiceGroupById = serviceDetailsFragment + serviceGroupDetailsFragment + File.ReadAllText(QueryPath + "modelling/getServiceGroupById.graphql");
- getGlobalServiceGroups = serviceDetailsFragment + serviceGroupDetailsFragment + File.ReadAllText(QueryPath + "modelling/getGlobalServiceGroups.graphql");
- newServiceGroup = File.ReadAllText(QueryPath + "modelling/newServiceGroup.graphql");
- updateServiceGroup = File.ReadAllText(QueryPath + "modelling/updateServiceGroup.graphql");
- deleteServiceGroup = File.ReadAllText(QueryPath + "modelling/deleteServiceGroup.graphql");
- addServiceToServiceGroup = File.ReadAllText(QueryPath + "modelling/addServiceToServiceGroup.graphql");
- removeServiceFromServiceGroup = File.ReadAllText(QueryPath + "modelling/removeServiceFromServiceGroup.graphql");
- getServiceGroupIdsForService = File.ReadAllText(QueryPath + "modelling/getServiceGroupIdsForService.graphql");
-
- getHistory = File.ReadAllText(QueryPath + "modelling/getHistory.graphql");
- getHistoryForApp = File.ReadAllText(QueryPath + "modelling/getHistoryForApp.graphql");
- addHistoryEntry = File.ReadAllText(QueryPath + "modelling/addHistoryEntry.graphql");
-
- newAppZone = File.ReadAllText(QueryPath + "modelling/addNwAppZone.graphql");
- getAppZonesByAppId = appServerDetailsFragment + File.ReadAllText(QueryPath + "modelling/getAppZonesByAppId.graphql");
+ GetQueryText("modelling/fragments/connectionResolvedDetails.graphql");
+
+ getAreas = areaDetailsFragment + GetQueryText("modelling/getAreas.graphql");
+ newArea = GetQueryText("modelling/newArea.graphql");
+ setNwGroupDeletedState = GetQueryText("modelling/setNwGroupDeletedState.graphql");
+ newAreaIpData = GetQueryText("modelling/newAreaIpData.graphql");
+ getConnectionIdsForNwGroup = GetQueryText("modelling/getConnectionIdsForNwGroup.graphql");
+ getConnectionsForNwGroup = connectionDetailsFragment + GetQueryText("modelling/getConnectionsForNwGroup.graphql");
+
+ getAppServersByIp = appServerDetailsFragment + GetQueryText("modelling/getAppServersByIp.graphql");
+ getAppServersByName = appServerDetailsFragment + GetQueryText("modelling/getAppServersByName.graphql");
+ getAppServersForOwner = appServerDetailsFragment + GetQueryText("modelling/getAppServersForOwner.graphql");
+ getAppServersBySource = appServerDetailsFragment + GetQueryText("modelling/getAppServersBySource.graphql");
+ getAllAppServers = appServerDetailsFragment + GetQueryText("modelling/getAllAppServers.graphql");
+ newAppServer = GetQueryText("modelling/newAppServer.graphql");
+ updateAppServer = GetQueryText("modelling/updateAppServer.graphql");
+ setAppServerDeletedState = GetQueryText("modelling/setAppServerDeletedState.graphql");
+ setAppServerName = GetQueryText("modelling/setAppServerName.graphql");
+ setAppServerType = GetQueryText("modelling/setAppServerType.graphql");
+ deleteAppServer = GetQueryText("modelling/deleteAppServer.graphql");
+ getAppRolesForAppServer = GetQueryText("modelling/getAppRolesForAppServer.graphql");
+ getConnectionIdsForAppServer = GetQueryText("modelling/getConnectionIdsForAppServer.graphql");
+
+ getPublishedInterfaces = connectionDetailsFragment + GetQueryText("modelling/getPublishedInterfaces.graphql");
+ getConnectionById = connectionDetailsFragment + GetQueryText("modelling/getConnectionById.graphql");
+ getConnections = connectionDetailsFragment + GetQueryText("modelling/getConnections.graphql");
+ getConnectionsResolved = connectionResolvedDetailsFragment + GetQueryText("modelling/getConnectionsResolved.graphql");
+ getConnectionsByTicketId = connectionDetailsFragment + GetQueryText("modelling/getConnectionsByTicketId.graphql");
+ getDeletedConnections = connectionDetailsFragment + GetQueryText("modelling/getDeletedConnections.graphql");
+ getInterfaceUsers = GetQueryText("modelling/getInterfaceUsers.graphql");
+ getCommonServices = connectionDetailsFragment + GetQueryText("modelling/getCommonServices.graphql");
+ newConnection = GetQueryText("modelling/newConnection.graphql");
+ updateConnection = GetQueryText("modelling/updateConnection.graphql");
+ updateProposedConnectionOwner = GetQueryText("modelling/updateProposedConnectionOwner.graphql");
+ updateConnectionPublish = GetQueryText("modelling/updateConnectionPublish.graphql");
+ updateConnectionProperties = GetQueryText("modelling/updateConnectionProperties.graphql");
+ replaceUsedInterface = GetQueryText("modelling/replaceUsedInterface.graphql");
+ updateConnectionFwRequested = GetQueryText("modelling/updateConnectionFwRequested.graphql");
+ updateConnectionRemove = GetQueryText("modelling/updateConnectionRemove.graphql");
+ deleteConnection = GetQueryText("modelling/deleteConnection.graphql");
+ addAppServerToConnection = GetQueryText("modelling/addAppServerToConnection.graphql");
+ removeAppServerFromConnection = GetQueryText("modelling/removeAppServerFromConnection.graphql");
+ removeAllAppServersFromConnection = GetQueryText("modelling/removeAllAppServersFromConnection.graphql");
+ updateNwObjectInConnection = GetQueryText("modelling/updateNwObjectInConnection.graphql");
+ addNwGroupToConnection = GetQueryText("modelling/addNwGroupToConnection.graphql");
+ removeNwGroupFromConnection = GetQueryText("modelling/removeNwGroupFromConnection.graphql");
+ removeAllNwGroupsFromConnection = GetQueryText("modelling/removeAllNwGroupsFromConnection.graphql");
+ addServiceToConnection = GetQueryText("modelling/addServiceToConnection.graphql");
+ removeServiceFromConnection = GetQueryText("modelling/removeServiceFromConnection.graphql");
+ removeAllServicesFromConnection = GetQueryText("modelling/removeAllServicesFromConnection.graphql");
+ addServiceGroupToConnection = GetQueryText("modelling/addServiceGroupToConnection.graphql");
+ removeServiceGroupFromConnection = GetQueryText("modelling/removeServiceGroupFromConnection.graphql");
+ removeAllServiceGroupsFromConnection = GetQueryText("modelling/removeAllServiceGroupsFromConnection.graphql");
+ getConnectionIdsForService = GetQueryText("modelling/getConnectionIdsForService.graphql");
+ getConnectionIdsForServiceGroup = GetQueryText("modelling/getConnectionIdsForServiceGroup.graphql");
+ updateConnectionDecommission = GetQueryText("modelling/updateConnectionDecommission.graphql");
+
+ getSelectedConnections = connectionDetailsFragment + GetQueryText("modelling/getSelectedConnections.graphql");
+ addSelectedConnection = GetQueryText("modelling/addSelectedConnection.graphql");
+ removeSelectedConnectionFromApp = GetQueryText("modelling/removeSelectedConnectionFromApp.graphql");
+ removeSelectedConnection = GetQueryText("modelling/removeSelectedConnection.graphql");
+
+ getNwGroupObjects = GetQueryText("modelling/getNwGroupObjects.graphql");
+ getSelectedNwGroupObjects = GetQueryText("modelling/getSelectedNwGroupObjects.graphql");
+ addSelectedNwGroupObject = GetQueryText("modelling/addSelectedNwGroupObject.graphql");
+ removeSelectedNwGroupObject = GetQueryText("modelling/removeSelectedNwGroupObject.graphql");
+ removeSelectedNwGroupObjectFromAllApps = GetQueryText("modelling/removeSelectedNwGroupObjectFromAllApps.graphql");
+
+ getAppRoles = appServerDetailsFragment + appRoleDetailsFragment + GetQueryText("modelling/getAppRoles.graphql");
+ getNewestAppRoles = GetQueryText("modelling/getNewestAppRoles.graphql");
+ getDummyAppRole = appServerDetailsFragment + appRoleDetailsFragment + GetQueryText("modelling/getDummyAppRole.graphql");
+ newAppRole = GetQueryText("modelling/newAppRole.graphql");
+ updateAppRole = GetQueryText("modelling/updateAppRole.graphql");
+ deleteNwGroup = GetQueryText("modelling/deleteNwGroup.graphql");
+ addNwObjectToNwGroup = GetQueryText("modelling/addNwObjectToNwGroup.graphql");
+ removeNwObjectFromNwGroup = GetQueryText("modelling/removeNwObjectFromNwGroup.graphql");
+ updateNwObjectInNwGroup = GetQueryText("modelling/updateNwObjectInNwGroup.graphql");
+
+ getServicesForApp = serviceDetailsFragment + GetQueryText("modelling/getServicesForApp.graphql");
+ getGlobalServices = serviceDetailsFragment + GetQueryText("modelling/getGlobalServices.graphql");
+ newService = GetQueryText("modelling/newService.graphql");
+ updateService = GetQueryText("modelling/updateService.graphql");
+ deleteService = GetQueryText("modelling/deleteService.graphql");
+
+ getServiceGroupsForApp = serviceDetailsFragment + serviceGroupDetailsFragment + GetQueryText("modelling/getServiceGroupsForApp.graphql");
+ getServiceGroupById = serviceDetailsFragment + serviceGroupDetailsFragment + GetQueryText("modelling/getServiceGroupById.graphql");
+ getGlobalServiceGroups = serviceDetailsFragment + serviceGroupDetailsFragment + GetQueryText("modelling/getGlobalServiceGroups.graphql");
+ newServiceGroup = GetQueryText("modelling/newServiceGroup.graphql");
+ updateServiceGroup = GetQueryText("modelling/updateServiceGroup.graphql");
+ deleteServiceGroup = GetQueryText("modelling/deleteServiceGroup.graphql");
+ addServiceToServiceGroup = GetQueryText("modelling/addServiceToServiceGroup.graphql");
+ removeServiceFromServiceGroup = GetQueryText("modelling/removeServiceFromServiceGroup.graphql");
+ getServiceGroupIdsForService = GetQueryText("modelling/getServiceGroupIdsForService.graphql");
+
+ getHistory = GetQueryText("modelling/getHistory.graphql");
+ getHistoryForApp = GetQueryText("modelling/getHistoryForApp.graphql");
+ addHistoryEntry = GetQueryText("modelling/addHistoryEntry.graphql");
+
+ newAppZone = GetQueryText("modelling/addNwAppZone.graphql");
+ getAppZonesByAppId = appServerDetailsFragment + GetQueryText("modelling/getAppZonesByAppId.graphql");
}
catch (Exception exception)
{
Log.WriteError("Initialize ModellingQueries", "Api ModellingQueries could not be loaded.", exception);
+#if RELEASE
Environment.Exit(-1);
+#else
+ throw;
+#endif
}
}
}
diff --git a/roles/lib/files/FWO.Api.Client/Queries/MonitorQueries.cs b/roles/lib/files/FWO.Api.Client/Queries/MonitorQueries.cs
index ddc2d72194..ea88b16c19 100644
--- a/roles/lib/files/FWO.Api.Client/Queries/MonitorQueries.cs
+++ b/roles/lib/files/FWO.Api.Client/Queries/MonitorQueries.cs
@@ -29,36 +29,40 @@ static MonitorQueries()
{
try
{
- addLogEntry = File.ReadAllText(QueryPath + "monitor/addLogEntry.graphql");
- getLogEntrys = File.ReadAllText(QueryPath + "monitor/getLogEntrys.graphql");
+ addLogEntry = GetQueryText("monitor/addLogEntry.graphql");
+ getLogEntrys = GetQueryText("monitor/getLogEntrys.graphql");
- addUiLogEntry = File.ReadAllText(QueryPath + "monitor/addUiLogEntry.graphql");
- getUiLogEntrys = File.ReadAllText(QueryPath + "monitor/getUiLogEntrys.graphql");
- getAllUiLogEntrys = File.ReadAllText(QueryPath + "monitor/getAllUiLogEntrys.graphql");
+ addUiLogEntry = GetQueryText("monitor/addUiLogEntry.graphql");
+ getUiLogEntrys = GetQueryText("monitor/getUiLogEntrys.graphql");
+ getAllUiLogEntrys = GetQueryText("monitor/getAllUiLogEntrys.graphql");
- getImportLogEntrys = File.ReadAllText(QueryPath + "monitor/getImportLogEntrys.graphql");
+ getImportLogEntrys = GetQueryText("monitor/getImportLogEntrys.graphql");
- addAlert = File.ReadAllText(QueryPath + "monitor/addAlert.graphql");
- getOpenAlerts = File.ReadAllText(QueryPath + "monitor/getOpenAlerts.graphql");
- getAlerts = File.ReadAllText(QueryPath + "monitor/getAlerts.graphql");
- getAlertById = File.ReadAllText(QueryPath + "monitor/getAlertById.graphql");
- acknowledgeAlert = File.ReadAllText(QueryPath + "monitor/acknowledgeAlert.graphql");
- subscribeAlertChanges = File.ReadAllText(QueryPath + "monitor/subscribeAlertChanges.graphql");
+ addAlert = GetQueryText("monitor/addAlert.graphql");
+ getOpenAlerts = GetQueryText("monitor/getOpenAlerts.graphql");
+ getAlerts = GetQueryText("monitor/getAlerts.graphql");
+ getAlertById = GetQueryText("monitor/getAlertById.graphql");
+ acknowledgeAlert = GetQueryText("monitor/acknowledgeAlert.graphql");
+ subscribeAlertChanges = GetQueryText("monitor/subscribeAlertChanges.graphql");
- getImportStatus = File.ReadAllText(QueryPath + "monitor/getImportStatus.graphql");
+ getImportStatus = GetQueryText("monitor/getImportStatus.graphql");
- addAutodiscoveryLogEntry = File.ReadAllText(QueryPath + "monitor/addAutodiscoveryLogEntry.graphql");
- getAutodiscoveryLogEntrys = File.ReadAllText(QueryPath + "monitor/getAutodiscoveryLogEntrys.graphql");
- getDailyCheckLogEntrys = File.ReadAllText(QueryPath + "monitor/getDailyCheckLogEntrys.graphql");
- addDataImportLogEntry = File.ReadAllText(QueryPath + "monitor/addDataImportLogEntry.graphql");
- getDataImportLogEntrys = File.ReadAllText(QueryPath + "monitor/getDataImportLogEntrys.graphql");
+ addAutodiscoveryLogEntry = GetQueryText("monitor/addAutodiscoveryLogEntry.graphql");
+ getAutodiscoveryLogEntrys = GetQueryText("monitor/getAutodiscoveryLogEntrys.graphql");
+ getDailyCheckLogEntrys = GetQueryText("monitor/getDailyCheckLogEntrys.graphql");
+ addDataImportLogEntry = GetQueryText("monitor/addDataImportLogEntry.graphql");
+ getDataImportLogEntrys = GetQueryText("monitor/getDataImportLogEntrys.graphql");
- getOwnerTickets = RequestQueries.ticketOverviewFragment + File.ReadAllText(QueryPath + "monitor/getOwnerTickets.graphql");
+ getOwnerTickets = RequestQueries.ticketOverviewFragment + GetQueryText("monitor/getOwnerTickets.graphql");
}
catch (Exception exception)
{
Log.WriteError("Initialize MonitorQueries", "Api MonitorQueries could not be loaded.", exception);
+#if RELEASE
Environment.Exit(-1);
+#else
+ throw;
+#endif
}
}
}
diff --git a/roles/lib/files/FWO.Api.Client/Queries/NetworkAnalysisQueries.cs b/roles/lib/files/FWO.Api.Client/Queries/NetworkAnalysisQueries.cs
index 2654c7822d..23aa8c041e 100644
--- a/roles/lib/files/FWO.Api.Client/Queries/NetworkAnalysisQueries.cs
+++ b/roles/lib/files/FWO.Api.Client/Queries/NetworkAnalysisQueries.cs
@@ -11,13 +11,17 @@ static NetworkAnalysisQueries()
try
{
pathAnalysis =
- File.ReadAllText(QueryPath + "networking/analyzePath.graphql");
+ GetQueryText("networking/analyzePath.graphql");
}
catch (Exception exception)
{
Log.WriteError("Initialize Api Queries", "Api Object Queries could not be loaded." , exception);
+#if RELEASE
Environment.Exit(-1);
+#else
+ throw;
+#endif
}
}
}
diff --git a/roles/lib/files/FWO.Api.Client/Queries/NotificationQueries.cs b/roles/lib/files/FWO.Api.Client/Queries/NotificationQueries.cs
index f6a64ead0d..a8e563bb39 100644
--- a/roles/lib/files/FWO.Api.Client/Queries/NotificationQueries.cs
+++ b/roles/lib/files/FWO.Api.Client/Queries/NotificationQueries.cs
@@ -24,7 +24,11 @@ static NotificationQueries()
catch (Exception exception)
{
Log.WriteError("Initialize NotificationQueries", "Api NotificationQueries could not be loaded.", exception);
+#if RELEASE
Environment.Exit(-1);
+#else
+ throw;
+#endif
}
}
}
diff --git a/roles/lib/files/FWO.Api.Client/Queries/ObjectQueries.cs b/roles/lib/files/FWO.Api.Client/Queries/ObjectQueries.cs
index c8d542bcab..6f05b36b5f 100644
--- a/roles/lib/files/FWO.Api.Client/Queries/ObjectQueries.cs
+++ b/roles/lib/files/FWO.Api.Client/Queries/ObjectQueries.cs
@@ -18,67 +18,71 @@ public class ObjectQueries : Queries
public static readonly string getReportFilteredNetworkServiceDetails;
public static readonly string getReportFilteredUserDetails;
- static ObjectQueries()
+ static ObjectQueries()
{
try
{
networkObjectDetailsFragment =
- File.ReadAllText(QueryPath + "networkObject/fragments/networkObjectDetails.graphql");
-
+ GetQueryText("networkObject/fragments/networkObjectDetails.graphql");
+
networkObjectDetailsForVarianceFragment =
- File.ReadAllText(QueryPath + "networkObject/fragments/networkObjectDetailsForVariance.graphql");
+ GetQueryText("networkObject/fragments/networkObjectDetailsForVariance.graphql");
getNetworkObjectDetails =
networkObjectDetailsFragment +
- File.ReadAllText(QueryPath + "networkObject/getNetworkObjectDetails.graphql");
+ GetQueryText("networkObject/getNetworkObjectDetails.graphql");
getNetworkObjectsForManagement =
networkObjectDetailsForVarianceFragment +
- File.ReadAllText(QueryPath + "networkObject/getNetworkObjectsForManagement.graphql");
+ GetQueryText("networkObject/getNetworkObjectsForManagement.graphql");
networkServiceDetailsFragment =
- File.ReadAllText(QueryPath + "networkService/fragments/networkServiceDetails.graphql");
+ GetQueryText("networkService/fragments/networkServiceDetails.graphql");
getNetworkServiceDetails =
networkServiceDetailsFragment +
- File.ReadAllText(QueryPath + "networkService/getNetworkServiceDetails.graphql");
+ GetQueryText("networkService/getNetworkServiceDetails.graphql");
- userDetailsFragment = File.ReadAllText(QueryPath + "user/fragments/userDetails.graphql");
+ userDetailsFragment = GetQueryText("user/fragments/userDetails.graphql");
getUserDetails =
userDetailsFragment +
- File.ReadAllText(QueryPath + "user/getUserDetails.graphql");
+ GetQueryText("user/getUserDetails.graphql");
// used for right side bar objects
getAllObjectDetails =
userDetailsFragment +
networkServiceDetailsFragment +
networkObjectDetailsFragment +
- File.ReadAllText(QueryPath + "allObjects/getAllObjectDetails.graphql");
+ GetQueryText("allObjects/getAllObjectDetails.graphql");
// for rule export and RSB obj filtering per report
- getReportFilteredObjectDetails =
+ getReportFilteredObjectDetails =
userDetailsFragment +
networkServiceDetailsFragment +
networkObjectDetailsFragment +
- File.ReadAllText(QueryPath + "report/getReportFilteredObjectDetails.graphql");
+ GetQueryText("report/getReportFilteredObjectDetails.graphql");
getReportFilteredNetworkObjectDetails =
networkObjectDetailsFragment +
- File.ReadAllText(QueryPath + "report/getReportFilteredNetworkObjectDetails.graphql");
+ GetQueryText("report/getReportFilteredNetworkObjectDetails.graphql");
getReportFilteredNetworkServiceDetails =
networkServiceDetailsFragment +
- File.ReadAllText(QueryPath + "report/getReportFilteredNetworkServiceDetails.graphql");
+ GetQueryText("report/getReportFilteredNetworkServiceDetails.graphql");
getReportFilteredUserDetails =
userDetailsFragment +
- File.ReadAllText(QueryPath + "report/getReportFilteredUserDetails.graphql");
+ GetQueryText("report/getReportFilteredUserDetails.graphql");
}
catch (Exception exception)
{
- Log.WriteError("Initialize Api Queries", "Api Object Queries could not be loaded." , exception);
+ Log.WriteError("Initialize Api Queries", "Api Object Queries could not be loaded.", exception);
+#if RELEASE
Environment.Exit(-1);
+#else
+ throw;
+#endif
}
}
}
diff --git a/roles/lib/files/FWO.Api.Client/Queries/OwnerQueries.cs b/roles/lib/files/FWO.Api.Client/Queries/OwnerQueries.cs
index 3f03e76e9b..affc68f7d3 100644
--- a/roles/lib/files/FWO.Api.Client/Queries/OwnerQueries.cs
+++ b/roles/lib/files/FWO.Api.Client/Queries/OwnerQueries.cs
@@ -37,38 +37,42 @@ static OwnerQueries()
{
try
{
- ownerDetailsFragment = File.ReadAllText(QueryPath + "owner/fragments/ownerDetails.graphql");
+ ownerDetailsFragment = GetQueryText("owner/fragments/ownerDetails.graphql");
- getOwners = ownerDetailsFragment + File.ReadAllText(QueryPath + "owner/getOwners.graphql");
- getOwnersWithConn = ownerDetailsFragment + File.ReadAllText(QueryPath + "owner/getOwnersWithConn.graphql");
- getEditableOwners = ownerDetailsFragment + File.ReadAllText(QueryPath + "owner/getEditableOwners.graphql");
- getEditableOwnersWithConn = ownerDetailsFragment + File.ReadAllText(QueryPath + "owner/getEditableOwnersWithConn.graphql");
- newOwner = File.ReadAllText(QueryPath + "owner/newOwner.graphql");
- newLifeCycle = File.ReadAllText(QueryPath + "owner/newLifeCycle.graphql");
- updateOwner = File.ReadAllText(QueryPath + "owner/updateOwner.graphql");
- updateLifeCycle = File.ReadAllText(QueryPath + "owner/updateLifeCycle.graphql");
- deactivateOwner = File.ReadAllText(QueryPath + "owner/deactivateOwner.graphql");
- deleteOwner = File.ReadAllText(QueryPath + "owner/deleteOwner.graphql");
- deleteLifeCycle = File.ReadAllText(QueryPath + "owner/deleteLifeCycle.graphql");
- getOwnerLifeCycleStates = File.ReadAllText(QueryPath + "owner/getOwnerLifeCycleStates.graphql");
- //setDefaultOwner = File.ReadAllText(QueryPath + "owner/setDefaultOwner.graphql");
- setOwnerLastCheck = File.ReadAllText(QueryPath + "owner/setOwnerLastCheck.graphql");
- setOwnerLastRecert = File.ReadAllText(QueryPath + "owner/setOwnerLastRecert.graphql");
- getOwnersFromGroups = ownerDetailsFragment + File.ReadAllText(QueryPath + "owner/getOwnersFromGroups.graphql");
- getOwnersForUser = ownerDetailsFragment + File.ReadAllText(QueryPath + "owner/getOwnersForUser.graphql");
- getNetworkOwnerships = ownerDetailsFragment + File.ReadAllText(QueryPath + "owner/getNetworkOwnerships.graphql");
- newNetworkOwnership = ownerDetailsFragment + File.ReadAllText(QueryPath + "owner/newNetworkOwnership.graphql");
- deleteNetworkOwnership = ownerDetailsFragment + File.ReadAllText(QueryPath + "owner/deleteNetworkOwnership.graphql");
- deleteAreaIpData = File.ReadAllText(QueryPath + "owner/deleteAreaIpData.graphql");
- getRuleOwnerships = ownerDetailsFragment + File.ReadAllText(QueryPath + "owner/getRuleOwnerships.graphql");
- newRuleOwnership = ownerDetailsFragment + File.ReadAllText(QueryPath + "owner/newRuleOwnership.graphql");
- deleteRuleOwnership = ownerDetailsFragment + File.ReadAllText(QueryPath + "owner/deleteRuleOwnership.graphql");
- getOwnerId = File.ReadAllText(QueryPath + "/owner/getOwnerId.graphql");
+ getOwners = ownerDetailsFragment + GetQueryText("owner/getOwners.graphql");
+ getOwnersWithConn = ownerDetailsFragment + GetQueryText("owner/getOwnersWithConn.graphql");
+ getEditableOwners = ownerDetailsFragment + GetQueryText("owner/getEditableOwners.graphql");
+ getEditableOwnersWithConn = ownerDetailsFragment + GetQueryText("owner/getEditableOwnersWithConn.graphql");
+ newOwner = GetQueryText("owner/newOwner.graphql");
+ newLifeCycle = GetQueryText("owner/newLifeCycle.graphql");
+ updateOwner = GetQueryText("owner/updateOwner.graphql");
+ updateLifeCycle = GetQueryText("owner/updateLifeCycle.graphql");
+ deactivateOwner = GetQueryText("owner/deactivateOwner.graphql");
+ deleteOwner = GetQueryText("owner/deleteOwner.graphql");
+ deleteLifeCycle = GetQueryText("owner/deleteLifeCycle.graphql");
+ getOwnerLifeCycleStates = GetQueryText("owner/getOwnerLifeCycleStates.graphql");
+ //setDefaultOwner = GetQueryText("owner/setDefaultOwner.graphql");
+ setOwnerLastCheck = GetQueryText("owner/setOwnerLastCheck.graphql");
+ setOwnerLastRecert = GetQueryText("owner/setOwnerLastRecert.graphql");
+ getOwnersFromGroups = ownerDetailsFragment + GetQueryText("owner/getOwnersFromGroups.graphql");
+ getOwnersForUser = ownerDetailsFragment + GetQueryText("owner/getOwnersForUser.graphql");
+ getNetworkOwnerships = ownerDetailsFragment + GetQueryText("owner/getNetworkOwnerships.graphql");
+ newNetworkOwnership = ownerDetailsFragment + GetQueryText("owner/newNetworkOwnership.graphql");
+ deleteNetworkOwnership = ownerDetailsFragment + GetQueryText("owner/deleteNetworkOwnership.graphql");
+ deleteAreaIpData = GetQueryText("owner/deleteAreaIpData.graphql");
+ getRuleOwnerships = ownerDetailsFragment + GetQueryText("owner/getRuleOwnerships.graphql");
+ newRuleOwnership = ownerDetailsFragment + GetQueryText("owner/newRuleOwnership.graphql");
+ deleteRuleOwnership = ownerDetailsFragment + GetQueryText("owner/deleteRuleOwnership.graphql");
+ getOwnerId = GetQueryText("/owner/getOwnerId.graphql");
}
catch (Exception exception)
{
Log.WriteError("Initialize OwnerQueries", "Api OwnerQueries could not be loaded.", exception);
+#if RELEASE
Environment.Exit(-1);
+#else
+ throw;
+#endif
}
}
}
diff --git a/roles/lib/files/FWO.Api.Client/Queries/Queries.cs b/roles/lib/files/FWO.Api.Client/Queries/Queries.cs
index a935237d78..20e307aef6 100644
--- a/roles/lib/files/FWO.Api.Client/Queries/Queries.cs
+++ b/roles/lib/files/FWO.Api.Client/Queries/Queries.cs
@@ -1,23 +1,43 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
-using FWO.Config;
using System.Text.RegularExpressions;
+using FWO.Basics;
namespace FWO.Api.Client.Queries
{
public class Queries
{
- protected static readonly string QueryPath = AppDomain.CurrentDomain.BaseDirectory + "../../../../../../lib/files/FWO.Api.Client/APIcalls/";
- public static string compact(string raw_query)
- { // return Regex.Replace(input, @"[^\w\.\*\-\:\?@/\(\)\[\]\{\}\$\+<>#\$ ]", "").Trim();
- raw_query = Regex.Replace(raw_query, @"\\t+", @"\s").Trim(); // replace tabs with a single space
- raw_query = Regex.Replace(raw_query, @"\\s+", @"\s"); // replace multiple space chars by a single one
- raw_query = Regex.Replace(raw_query, @"[\n]", ""); // remove EOL chars
+ protected static readonly string QueryPath = GlobalConst.kFwoBaseDir + "/fwo-api-calls/";
+
+ protected static string GetQueryText(string relativeQueryFileName)
+ {
+ return Compact(" " + File.ReadAllText(QueryPath + relativeQueryFileName) + " ");
+ }
+ public static string Compact(string raw_query)
+ {
+ // Split the input into lines
+ var lines = raw_query.Split(new[] { '\n' }, StringSplitOptions.None);
+
+ // Remove comments and process each line
+ for (int i = 0; i < lines.Length; i++)
+ {
+ // Remove everything starting from '#' to the end of the line
+ lines[i] = Regex.Replace(lines[i], @"#.*", "", RegexOptions.None, TimeSpan.FromMilliseconds(100)).Trim();
+ }
+
+ // Rejoin the lines back into a single string
+ raw_query = string.Join("\n", lines);
+
+ // Replace tabs with a single space
+ raw_query = Regex.Replace(raw_query, @"\t+", " ", RegexOptions.None, TimeSpan.FromMilliseconds(100)).Trim();
+
+ // Replace multiple spaces with a single space
+ raw_query = Regex.Replace(raw_query, @"\s+", " ", RegexOptions.None, TimeSpan.FromMilliseconds(100));
+
+ // Remove remaining newline characters (if needed)
+ raw_query = Regex.Replace(raw_query, @"[\n]", "", RegexOptions.None, TimeSpan.FromMilliseconds(100));
+
return raw_query;
}
+
}
}
diff --git a/roles/lib/files/FWO.Api.Client/Queries/RecertQueries.cs b/roles/lib/files/FWO.Api.Client/Queries/RecertQueries.cs
index 4e04043818..cf7263f77c 100644
--- a/roles/lib/files/FWO.Api.Client/Queries/RecertQueries.cs
+++ b/roles/lib/files/FWO.Api.Client/Queries/RecertQueries.cs
@@ -26,29 +26,33 @@ static RecertQueries()
try
{
ruleOverviewFragments =
- File.ReadAllText(QueryPath + "networkObject/fragments/networkObjectOverview.graphql") +
- File.ReadAllText(QueryPath + "networkService/fragments/networkServiceOverview.graphql") +
- File.ReadAllText(QueryPath + "user/fragments/userOverview.graphql") +
- File.ReadAllText(QueryPath + "rule/fragments/ruleOverview.graphql");
- ruleOpenRecertFragments = ruleOverviewFragments + File.ReadAllText(QueryPath + "recertification/fragments/ruleOpenCertOverview.graphql");
-
- prepareNextRecertification = File.ReadAllText(QueryPath + "recertification/prepareNextRecertification.graphql");
- recertify = File.ReadAllText(QueryPath + "recertification/recertify.graphql");
- recertifyOwner = File.ReadAllText(QueryPath + "recertification/recertifyOwner.graphql");
- recertifyRuleDirectly = File.ReadAllText(QueryPath + "recertification/recertifyRuleDirectly.graphql");
- getOpenRecertsForRule = File.ReadAllText(QueryPath + "recertification/getOpenRecertsForRule.graphql");
- getOpenRecertsForOwners = File.ReadAllText(QueryPath + "recertification/getOpenRecertsForOwners.graphql");
- clearOpenRecerts = File.ReadAllText(QueryPath + "recertification/clearOpenRecerts.graphql");
- addRecertEntries = File.ReadAllText(QueryPath + "recertification/addRecertEntries.graphql");
- refreshViewRuleWithOwner = File.ReadAllText(QueryPath + "recertification/refreshViewRuleWithOwner.graphql");
- getOwnerRecerts = File.ReadAllText(QueryPath + "recertification/getOwnerRecerts.graphql");
- getInitialOwnerRecert = File.ReadAllText(QueryPath + "recertification/getInitialOwnerRecert.graphql");
- updateRecertReportId = File.ReadAllText(QueryPath + "recertification/updateRecertReportId.graphql");
+ GetQueryText("networkObject/fragments/networkObjectOverview.graphql") +
+ GetQueryText("networkService/fragments/networkServiceOverview.graphql") +
+ GetQueryText("user/fragments/userOverview.graphql") +
+ GetQueryText("rule/fragments/ruleOverview.graphql") +
+ GetQueryText("rule/fragments/rulebaseOverview.graphql");
+ ruleOpenRecertFragments = ruleOverviewFragments + GetQueryText("recertification/fragments/ruleOpenCertOverview.graphql");
+ prepareNextRecertification = GetQueryText("recertification/prepareNextRecertification.graphql");
+ recertify = GetQueryText("recertification/recertify.graphql");
+ recertifyOwner = GetQueryText("recertification/recertifyOwner.graphql");
+ recertifyRuleDirectly = GetQueryText("recertification/recertifyRuleDirectly.graphql");
+ getOpenRecertsForRule = GetQueryText("recertification/getOpenRecertsForRule.graphql");
+ getOpenRecertsForOwners = GetQueryText("recertification/getOpenRecertsForOwners.graphql");
+ clearOpenRecerts = GetQueryText("recertification/clearOpenRecerts.graphql");
+ addRecertEntries = GetQueryText("recertification/addRecertEntries.graphql");
+ refreshViewRuleWithOwner = GetQueryText("recertification/refreshViewRuleWithOwner.graphql");
+ getOwnerRecerts = GetQueryText("recertification/getOwnerRecerts.graphql");
+ getInitialOwnerRecert = GetQueryText("recertification/getInitialOwnerRecert.graphql");
+ updateRecertReportId = GetQueryText("recertification/updateRecertReportId.graphql");
}
catch (Exception exception)
{
Log.WriteError("Initialize Api Queries", "Api Recert Queries could not be loaded.", exception);
+#if RELEASE
Environment.Exit(-1);
+#else
+ throw;
+#endif
}
}
}
diff --git a/roles/lib/files/FWO.Api.Client/Queries/ReportQueries.cs b/roles/lib/files/FWO.Api.Client/Queries/ReportQueries.cs
index ac19afa531..ecded13ab1 100644
--- a/roles/lib/files/FWO.Api.Client/Queries/ReportQueries.cs
+++ b/roles/lib/files/FWO.Api.Client/Queries/ReportQueries.cs
@@ -34,42 +34,50 @@ public class ReportQueries : Queries
public static readonly string getImportsToNotify;
public static readonly string setImportsNotified;
- static ReportQueries()
+ public static readonly string getManagementForNormalizedConfig;
+ public static readonly string getManagementForLatestNormalizedConfig;
+
+ static ReportQueries()
{
try
{
- addReportTemplate = File.ReadAllText(QueryPath + "report/addReportTemplate.graphql");
- addReportSchedule = File.ReadAllText(QueryPath + "report/addReportSchedule.graphql");
- addReportScheduleFileFormats = File.ReadAllText(QueryPath + "report/addReportScheduleFileFormats.graphql");
- editReportSchedule = File.ReadAllText(QueryPath + "report/editReportSchedule.graphql");
- deleteReportSchedule = File.ReadAllText(QueryPath + "report/deleteReportSchedule.graphql");
- getReportSchedules = File.ReadAllText(QueryPath + "report/getReportSchedules.graphql");
- countReportSchedule = File.ReadAllText(QueryPath + "report/countReportSchedule.graphql");
- getReportsOverview = File.ReadAllText(QueryPath + "report/getReportsOverview.graphql");
- getReportsById = File.ReadAllText(QueryPath + "report/getReportById.graphql");
- getReportTemplates = File.ReadAllText(QueryPath + "report/getReportTemplates.graphql");
- getRelevantImportIdsAtTime = File.ReadAllText(QueryPath + "report/getRelevantImportIdsAtTime.graphql");
- getRelevantImportIdsInTimeRange = File.ReadAllText(QueryPath + "report/getRelevantImportIdsInTimeRange.graphql");
- statisticsReportCurrent = File.ReadAllText(QueryPath + "report/statisticsCurrent.graphql");
- statisticsReportCurrent = File.ReadAllText(QueryPath + "report/statisticsCurrentOverall.graphql");
- updateReportTemplate = File.ReadAllText(QueryPath + "report/updateReportTemplate.graphql");
- deleteReportTemplate = File.ReadAllText(QueryPath + "report/deleteReportTemplate.graphql");
- subscribeReportScheduleChanges = File.ReadAllText(QueryPath + "report/subscribeReportScheduleChanges.graphql");
- subscribeGeneratedReportsChanges = File.ReadAllText(QueryPath + "report/subscribeGeneratedReportsChanges.graphql");
- getGeneratedReports = File.ReadAllText(QueryPath + "report/getGeneratedReports.graphql");
- getGeneratedReport = File.ReadAllText(QueryPath + "report/getGeneratedReport.graphql");
- deleteGeneratedReport = File.ReadAllText(QueryPath + "report/deleteGeneratedReport.graphql");
- addGeneratedReport = File.ReadAllText(QueryPath + "report/addGeneratedReport.graphql");
- getUsageDataCount = File.ReadAllText(QueryPath + "report/getUsageDataCount.graphql");
+ addReportTemplate = GetQueryText("report/addReportTemplate.graphql");
+ addReportSchedule = GetQueryText("report/addReportSchedule.graphql");
+ addReportScheduleFileFormats = GetQueryText("report/addReportScheduleFileFormats.graphql");
+ editReportSchedule = GetQueryText("report/editReportSchedule.graphql");
+ deleteReportSchedule = GetQueryText("report/deleteReportSchedule.graphql");
+ getReportSchedules = GetQueryText("report/getReportSchedules.graphql");
+ countReportSchedule = GetQueryText("report/countReportSchedule.graphql");
+ getReportsOverview = GetQueryText("report/getReportsOverview.graphql");
+ getReportsById = GetQueryText("report/getReportById.graphql");
+ getReportTemplates = GetQueryText("report/getReportTemplates.graphql");
+ getRelevantImportIdsAtTime = GetQueryText("report/getRelevantImportIdsAtTime.graphql");
+ getRelevantImportIdsInTimeRange = GetQueryText("report/getRelevantImportIdsInTimeRange.graphql");
+ statisticsReportCurrent = GetQueryText("report/statisticsCurrent.graphql");
+ statisticsReportCurrent = GetQueryText("report/statisticsCurrentOverall.graphql");
+ updateReportTemplate = GetQueryText("report/updateReportTemplate.graphql");
+ deleteReportTemplate = GetQueryText("report/deleteReportTemplate.graphql");
+ subscribeReportScheduleChanges = GetQueryText("report/subscribeReportScheduleChanges.graphql");
+ subscribeGeneratedReportsChanges = GetQueryText("report/subscribeGeneratedReportsChanges.graphql");
+ getGeneratedReports = GetQueryText("report/getGeneratedReports.graphql");
+ getGeneratedReport = GetQueryText("report/getGeneratedReport.graphql");
+ deleteGeneratedReport = GetQueryText("report/deleteGeneratedReport.graphql");
+ addGeneratedReport = GetQueryText("report/addGeneratedReport.graphql");
+ getUsageDataCount = GetQueryText("report/getUsageDataCount.graphql");
// note: currently we only check for rule changes, but this should be extended to other changes in the future
- // getImportsToNotify = File.ReadAllText(QueryPath + "report/getImportsToNotifyForAnyChanges.phql");
- getImportsToNotify = File.ReadAllText(QueryPath + "report/getImportsToNotifyForRuleChanges.graphql");
- setImportsNotified = File.ReadAllText(QueryPath + "report/setImportsNotified.graphql");
+ getImportsToNotify = GetQueryText("report/getImportsToNotifyForRuleChanges.graphql");
+ setImportsNotified = GetQueryText("report/setImportsNotified.graphql");
+ getManagementForNormalizedConfig = GetQueryText("report/getManagementForNormalizedConfig.graphql");
+ getManagementForLatestNormalizedConfig = GetQueryText("report/getManagementForLatestNormalizedConfig.graphql");
}
catch (Exception exception)
{
Log.WriteError("Initialize Api Queries", "Api ReportQueries could not be loaded.", exception);
+#if RELEASE
Environment.Exit(-1);
+#else
+ throw;
+#endif
}
}
}
diff --git a/roles/lib/files/FWO.Api.Client/Queries/RequestQueries.cs b/roles/lib/files/FWO.Api.Client/Queries/RequestQueries.cs
index 5caad36af6..7aad07648e 100644
--- a/roles/lib/files/FWO.Api.Client/Queries/RequestQueries.cs
+++ b/roles/lib/files/FWO.Api.Client/Queries/RequestQueries.cs
@@ -64,65 +64,69 @@ static RequestQueries()
{
try
{
- commentDetailsFragment = File.ReadAllText(QueryPath + "request/fragments/commentDetails.graphql");
- implTaskDetailsFragment = commentDetailsFragment + File.ReadAllText(QueryPath + "request/fragments/implTaskDetails.graphql");
- reqElementDetailsFragment = File.ReadAllText(QueryPath + "request/fragments/reqElementDetails.graphql");
- reqTaskDetailsFragment = OwnerQueries.ownerDetailsFragment + reqElementDetailsFragment + implTaskDetailsFragment + File.ReadAllText(QueryPath + "request/fragments/reqTaskDetails.graphql");
- ticketDetailsFragment = reqTaskDetailsFragment + File.ReadAllText(QueryPath + "request/fragments/ticketDetails.graphql");
- reqTaskOverviewFragment = OwnerQueries.ownerDetailsFragment + File.ReadAllText(QueryPath + "request/fragments/reqTaskOverview.graphql");
- ticketOverviewFragment = reqTaskOverviewFragment + File.ReadAllText(QueryPath + "request/fragments/ticketOverview.graphql");
- ticketDetailsReqTaskOverviewFragment = commentDetailsFragment + reqTaskOverviewFragment + File.ReadAllText(QueryPath + "request/fragments/ticketDetailsReqTaskOverview.graphql");
+ commentDetailsFragment = GetQueryText("request/fragments/commentDetails.graphql");
+ implTaskDetailsFragment = commentDetailsFragment + GetQueryText("request/fragments/implTaskDetails.graphql");
+ reqElementDetailsFragment = GetQueryText("request/fragments/reqElementDetails.graphql");
+ reqTaskDetailsFragment = OwnerQueries.ownerDetailsFragment + reqElementDetailsFragment + implTaskDetailsFragment + GetQueryText("request/fragments/reqTaskDetails.graphql");
+ ticketDetailsFragment = reqTaskDetailsFragment + GetQueryText("request/fragments/ticketDetails.graphql");
+ reqTaskOverviewFragment = OwnerQueries.ownerDetailsFragment + GetQueryText("request/fragments/reqTaskOverview.graphql");
+ ticketOverviewFragment = reqTaskOverviewFragment + GetQueryText("request/fragments/ticketOverview.graphql");
+ ticketDetailsReqTaskOverviewFragment = commentDetailsFragment + reqTaskOverviewFragment + GetQueryText("request/fragments/ticketDetailsReqTaskOverview.graphql");
- getTickets = ticketDetailsReqTaskOverviewFragment + File.ReadAllText(QueryPath + "request/getTickets.graphql");
- getFullTickets = ticketDetailsFragment + File.ReadAllText(QueryPath + "request/getFullTickets.graphql");
- getOwnerTicketIds = File.ReadAllText(QueryPath + "monitor/getOwnerTicketIds.graphql");
- getTicketById = ticketDetailsFragment + File.ReadAllText(QueryPath + "request/getTicketById.graphql");
- newTicket = File.ReadAllText(QueryPath + "request/newTicket.graphql");
- updateTicket = File.ReadAllText(QueryPath + "request/updateTicket.graphql");
- updateTicketState = File.ReadAllText(QueryPath + "request/updateTicketState.graphql");
- subscribeTicketStateChanges = File.ReadAllText(QueryPath + "request/subscribeTicketStateChanges.graphql");
- subscribeTaskChanges = reqElementDetailsFragment + File.ReadAllText(QueryPath + "request/subscribeTaskChanges.graphql");
- newRequestTask = File.ReadAllText(QueryPath + "request/newRequestTask.graphql");
- updateRequestTask = File.ReadAllText(QueryPath + "request/updateRequestTask.graphql");
- updateRequestTaskState = File.ReadAllText(QueryPath + "request/updateRequestTaskState.graphql");
- updateRequestTaskAdditionalInfo = File.ReadAllText(QueryPath + "request/updateRequestTaskAdditionalInfo.graphql");
- deleteRequestTask = File.ReadAllText(QueryPath + "request/deleteRequestTask.graphql");
- newRequestElement = File.ReadAllText(QueryPath + "request/newRequestElement.graphql");
- updateRequestElement = File.ReadAllText(QueryPath + "request/updateRequestElement.graphql");
- deleteRequestElement = File.ReadAllText(QueryPath + "request/deleteRequestElement.graphql");
- newImplementationTask = File.ReadAllText(QueryPath + "request/newImplementationTask.graphql");
- updateImplementationTask = File.ReadAllText(QueryPath + "request/updateImplementationTask.graphql");
- updateImplementationTaskState = File.ReadAllText(QueryPath + "request/updateImplementationTaskState.graphql");
- deleteImplementationTask = File.ReadAllText(QueryPath + "request/deleteImplementationTask.graphql");
- newImplementationElement = File.ReadAllText(QueryPath + "request/newImplementationElement.graphql");
- updateImplementationElement = File.ReadAllText(QueryPath + "request/updateImplementationElement.graphql");
- deleteImplementationElement = File.ReadAllText(QueryPath + "request/deleteImplementationElement.graphql");
- newApproval = File.ReadAllText(QueryPath + "request/newApproval.graphql");
- updateApproval = File.ReadAllText(QueryPath + "request/updateApproval.graphql");
- getStates = File.ReadAllText(QueryPath + "request/getStates.graphql");
- upsertState = File.ReadAllText(QueryPath + "request/upsertState.graphql");
- deleteState = File.ReadAllText(QueryPath + "request/deleteState.graphql");
- getExtStates = File.ReadAllText(QueryPath + "request/getExtStates.graphql");
- addExtState = File.ReadAllText(QueryPath + "request/addExtState.graphql");
- removeExtState = File.ReadAllText(QueryPath + "request/removeExtState.graphql");
- getActions = File.ReadAllText(QueryPath + "request/getActions.graphql");
- newAction = File.ReadAllText(QueryPath + "request/newAction.graphql");
- updateAction = File.ReadAllText(QueryPath + "request/updateAction.graphql");
- deleteAction = File.ReadAllText(QueryPath + "request/deleteAction.graphql");
- addStateAction = File.ReadAllText(QueryPath + "request/addStateAction.graphql");
- removeStateAction = File.ReadAllText(QueryPath + "request/removeStateAction.graphql");
- newComment = File.ReadAllText(QueryPath + "request/newComment.graphql");
- addCommentToReqTask = File.ReadAllText(QueryPath + "request/addCommentToReqTask.graphql");
- addCommentToImplTask = File.ReadAllText(QueryPath + "request/addCommentToImplTask.graphql");
- addCommentToTicket = File.ReadAllText(QueryPath + "request/addCommentToTicket.graphql");
- addCommentToApproval = File.ReadAllText(QueryPath + "request/addCommentToApproval.graphql");
- addOwnerToReqTask = File.ReadAllText(QueryPath + "request/addOwnerToReqTask.graphql");
- removeOwnerFromReqTask = File.ReadAllText(QueryPath + "request/removeOwnerFromReqTask.graphql");
+ getTickets = ticketDetailsReqTaskOverviewFragment + GetQueryText("request/getTickets.graphql");
+ getFullTickets = ticketDetailsFragment + GetQueryText("request/getFullTickets.graphql");
+ getOwnerTicketIds = GetQueryText("monitor/getOwnerTicketIds.graphql");
+ getTicketById = ticketDetailsFragment + GetQueryText("request/getTicketById.graphql");
+ newTicket = GetQueryText("request/newTicket.graphql");
+ updateTicket = GetQueryText("request/updateTicket.graphql");
+ updateTicketState = GetQueryText("request/updateTicketState.graphql");
+ subscribeTicketStateChanges = GetQueryText("request/subscribeTicketStateChanges.graphql");
+ subscribeTaskChanges = reqElementDetailsFragment + GetQueryText("request/subscribeTaskChanges.graphql");
+ newRequestTask = GetQueryText("request/newRequestTask.graphql");
+ updateRequestTask = GetQueryText("request/updateRequestTask.graphql");
+ updateRequestTaskState = GetQueryText("request/updateRequestTaskState.graphql");
+ updateRequestTaskAdditionalInfo = GetQueryText("request/updateRequestTaskAdditionalInfo.graphql");
+ deleteRequestTask = GetQueryText("request/deleteRequestTask.graphql");
+ newRequestElement = GetQueryText("request/newRequestElement.graphql");
+ updateRequestElement = GetQueryText("request/updateRequestElement.graphql");
+ deleteRequestElement = GetQueryText("request/deleteRequestElement.graphql");
+ newImplementationTask = GetQueryText("request/newImplementationTask.graphql");
+ updateImplementationTask = GetQueryText("request/updateImplementationTask.graphql");
+ updateImplementationTaskState = GetQueryText("request/updateImplementationTaskState.graphql");
+ deleteImplementationTask = GetQueryText("request/deleteImplementationTask.graphql");
+ newImplementationElement = GetQueryText("request/newImplementationElement.graphql");
+ updateImplementationElement = GetQueryText("request/updateImplementationElement.graphql");
+ deleteImplementationElement = GetQueryText("request/deleteImplementationElement.graphql");
+ newApproval = GetQueryText("request/newApproval.graphql");
+ updateApproval = GetQueryText("request/updateApproval.graphql");
+ getStates = GetQueryText("request/getStates.graphql");
+ upsertState = GetQueryText("request/upsertState.graphql");
+ deleteState = GetQueryText("request/deleteState.graphql");
+ getExtStates = GetQueryText("request/getExtStates.graphql");
+ addExtState = GetQueryText("request/addExtState.graphql");
+ removeExtState = GetQueryText("request/removeExtState.graphql");
+ getActions = GetQueryText("request/getActions.graphql");
+ newAction = GetQueryText("request/newAction.graphql");
+ updateAction = GetQueryText("request/updateAction.graphql");
+ deleteAction = GetQueryText("request/deleteAction.graphql");
+ addStateAction = GetQueryText("request/addStateAction.graphql");
+ removeStateAction = GetQueryText("request/removeStateAction.graphql");
+ newComment = GetQueryText("request/newComment.graphql");
+ addCommentToReqTask = GetQueryText("request/addCommentToReqTask.graphql");
+ addCommentToImplTask = GetQueryText("request/addCommentToImplTask.graphql");
+ addCommentToTicket = GetQueryText("request/addCommentToTicket.graphql");
+ addCommentToApproval = GetQueryText("request/addCommentToApproval.graphql");
+ addOwnerToReqTask = GetQueryText("request/addOwnerToReqTask.graphql");
+ removeOwnerFromReqTask = GetQueryText("request/removeOwnerFromReqTask.graphql");
}
catch (Exception exception)
{
Log.WriteError("Initialize RequestQueries", "Api RequestQueries could not be loaded.", exception);
+#if RELEASE
Environment.Exit(-1);
+#else
+ throw;
+#endif
}
}
}
diff --git a/roles/lib/files/FWO.Api.Client/Queries/RuleQueries.cs b/roles/lib/files/FWO.Api.Client/Queries/RuleQueries.cs
index 471474fbf9..386c61f3ea 100644
--- a/roles/lib/files/FWO.Api.Client/Queries/RuleQueries.cs
+++ b/roles/lib/files/FWO.Api.Client/Queries/RuleQueries.cs
@@ -1,4 +1,4 @@
-using FWO.Logging;
+using FWO.Logging;
namespace FWO.Api.Client.Queries
{
@@ -27,67 +27,80 @@ public class RuleQueries : Queries
public static readonly string getNatRuleDetails;
// public static readonly string getNatRuleDetailsForReport;
-
+ public static readonly string countRules;
+ public static readonly string countActiveRules;
+ public static readonly string getRulesWithViolationsInTimespanByChunk;
+ public static readonly string getRulesWithCurrentViolationsByChunk;
+ public static readonly string getRulesForSelectedManagements;
+
+
static RuleQueries()
{
try
{
ruleOverviewFragments =
- File.ReadAllText(QueryPath + "networkObject/fragments/networkObjectOverview.graphql") +
- File.ReadAllText(QueryPath + "networkService/fragments/networkServiceOverview.graphql") +
- File.ReadAllText(QueryPath + "user/fragments/userOverview.graphql") +
- File.ReadAllText(QueryPath + "rule/fragments/ruleOverview.graphql");
+ GetQueryText("networkObject/fragments/networkObjectOverview.graphql") +
+ GetQueryText("networkService/fragments/networkServiceOverview.graphql") +
+ GetQueryText("user/fragments/userOverview.graphql") +
+ GetQueryText("rule/fragments/ruleOverview.graphql");
ruleOverviewForChangeReportFragments =
- File.ReadAllText(QueryPath + "networkObject/fragments/networkObjectOverview.graphql") +
- File.ReadAllText(QueryPath + "networkService/fragments/networkServiceOverview.graphql") +
- File.ReadAllText(QueryPath + "user/fragments/userOverview.graphql") +
- File.ReadAllText(QueryPath + "rule/fragments/ruleOverviewChangesOld.graphql") +
- File.ReadAllText(QueryPath + "rule/fragments/ruleOverviewChangesNew.graphql");
+ GetQueryText("networkObject/fragments/networkObjectOverview.graphql") +
+ GetQueryText("networkService/fragments/networkServiceOverview.graphql") +
+ GetQueryText("user/fragments/userOverview.graphql") +
+ GetQueryText("rule/fragments/ruleOverviewChangesOld.graphql") +
+ GetQueryText("rule/fragments/ruleOverviewChangesNew.graphql");
ruleDetailsFragments =
ObjectQueries.networkObjectDetailsFragment +
ObjectQueries.networkServiceDetailsFragment +
ObjectQueries.userDetailsFragment +
- File.ReadAllText(QueryPath + "rule/fragments/ruleDetails.graphql");
+ GetQueryText("rule/fragments/ruleDetails.graphql");
ruleDetailsForReportFragments =
ObjectQueries.networkObjectDetailsFragment +
ObjectQueries.networkServiceDetailsFragment +
ObjectQueries.userDetailsFragment +
- File.ReadAllText(QueryPath + "rule/fragments/ruleDetailsForReport.graphql");
- natRuleOverviewFragments = ruleOverviewFragments + File.ReadAllText(QueryPath + "rule/fragments/natRuleOverview.graphql");
- natRuleDetailsFragments = ruleDetailsFragments + File.ReadAllText(QueryPath + "rule/fragments/natRuleDetails.graphql");
+ GetQueryText("rule/fragments/ruleDetailsForReport.graphql");
+ natRuleOverviewFragments = ruleOverviewFragments + GetQueryText("rule/fragments/natRuleOverview.graphql");
+ natRuleDetailsFragments = ruleDetailsFragments + GetQueryText("rule/fragments/natRuleDetails.graphql");
natRuleDetailsForReportFragments =
ObjectQueries.networkObjectDetailsFragment +
ObjectQueries.networkServiceDetailsFragment +
ObjectQueries.userDetailsFragment +
- File.ReadAllText(QueryPath + "rule/fragments/natRuleDetailsForReport.graphql");
+ GetQueryText("rule/fragments/natRuleDetailsForReport.graphql");
ruleDetailsForChangeReportFragments =
- File.ReadAllText(QueryPath + "networkObject/fragments/networkObjectDetailsChangesOld.graphql") +
- File.ReadAllText(QueryPath + "networkObject/fragments/networkObjectDetailsChangesNew.graphql") +
- File.ReadAllText(QueryPath + "networkService/fragments/networkServiceDetailsChangesOld.graphql") +
- File.ReadAllText(QueryPath + "networkService/fragments/networkServiceDetailsChangesNew.graphql") +
- File.ReadAllText(QueryPath + "user/fragments/userDetailsChangesOld.graphql") +
- File.ReadAllText(QueryPath + "user/fragments/userDetailsChangesNew.graphql") +
- File.ReadAllText(QueryPath + "rule/fragments/ruleDetailsChangesOld.graphql") +
- File.ReadAllText(QueryPath + "rule/fragments/ruleDetailsChangesNew.graphql");
+ GetQueryText("networkObject/fragments/networkObjectDetailsChangesOld.graphql") +
+ GetQueryText("networkObject/fragments/networkObjectDetailsChangesNew.graphql") +
+ GetQueryText("networkService/fragments/networkServiceDetailsChangesOld.graphql") +
+ GetQueryText("networkService/fragments/networkServiceDetailsChangesNew.graphql") +
+ GetQueryText("user/fragments/userDetailsChangesOld.graphql") +
+ GetQueryText("user/fragments/userDetailsChangesNew.graphql") +
+ GetQueryText("rule/fragments/ruleDetailsChangesOld.graphql") +
+ GetQueryText("rule/fragments/ruleDetailsChangesNew.graphql");
- getRuleOverview = ruleOverviewFragments + File.ReadAllText(QueryPath + "rule/getRuleOverview.graphql");
- getRuleDetails = ruleDetailsFragments + File.ReadAllText(QueryPath + "rule/getRuleDetails.graphql");
- // getRuleDetailsForReport = ruleDetailsForReportFragments + File.ReadAllText(QueryPath + "rule/getRuleDetails.graphql");
- getRuleByUid = File.ReadAllText(QueryPath + "rule/getRuleByUid.graphql");
+ getRuleOverview = ruleOverviewFragments + GetQueryText("rule/getRuleOverview.graphql");
+ getRuleDetails = ruleDetailsFragments + GetQueryText("rule/getRuleDetails.graphql");
+ getRuleByUid = GetQueryText("rule/getRuleByUid.graphql");
getRuleNetworkObjectDetails = ObjectQueries.networkObjectDetailsFragment;
- getRuleIdsOfImport = File.ReadAllText(QueryPath + "report/getRuleIdsOfImport.graphql");
- getRuleUidsOfDevice = File.ReadAllText(QueryPath + "report/getRuleUidsOfDevice.graphql");
- getRulesByManagement = ruleDetailsFragments + File.ReadAllText(QueryPath + "report/getRulesByManagement.graphql");
- getModelledRulesByManagementName = ruleDetailsForReportFragments + File.ReadAllText(QueryPath + "report/getModelledRulesByManagementName.graphql");
- getModelledRulesByManagementComment = ruleDetailsForReportFragments + File.ReadAllText(QueryPath + "report/getModelledRulesByManagementComment.graphql");
- getNatRuleOverview = natRuleOverviewFragments + File.ReadAllText(QueryPath + "rule/getNatRuleOverview.graphql");
- getNatRuleDetails = natRuleDetailsFragments + File.ReadAllText(QueryPath + "rule/getNatRuleDetails.graphql");
- // getNatRuleDetailsForReport = natRuleDetailsForReportFragments + File.ReadAllText(QueryPath + "rule/getNatRuleDetails.graphql");
+ getRuleIdsOfImport = GetQueryText("report/getRuleIdsOfImport.graphql");
+ getRuleUidsOfDevice = GetQueryText("report/getRuleUidsOfDevice.graphql");
+ getRulesByManagement = ruleDetailsFragments + GetQueryText("report/getRulesByManagement.graphql");
+ getModelledRulesByManagementName = ruleDetailsForReportFragments + GetQueryText("report/getModelledRulesByManagementName.graphql");
+ getModelledRulesByManagementComment = ruleDetailsForReportFragments + GetQueryText("report/getModelledRulesByManagementComment.graphql");
+ getNatRuleOverview = natRuleOverviewFragments + GetQueryText("rule/getNatRuleOverview.graphql");
+ getNatRuleDetails = natRuleDetailsFragments + GetQueryText("rule/getNatRuleDetails.graphql");
+ getRulesWithViolationsInTimespanByChunk = ruleDetailsFragments + GetQueryText("rule/getRulesWithViolationsInTimespanByChunk.graphql");
+ getRulesWithCurrentViolationsByChunk = ruleDetailsFragments + GetQueryText("rule/getRulesWithCurrentViolationsByChunk.graphql");
+ getRulesForSelectedManagements = ruleDetailsFragments + GetQueryText("rule/getRulesForSelectedManagements.graphql");
+ countRules = GetQueryText("rule/countRules.graphql");
+ countActiveRules = GetQueryText("rule/countActiveRules.graphql");
}
catch (Exception exception)
{
Log.WriteError("Initialize Api Queries", "Api Rule Queries could not be loaded.", exception);
+#if RELEASE
Environment.Exit(-1);
+#else
+ throw;
+#endif
}
}
}
diff --git a/roles/lib/files/FWO.Api.Client/Queries/StmQueries.cs b/roles/lib/files/FWO.Api.Client/Queries/StmQueries.cs
index fc93625e4d..c63468d85e 100644
--- a/roles/lib/files/FWO.Api.Client/Queries/StmQueries.cs
+++ b/roles/lib/files/FWO.Api.Client/Queries/StmQueries.cs
@@ -13,14 +13,18 @@ static StmQueries()
{
try
{
- getIpProtocols = File.ReadAllText(QueryPath + "stmTables/getIpProtocols.graphql");
- getRuleActions = File.ReadAllText(QueryPath + "stmTables/getRuleActions.graphql");
- getTracking = File.ReadAllText(QueryPath + "stmTables/getTracking.graphql");
+ getIpProtocols = GetQueryText("stmTables/getIpProtocols.graphql");
+ getRuleActions = GetQueryText("stmTables/getRuleActions.graphql");
+ getTracking = GetQueryText("stmTables/getTracking.graphql");
}
catch (Exception exception)
{
Log.WriteError("Initialize StmQueries", "Api StmQueries could not be loaded.", exception);
+#if RELEASE
Environment.Exit(-1);
+#else
+ throw;
+#endif
}
}
}
diff --git a/roles/lib/files/FWO.Basics/Comparer/ComplianceViolationComparer.cs b/roles/lib/files/FWO.Basics/Comparer/ComplianceViolationComparer.cs
new file mode 100644
index 0000000000..c4dfadde57
--- /dev/null
+++ b/roles/lib/files/FWO.Basics/Comparer/ComplianceViolationComparer.cs
@@ -0,0 +1,23 @@
+using FWO.Basics.Interfaces;
+
+namespace FWO.Basics.Comparer
+{
+ public class ComplianceViolationComparer : IEqualityComparer
+ {
+ public bool Equals(IComplianceViolation? x, IComplianceViolation? y)
+ {
+ if (ReferenceEquals(x, y)) return true;
+ if (x is null || y is null) return false;
+
+ return x.RuleId == y.RuleId &&
+ x.PolicyId == y.PolicyId &&
+ x.CriterionId == y.CriterionId &&
+ x.Details == y.Details;
+ }
+
+ public int GetHashCode(IComplianceViolation obj)
+ {
+ return HashCode.Combine(obj.RuleId, obj.PolicyId, obj.CriterionId, obj.Details);
+ }
+ }
+}
\ No newline at end of file
diff --git a/roles/lib/files/FWO.Basics/Enums/AssessabilityIssue.cs b/roles/lib/files/FWO.Basics/Enums/AssessabilityIssue.cs
new file mode 100644
index 0000000000..7c5207fcd3
--- /dev/null
+++ b/roles/lib/files/FWO.Basics/Enums/AssessabilityIssue.cs
@@ -0,0 +1,10 @@
+namespace FWO.Basics.Enums
+{
+ public enum AssessabilityIssue
+ {
+ IPNull,
+ AllIPs,
+ HostAddress,
+ Broadcast
+ }
+}
diff --git a/roles/lib/files/FWO.Basics/FWO.Basics.csproj b/roles/lib/files/FWO.Basics/FWO.Basics.csproj
index 3bd98ee11f..c709fe7797 100644
--- a/roles/lib/files/FWO.Basics/FWO.Basics.csproj
+++ b/roles/lib/files/FWO.Basics/FWO.Basics.csproj
@@ -9,6 +9,7 @@
+
diff --git a/roles/lib/files/FWO.Basics/GlobalConstants.cs b/roles/lib/files/FWO.Basics/GlobalConstants.cs
index ebed2482e3..3c6428a8f0 100644
--- a/roles/lib/files/FWO.Basics/GlobalConstants.cs
+++ b/roles/lib/files/FWO.Basics/GlobalConstants.cs
@@ -37,6 +37,7 @@ public struct GlobalConst
public const string kImportAppData = "importAppData";
public const string kAdjustAppServerNames = "adjustAppServerNames";
public const string kImportAreaSubnetData = "importAreaSubnetData";
+ public const string kImportZoneMatrixData = "importZoneMatrixData";
public const string kVarianceAnalysis = "varianceAnalysis";
public const string kManual = "manual";
public const string kCSV_ = "CSV_";
@@ -53,6 +54,7 @@ public struct GlobalConst
public const string kLdapGroupPattern = kModellerGroup + Placeholder.AppId;
public const string kImportChangeNotify = "importChangeNotify";
public const string kExternalRequest = "externalRequest";
+ public const string kComplianceCheck = "complianceCheck";
public const string kLdapInternalPostfix = "dc=" + kFwoProdName + ",dc=internal";
public const int kLdapInternalId = 1;
public const string kDummyAppRole = "DummyAppRole";
@@ -82,6 +84,7 @@ public struct ObjectType
public const string Host = "host";
public const string Network = "network";
public const string IPRange = "ip_range";
+ public const string AccessRole = "access-role";
}
public struct ServiceType
diff --git a/roles/lib/files/FWO.Basics/GlobalFunctions.cs b/roles/lib/files/FWO.Basics/GlobalFunctions.cs
new file mode 100644
index 0000000000..194f5c0402
--- /dev/null
+++ b/roles/lib/files/FWO.Basics/GlobalFunctions.cs
@@ -0,0 +1,14 @@
+
+namespace FWO.Basics
+{
+
+ public static class GlobalFunc
+ {
+ public static string ShowBool(bool boolVal)
+ {
+ // shows hook (true) or x (false) in UI
+ return boolVal ? "\u2714" : "\u2716";
+ }
+ }
+}
+
diff --git a/roles/lib/files/FWO.Basics/ITreeItem.cs b/roles/lib/files/FWO.Basics/ITreeItem.cs
new file mode 100644
index 0000000000..f7a794cbe4
--- /dev/null
+++ b/roles/lib/files/FWO.Basics/ITreeItem.cs
@@ -0,0 +1,20 @@
+namespace FWO.Basics
+{
+ public interface ITreeItem
+ {
+ List> Children { get; set; }
+ List> ElementsFlat { get; set; }
+ List? Position { get; set; }
+ ITreeItem? Parent { get; set; }
+ ITreeItem? LastAddedItem { get; set; }
+ TItem? Data { get; set; }
+ string? Identifier { get; set; }
+ bool IsRoot { get; set; }
+ string Header { get; set; }
+
+ string GetPositionString();
+ void SetPosition(string orderNumberString);
+ ITreeItem AddItem(ITreeItem? item = null, List? position = null, string header = "", bool isRoot = false, bool addToFlatList = false, bool addToChildren = false, bool setLastAddedItem = false);
+ string ToJson();
+ }
+}
diff --git a/roles/lib/files/FWO.Basics/Icons.cs b/roles/lib/files/FWO.Basics/Icons.cs
index d29beef83e..1d4efd0410 100644
--- a/roles/lib/files/FWO.Basics/Icons.cs
+++ b/roles/lib/files/FWO.Basics/Icons.cs
@@ -56,6 +56,10 @@ public struct Icons
public const string Refresh = "bi bi-arrow-clockwise";
public const string Undo = "bi bi-arrow-counterclockwise";
+ //Actions Html
+ public const string HtmlArrowExpanded = "▼";
+ public const string HtmlArrowCollapsed = "▶";
+
// Object types: General
public const string Ldap = "bi bi-journal-bookmark-fill";
public const string Management = "bi bi-inbox-fill";
diff --git a/roles/lib/files/FWO.Basics/Interfaces/IComplianceViolation.cs b/roles/lib/files/FWO.Basics/Interfaces/IComplianceViolation.cs
new file mode 100644
index 0000000000..cb71bb5700
--- /dev/null
+++ b/roles/lib/files/FWO.Basics/Interfaces/IComplianceViolation.cs
@@ -0,0 +1,15 @@
+namespace FWO.Basics.Interfaces
+{
+ public interface IComplianceViolation
+ {
+ int RuleId { get; set; }
+ DateTime FoundDate { get; set; }
+ DateTime? RemovedDate { get; set; }
+ string Details { get; set; }
+ long RiskScore { get; set; }
+ int PolicyId { get; set; }
+ int CriterionId { get; set; }
+ }
+}
+
+
diff --git a/roles/lib/files/FWO.Basics/Interfaces/ILogger.cs b/roles/lib/files/FWO.Basics/Interfaces/ILogger.cs
new file mode 100644
index 0000000000..03d2777d08
--- /dev/null
+++ b/roles/lib/files/FWO.Basics/Interfaces/ILogger.cs
@@ -0,0 +1,12 @@
+namespace FWO.Basics.Interfaces
+{
+ public interface ILogger
+ {
+ void TryWriteInfo(string title, string text, bool condition);
+ void TryWriteDebug(string title, string text, bool condition);
+ void TryWriteWarning(string title, string text, bool condition);
+ void TryWriteError(string title, string text, bool condition);
+ void TryWriteError(string title, Exception exception, bool condition);
+ void TryWriteAudit(string title, string text, bool condition);
+ }
+}
\ No newline at end of file
diff --git a/roles/lib/files/FWO.Basics/Interfaces/IRuleViewData.cs b/roles/lib/files/FWO.Basics/Interfaces/IRuleViewData.cs
new file mode 100644
index 0000000000..55a1ef4e25
--- /dev/null
+++ b/roles/lib/files/FWO.Basics/Interfaces/IRuleViewData.cs
@@ -0,0 +1,22 @@
+namespace FWO.Basics.Interfaces
+{
+ public interface IRuleViewData
+ {
+ string MgmtId { get; set; }
+ string Uid { get; set; }
+ string Name { get; set; }
+ string Source { get; set; }
+ string Destination { get; set; }
+ string Services { get; set; }
+ string Action { get; set; }
+ string InstallOn { get; set; }
+ string Compliance { get; set; }
+ string ViolationDetails { get; set; }
+ string ChangeID { get; set; }
+ string AdoITID { get; set; }
+ string Comment { get; set; }
+ string RulebaseId { get; set; }
+ string Enabled { get; set; }
+ }
+}
+
diff --git a/roles/lib/files/FWO.Basics/IpOperations.cs b/roles/lib/files/FWO.Basics/IpOperations.cs
index ff6aa505a1..ba35204130 100644
--- a/roles/lib/files/FWO.Basics/IpOperations.cs
+++ b/roles/lib/files/FWO.Basics/IpOperations.cs
@@ -2,6 +2,7 @@
using System.Net;
using NetTools;
using System;
+using System.Numerics;
using DnsClient;
namespace FWO.Basics
@@ -57,6 +58,20 @@ public static async Task DnsReverseLookUp(IPAddress address)
}
}
+ public static async Task DnsLookUp(string hostname)
+ {
+ try
+ {
+ return (await Dns.GetHostAddressesAsync(hostname))
+ .FirstOrDefault(a => a.AddressFamily == AddressFamily.InterNetwork)?
+ .ToString() ?? "";
+ }
+ catch (Exception)
+ {
+ return "";
+ }
+ }
+
public static (string, string) SplitIpToRange(string ipString)
{
string ipStart;
@@ -78,7 +93,7 @@ public static (string, string) SplitIpToRange(string ipString)
return (ipStart, ipEnd);
}
- public static bool TryParseIPStringToRange(this string ipString, out (string Start, string End) ipRange, bool strictv4Parse = false)
+ public static bool TryParseIPStringToRange(this string ipString, out (string start, string end) ipRange, bool strictv4Parse = false)
{
ipRange = default;
@@ -112,8 +127,8 @@ public static bool TryParseIPStringToRange(this string ipString, out (string Sta
return false;
}
- ipRange.Start = ipStart;
- ipRange.End = ipEnd;
+ ipRange.start = ipStart;
+ ipRange.end = ipEnd;
return true;
}
@@ -164,8 +179,8 @@ public static bool TryParseIPString(this string ipString, out T? ipResult, bo
}
else if (typeof(T) == typeof((IPAddress, IPAddress)))
{
- Tuple? ipTuple = new(ipAdressStart!, ipAdressEnd!);
- ipResult = (T)Convert.ChangeType(ipTuple, typeof(T));
+ (IPAddress, IPAddress) ipTuple = (ipAdressStart!, ipAdressEnd!);
+ ipResult = (T)(object)ipTuple;
return true;
}
@@ -383,5 +398,141 @@ public static int CompareIpFamilies(IPAddress ip1, IPAddress ip2)
return 0;
}
+
+ public static List Subtract(this IPAddressRange source, List subtractor)
+ {
+ List sourceNetwork = new();
+ List subtractorNetwork = new();
+
+ if (source.Begin.ToString().Equals(source.End.ToString()))
+ {
+ int sourceMask = source.Begin.AddressFamily == AddressFamily.InterNetwork ? 32 : 128;
+ sourceNetwork.Add(IPNetwork2.Parse(source.ToString(), (byte)sourceMask));
+ }
+ else if (IPNetwork2.TryParseRange(source.ToString(), out IEnumerable parsedSourceRange))
+ {
+ sourceNetwork.AddRange(parsedSourceRange);
+ }
+
+ foreach (IPAddressRange range in subtractor)
+ {
+ if (range.Begin.ToString().Equals(range.End.ToString()))
+ {
+ int rangeMask = source.Begin.AddressFamily == AddressFamily.InterNetwork ? 32 : 128;
+ subtractorNetwork.Add(IPNetwork2.Parse(range.ToString(), (byte)rangeMask));
+ }
+ else if (IPNetwork2.TryParseRange(range.ToString(), out IEnumerable parsedSubtractorRange))
+ {
+ subtractorNetwork.AddRange(parsedSubtractorRange);
+ }
+ }
+
+ List result = sourceNetwork.Subtract(subtractorNetwork).ToList();
+ List mergedRanges = result.ToMergedRanges();
+
+ return mergedRanges;
+ }
+
+ public static IEnumerable Subtract(
+ this IEnumerable source,
+ IEnumerable subtract)
+ {
+ if (source == null) throw new ArgumentNullException(nameof(source));
+ if (subtract == null) throw new ArgumentNullException(nameof(subtract));
+
+ var result = source.ToList();
+
+ foreach (var sub in subtract)
+ {
+ // jedes Netz aus 'subtract' von allen bisherigen abziehen
+ result = result
+ .SelectMany(n => n - sub) // nutzt IPNetwork2's Operator -
+ .ToList();
+ }
+
+ return result;
+ }
+
+ public static List ToMergedRanges(this IEnumerable networks, bool includeNetworkAndBroadcast = true)
+ {
+ var list = networks?.ToList() ?? new List();
+ if (list.Count == 0) return new List();
+
+ // 1) in [start,end] (inklusive) umwandeln
+ var intervals = list.Select(n =>
+ {
+ var start = includeNetworkAndBroadcast ? n.Network : n.FirstUsable;
+ var end = includeNetworkAndBroadcast ? n.Broadcast : n.LastUsable;
+ return (start, end);
+ }).ToList();
+
+ // Sicherheit: alles gleicher AddressFamily?
+ var af = intervals[0].start.AddressFamily;
+ if (intervals.Any(t => t.start.AddressFamily != af || t.end.AddressFamily != af))
+ throw new InvalidOperationException("Gemischte AddressFamily (IPv4/IPv6) in den Netzen.");
+
+ // 2) sortieren
+ intervals.Sort((a, b) => CompareIpValues(a.start, b.start));
+
+ // 3) zusammenführen (merge), wenn überlappend ODER direkt benachbart
+ var merged = new List<(IPAddress start, IPAddress end)>();
+ (IPAddress s, IPAddress e) cur = intervals[0];
+
+ foreach (var (s, e) in intervals.Skip(1))
+ {
+ // Wenn s <= cur.e + 1 => zusammenlegen
+ var nextToCurEndPlusOne = CompareIpValues(s, AddIp(cur.e, 1)) <= 0;
+ if (nextToCurEndPlusOne)
+ {
+ if (CompareIpValues(e, cur.e) > 0) cur.e = e;
+ }
+ else
+ {
+ merged.Add(cur);
+ cur = (s, e);
+ }
+ }
+ merged.Add(cur);
+
+ // 4) in IPAddressRange (inklusive) umwandeln
+ return merged.Select(t => new IPAddressRange(t.start, t.end)).ToList();
+ }
+
+ private static IPAddress AddIp(IPAddress ip, long delta)
+ {
+ var bi = ToBigInteger(ip) + new BigInteger(delta);
+ if (bi < BigInteger.Zero) bi = BigInteger.Zero;
+ var family = ip.AddressFamily;
+ var max = MaxValue(family);
+ if (bi > max) bi = max;
+ return FromBigInteger(bi, family);
+ }
+
+ private static BigInteger ToBigInteger(IPAddress ip)
+ {
+ var bytes = ip.GetAddressBytes(); // big-endian
+ var le = bytes.Reverse().Concat(new byte[] { 0 }).ToArray(); // little-endian + unsignd pad
+ return new BigInteger(le);
+ }
+
+ private static IPAddress FromBigInteger(BigInteger value, AddressFamily family)
+ {
+ int len = family == AddressFamily.InterNetwork ? 4 : 16;
+ var bytesLE = value.ToByteArray(); // little-endian
+ var bytesBE = new byte[len];
+ for (int i = 0; i < len; i++)
+ {
+ var src = i < bytesLE.Length ? bytesLE[i] : (byte)0;
+ bytesBE[len - 1 - i] = src;
+ }
+ return new IPAddress(bytesBE);
+ }
+
+ private static BigInteger MaxValue(AddressFamily family)
+ {
+ int bits = family == AddressFamily.InterNetwork ? 32 : 128;
+ return (BigInteger.One << bits) - 1;
+ }
+
}
}
diff --git a/roles/lib/files/FWO.Basics/LocalSettings.cs b/roles/lib/files/FWO.Basics/LocalSettings.cs
new file mode 100644
index 0000000000..4634770bae
--- /dev/null
+++ b/roles/lib/files/FWO.Basics/LocalSettings.cs
@@ -0,0 +1,53 @@
+using System.Text.Json;
+
+namespace FWO.Basics
+{
+ public static class LocalSettings
+ {
+ public static bool CSharpUnitTestsVerbose { get; set; } = false;
+
+ public static bool ComplianceCheckVerbose { get; set; } = true;
+
+ ///
+ /// Static constructor to load local settings from a JSON file specified by the
+ /// FWORCH_LOCAL_SETTINGS_PATH environment variable. To set this environment variable permanently on a linux system,
+ /// you can run a command like this in your terminal: 'export FWORCH_LOCAL_SETTINGS_PATH="//.vscode/settings.local.json"'.
+ /// Please adjust the path according to your setup.
+ /// If the environment variable is not set, the file is not found or cannot be read, default settings are used.
+ ///
+ static LocalSettings()
+ {
+ TryGetLocalSettings();
+ }
+
+ public static void TryGetLocalSettings()
+ {
+ string? localSettings = Environment.GetEnvironmentVariable("FWORCH_LOCAL_SETTINGS_PATH");
+
+ if (File.Exists(localSettings))
+ {
+ try
+ {
+ using FileStream s = File.OpenRead(localSettings);
+ JsonDocument json = JsonDocument.Parse(s);
+
+ if (json.RootElement.TryGetProperty("test.unittests.csharp.verbose", out var testUnitTestsCSharpVerbose))
+ {
+ CSharpUnitTestsVerbose = testUnitTestsCSharpVerbose.GetBoolean();
+ }
+
+ if (json.RootElement.TryGetProperty("log.compliancecheck.verbose", out var logComplianceCheckVerbose))
+ {
+ ComplianceCheckVerbose = logComplianceCheckVerbose.GetBoolean();
+ }
+
+ }
+ catch
+ {
+ Console.WriteLine($"Reading local settings from {localSettings} failed. Using default settings.");
+ }
+ }
+ }
+ }
+}
+
diff --git a/roles/lib/files/FWO.Basics/ReportType.cs b/roles/lib/files/FWO.Basics/ReportType.cs
index 0dace9da81..d762788ebb 100644
--- a/roles/lib/files/FWO.Basics/ReportType.cs
+++ b/roles/lib/files/FWO.Basics/ReportType.cs
@@ -2,7 +2,7 @@ namespace FWO.Basics
{
public enum ReportType
{
- All = 0,
+ Undefined = 0,
Rules = 1,
Changes = 2,
Statistics = 3,
@@ -19,7 +19,10 @@ public enum ReportType
VarianceAnalysis = 23,
OwnerRecertification = 24,
RecertificationEvent = 25,
- RecertEventReport = 26
+ RecertEventReport = 26,
+ ComplianceReport = 31,
+ ComplianceDiffReport = 32
+
}
public static class ReportTypeGroups
@@ -58,8 +61,10 @@ public static bool IsResolvedReport(this ReportType reportType)
ReportType.ResolvedRules or
ReportType.ResolvedRulesTech or
ReportType.ResolvedChanges or
- ReportType.ResolvedChangesTech => true,
- _ => false
+ ReportType.ResolvedChangesTech or
+ ReportType.ComplianceReport or
+ ReportType.ComplianceDiffReport => true,
+ _ => false,
};
}
@@ -103,6 +108,10 @@ ReportType.RecertificationEvent or
};
}
+ public static bool IsComplianceReport(this ReportType reportType)
+ {
+ return reportType == ReportType.ComplianceReport || reportType == ReportType.ComplianceDiffReport;
+ }
public static bool HasTimeFilter(this ReportType reportType)
{
return reportType switch
@@ -121,7 +130,7 @@ ReportType.ResolvedChanges or
public static List AllReportTypes()
{
- return [.. Enum.GetValues(typeof(ReportType)).Cast().Where(r => r != ReportType.All)];
+ return [.. Enum.GetValues(typeof(ReportType)).Cast().Where(r => r != ReportType.Undefined)];
}
public static List ReportTypeSelection(bool ruleRelated = true, bool modellingRelated = true)
@@ -134,7 +143,7 @@ public static List CustomSortReportType(List ListIn, boo
List ListOut = [];
List orderedReportTypeList =
[
- ReportType.All,
+ ReportType.Undefined,
ReportType.RecertificationEvent,
ReportType.Rules, ReportType.ResolvedRules, ReportType.ResolvedRulesTech, ReportType.UnusedRules, ReportType.NatRules,
ReportType.Changes, ReportType.ResolvedChanges, ReportType.ResolvedChangesTech,
@@ -148,7 +157,7 @@ public static List CustomSortReportType(List ListIn, boo
];
foreach (var reportType in orderedReportTypeList.Where(r => ListIn.Contains(r)))
{
- if (reportType == ReportType.All || ruleRelated && reportType.IsDeviceRelatedReport() || modellingRelated && reportType.IsModellingReport())
+ if (reportType == ReportType.Undefined || ruleRelated && reportType.IsDeviceRelatedReport() || modellingRelated && reportType.IsModellingReport())
{
ListOut.Add(reportType);
}
diff --git a/roles/lib/files/FWO.Basics/StringExtensionsHtml.cs b/roles/lib/files/FWO.Basics/StringExtensionsHtml.cs
new file mode 100644
index 0000000000..1748b13c50
--- /dev/null
+++ b/roles/lib/files/FWO.Basics/StringExtensionsHtml.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+
+namespace FWO.Basics
+{
+ public static partial class StringExtensions
+ {
+
+ private const string HtmlTagPattern = "<.*?>";
+ private static readonly HashSet SafeHtmlTags = new HashSet(["br", "i", "hr", "b", "u", "strong", "em", "p", "ul", "ol", "li", "span", "div"], StringComparer.Ordinal);
+
+
+ public static string StripDangerousHtmlTags(this string text, RegexOptions options = RegexOptions.None)
+ {
+ return Regex.Replace(text, HtmlTagPattern, StripIfDangerousTag, options, TimeSpan.FromMilliseconds(100));
+ }
+
+ public static string StripHtmlTags(this string text, RegexOptions options = RegexOptions.None)
+ {
+ return Regex.Replace(text, HtmlTagPattern, string.Empty, options, TimeSpan.FromMilliseconds(100));
+ }
+
+ private static string StripIfDangerousTag(Match tagMatch)
+ {
+ string tagName = ExtractTagName(tagMatch.Value);
+ return SafeHtmlTags.Contains(tagName) ? tagMatch.Value : string.Empty;
+ }
+
+ private static string ExtractTagName(string tag)
+ {
+ if (string.IsNullOrEmpty(tag) || tag.Length < 3)
+ {
+ return string.Empty;
+ }
+
+ int index = 1;
+ while (index < tag.Length && (tag[index] == '/' || char.IsWhiteSpace(tag[index])))
+ {
+ index++;
+ }
+
+ int start = index;
+ while (index < tag.Length && char.IsLetterOrDigit(tag[index]))
+ {
+ index++;
+ }
+
+ return index > start ? tag[start..index] : string.Empty;
+ }
+ }
+}
diff --git a/roles/lib/files/FWO.Basics/StringExtensionsIp.cs b/roles/lib/files/FWO.Basics/StringExtensionsIp.cs
index 380af23ec7..d27377d6cb 100644
--- a/roles/lib/files/FWO.Basics/StringExtensionsIp.cs
+++ b/roles/lib/files/FWO.Basics/StringExtensionsIp.cs
@@ -9,8 +9,47 @@ namespace FWO.Basics
{
public static partial class StringExtensions
{
- private const string HtmlTagPattern = "<.*?>";
- private static readonly string[] AllowedTags = ["br?", "i", "hr"];
+
+ public static int Compare(this string left, string right)
+ {
+ if (!IPAddress.TryParse(left?.Trim(), out var a))
+ throw new ArgumentException("Invalid IP address: " + left, nameof(left));
+ if (!IPAddress.TryParse(right?.Trim(), out var b))
+ throw new ArgumentException("Invalid IP address: " + right, nameof(right));
+
+ var a16 = ToComparableBytes(a);
+ var b16 = ToComparableBytes(b);
+
+ // Lexicographic compare, most-significant byte first (network byte order).
+ for (int i = 0; i < 16; i++)
+ {
+ int diff = a16[i].CompareTo(b16[i]);
+ if (diff != 0) return diff;
+ }
+ return 0;
+ }
+
+ public static bool IsGreater(this string left, string right) => Compare(left, right) > 0;
+
+ private static byte[] ToComparableBytes(IPAddress ip)
+ {
+ // Ensure a 16-byte representation:
+ // - IPv4 -> IPv6-mapped (::ffff:a.b.c.d)
+ // - IPv6 -> as-is
+ IPAddress v6 = ip.AddressFamily == AddressFamily.InterNetwork
+ ? ip.MapToIPv6()
+ : ip;
+
+ var bytes = v6.GetAddressBytes(); // always 16 for IPv6
+ if (bytes.Length != 16)
+ throw new InvalidOperationException("Expected a 16-byte IPv6 address after mapping.");
+ return bytes;
+ }
+
+ public static bool GenerousCompare(this string? string1, string? string2)
+ {
+ return string.IsNullOrEmpty(string1) && string.IsNullOrEmpty(string2) || string1 == string2;
+ }
[GeneratedRegex(@"(\/[\d\.\:]+)\D?")]
private static partial Regex NetmaskRegex();
@@ -99,23 +138,6 @@ public static string StripOffUnnecessaryNetmask(this string ip)
return ip;
}
- private static string BuildDangerousHtmlTagPattern()
- {
- string allowedTags = string.Join('|', AllowedTags);
- return $"<(?!:{allowedTags}).*?(?";
- }
-
- public static string StripHtmlTags(this string text, RegexOptions options = RegexOptions.None)
- {
- return Regex.Replace(text, HtmlTagPattern, string.Empty, options);
- }
-
- public static string StripDangerousHtmlTags(this string text, RegexOptions options = RegexOptions.None)
- {
- string pattern = BuildDangerousHtmlTagPattern();
- return Regex.Replace(text, pattern, string.Empty, options);
- }
-
public static bool IsIPv4(this string ipAddress)
{
if (IPAddress.TryParse(ipAddress, out IPAddress? addr))
@@ -143,9 +165,14 @@ public static bool IsIPv6(this string ipAddress)
}
public static string IpAsCidr(this string ip)
- {
- return IPAddressRange.Parse(ip).ToCidrString();
- }
+ {
+ return IPAddressRange.Parse(ip).ToCidrString();
+ }
+
+ public static string ToComparableIpString(this string ip)
+ {
+ return ip.IpAsCidr().PadLeft(43, '0'); // max length of an IPv6 CIDR string is 43 chars
+ }
public static (string start, string end) CidrToRangeString(this string cidr)
{
@@ -188,8 +215,14 @@ public static (IPAddress start, IPAddress end) CidrToRange(this string cidr)
private static (IPAddress start, IPAddress end) IPv4CidrToRange(byte[] addressBytes, int prefixLength)
{
+ if (prefixLength is < 0 or > 32)
+ throw new ArgumentOutOfRangeException(nameof(prefixLength));
+
uint ipAddress = BitConverter.ToUInt32([.. addressBytes.Reverse()], 0);
- uint mask = (uint.MaxValue << (32 - prefixLength)) & uint.MaxValue;
+
+ uint mask = prefixLength == 0
+ ? 0u
+ : uint.MaxValue << (32 - prefixLength);
uint startIp = ipAddress & mask;
uint endIp = startIp | ~mask;
@@ -198,6 +231,7 @@ private static (IPAddress start, IPAddress end) IPv4CidrToRange(byte[] addressBy
new IPAddress([.. BitConverter.GetBytes(endIp).Reverse()]));
}
+
private static (IPAddress start, IPAddress end) IPv6CidrToRange(byte[] addressBytes, int prefixLength)
{
if (BitConverter.IsLittleEndian)
diff --git a/roles/lib/files/FWO.Basics/TreeItem.cs b/roles/lib/files/FWO.Basics/TreeItem.cs
new file mode 100644
index 0000000000..253b60765e
--- /dev/null
+++ b/roles/lib/files/FWO.Basics/TreeItem.cs
@@ -0,0 +1,186 @@
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace FWO.Basics
+{
+ ///
+ /// A generic class to build tree structures.
+ ///
+ ///
+ public class TreeItem : ITreeItem
+ {
+ ///
+ /// Direct Children of this item.
+ ///
+ public List> Children { get; set; } = new();
+ ///
+ /// A flat list of every tree item that is somewhere in the tree.
+ ///
+ public List> ElementsFlat { get; set; } = new();
+
+ ///
+ /// N-dimensional numeric indicator for the position of this item in the tree.
+ ///
+ public List? Position { get; set; } = new();
+ ///
+ /// Direct parent of this tree item.
+ ///
+ public ITreeItem? Parent { get; set; }
+ ///
+ /// The last item that was added to the tree.
+ ///
+ public ITreeItem? LastAddedItem { get; set; }
+ ///
+ /// The object that is organized with its kins in the tree structure
+ ///
+ public TItem? Data { get; set; }
+ ///
+ /// An identifier for the object.
+ ///
+ public string? Identifier { get; set; }
+
+ ///
+ /// Flag that indicates wether this is the root item of the tree.
+ ///
+ public bool IsRoot { get; set; } = false;
+ ///
+ /// A header for items that are organizational groups (like sections, or ordered layers for rules).
+ ///
+ public string Header { get; set; } = "";
+
+ ///
+ /// Returns the position as a dotted number of type string.
+ ///
+ public string GetPositionString()
+ {
+ if (Position != null)
+ {
+ return string.Join(".", Position);
+ }
+ else
+ {
+ return "";
+ }
+
+ }
+
+ ///
+ /// Sets the position on the basis of a (dotted number) string.
+ ///
+ public void SetPosition(string orderNumberString)
+ {
+ Position = orderNumberString
+ .Split('.')
+ .Select(int.Parse)
+ .ToList();
+ }
+
+ ///
+ /// Adds an item to the tree.
+ ///
+ public virtual ITreeItem AddItem(ITreeItem? item = null, List? position = null, string header = "", bool isRoot = false, bool addToFlatList = false, bool addToChildren = false, bool setLastAddedItem = false)
+ {
+ ITreeItem newItem = item ?? new TreeItem();
+
+ newItem.Parent = this;
+ newItem.Position = position;
+ newItem.Header = header;
+ newItem.IsRoot = isRoot;
+
+ if (addToFlatList)
+ {
+ ElementsFlat.Add(newItem);
+ }
+
+ if (addToChildren)
+ {
+ Children.Add(newItem);
+ }
+
+ if (setLastAddedItem)
+ {
+ LastAddedItem = newItem;
+ }
+
+ return newItem;
+ }
+
+ ///
+ /// Creates a json string for the structure, taking this item as the root.
+ ///
+ public string ToJson()
+ {
+ var rootNode = BuildSerializableNode(this);
+
+ var options = new JsonSerializerOptions
+ {
+ WriteIndented = true
+ };
+
+ return JsonSerializer.Serialize(rootNode, options);
+ }
+
+ ///
+ /// Creates items that can be serialized to a json.
+ ///
+ private SerializableTreeNode BuildSerializableNode(ITreeItem node)
+ {
+ var serializable = new SerializableTreeNode();
+
+ if (node.Identifier != null)
+ {
+ serializable.Identifier = node.Identifier;
+ }
+
+ if (node.IsRoot == true)
+ {
+ serializable.IsRoot = true;
+ }
+
+ if (node.Header != "")
+ {
+ serializable.Header = node.Header;
+ }
+
+ if (node.Data != null)
+ {
+ serializable.Position = node.GetPositionString();
+ }
+
+ if (node.Children.Any())
+ {
+ serializable.Children = new();
+
+ foreach (var child in node.Children)
+ {
+ serializable.Children.Add(BuildSerializableNode(child));
+ }
+ }
+
+
+ return serializable;
+ }
+
+ ///
+ /// Definition which data of the actual items are processed and how they are processed during serialization.
+ ///
+ private class SerializableTreeNode
+ {
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? Identifier { get; set; }
+
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public bool? IsRoot { get; set; }
+
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? Header { get; set; }
+
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? Position { get; set; }
+
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public List? Children { get; set; }
+ }
+
+ }
+}
diff --git a/roles/lib/files/FWO.Compliance/ComplianceCheck.cs b/roles/lib/files/FWO.Compliance/ComplianceCheck.cs
new file mode 100644
index 0000000000..b158f19fa1
--- /dev/null
+++ b/roles/lib/files/FWO.Compliance/ComplianceCheck.cs
@@ -0,0 +1,1096 @@
+using FWO.Api.Client;
+using FWO.Api.Client.Queries;
+using FWO.Basics;
+using FWO.Basics.Interfaces;
+using FWO.Basics.Enums;
+using FWO.Config.Api;
+using FWO.Data;
+using NetTools;
+using FWO.Logging;
+using FWO.Ui.Display;
+using FWO.Data.Extensions;
+using System.Net;
+using System.Collections.Concurrent;
+using FWO.Services;
+
+namespace FWO.Compliance
+{
+ ///
+ /// Provides the state and methods required to evaluate how well
+ /// firewall management rules comply with the defined compliance policy.
+ ///
+ /// The ComplianceCheck class encapsulates the logic used to analyze
+ /// rule configurations, identify deviations from policy requirements,
+ /// and deliver a structured assessment of compliance status.
+ ///
+ public class ComplianceCheck
+ {
+ #region Props & fields
+
+ ///
+ /// Active policy that defines the compliance criteria.
+ ///
+ public CompliancePolicy? Policy = null;
+
+ ///
+ /// Network zones to use for matrix compliance check.
+ ///
+ public List NetworkZones { get; set; } = [];
+
+ ///
+ /// Wraps the static class FWO.Logging.Log to make it accessible for unit tests.
+ ///
+ public ILogger Logger { get; set; } = new Logger();
+
+ ///
+ /// Violations found in the last run of CheckAll.
+ ///
+ public List CurrentViolationsInCheck { get; private set; } = [];
+
+ ///
+ /// Rules that are to be evaluated in the next run of CheckAll.
+ ///
+ public List? RulesInCheck { get; set; } = [];
+
+ ///
+ /// Managements that are the subjects of the check.
+ ///
+ public List? Managements { get; set; } = [];
+
+ ///
+ /// Access to API.
+ ///
+ private readonly ApiConnection _apiConnection;
+ ///
+ /// Access to user config.
+ ///
+ private readonly UserConfig _userConfig;
+
+ ///
+ /// Parameter for treating domain and dynamic network objects as part of the auto-calculated internet zone.
+ ///
+ private bool _treatDomainAndDynamicObjectsAsInternet = false;
+ ///
+ /// True if the feature auto-calculated internet zone is activated.
+ ///
+ private bool _autoCalculatedInternetZoneActive = false;
+ ///
+ /// Id of the compliance policy that is configured for the check.
+ ///
+ private int _complianceCheckPolicyId = 0;
+ ///
+ /// Number of elements that are treated as a chunk in parallelized processes
+ ///
+ private int _elementsPerFetch;
+ ///
+ /// Limit of threads that may be used for the compliance check.
+ ///
+ private int _maxDegreeOfParallelism;
+ ///
+ /// Collection that is suitable for parallel processing and receives and holds insert arguments for newly found violations.
+ ///
+ private readonly ConcurrentBag _violationsToAdd = new();
+ ///
+ /// Collection that is suitable for parallel processing and receives and holds remove arguments for deprecated violations.
+ ///
+ private readonly ConcurrentBag _violationsToRemove = new();
+ ///
+ /// Collection that is suitable for parallel processing and receives and holds violations as a result of the current check.
+ ///
+ private readonly ConcurrentBag _currentViolations = new();
+ ///
+ /// Multi-threading helper.
+ ///
+ private readonly ParallelProcessor _parallelProcessor;
+
+ #endregion
+
+ #region Ctor
+
+ ///
+ /// Constructor for compliance check
+ ///
+ /// User configuration
+ /// Api connection
+ /// Log
+ public ComplianceCheck(UserConfig userConfig, ApiConnection apiConnection, ILogger? logger = null)
+ {
+ _apiConnection = apiConnection;
+ _userConfig = userConfig;
+
+ if (logger != null)
+ {
+ Logger = logger;
+ }
+
+ _parallelProcessor = new(apiConnection, Logger);
+
+ if (_userConfig.GlobalConfig == null)
+ {
+ Logger.TryWriteInfo("Compliance Check", "Global config not found.", _userConfig.GlobalConfig == null);
+ }
+
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ ///
+ /// Full compliance check to be called by scheduler.
+ ///
+ /// Task that completes when the asynchronous compliance evaluation finished.
+ public async Task CheckAll()
+ {
+ DateTime startTime = DateTime.UtcNow;
+
+ try
+ {
+ // Gathering necessary parameters for compliance check.
+
+ Logger.TryWriteInfo("Compliance Check", "Starting compliance check.", true);
+
+ GlobalConfig? globalConfig = _userConfig.GlobalConfig;
+
+ if (globalConfig == null)
+ {
+ Logger.TryWriteInfo("Compliance Check", "Global config is necessary for compliance check, but was not found. Aborting compliance check.", true);
+ return;
+ }
+
+ _complianceCheckPolicyId = globalConfig.ComplianceCheckPolicyId;
+ _autoCalculatedInternetZoneActive = globalConfig.AutoCalculateInternetZone;
+ _treatDomainAndDynamicObjectsAsInternet = globalConfig.TreatDynamicAndDomainObjectsAsInternet;
+ _elementsPerFetch = globalConfig.ComplianceCheckElementsPerFetch;
+ _maxDegreeOfParallelism = globalConfig.ComplianceCheckAvailableProcessors;
+
+ Logger.TryWriteInfo("Compliance Check", $"Parallelizing config: {_elementsPerFetch} elements per fetch and {_maxDegreeOfParallelism} processors.", LocalSettings.ComplianceCheckVerbose);
+
+ if (_complianceCheckPolicyId == 0)
+ {
+ Logger.TryWriteInfo("Compliance Check", "No Policy defined. Compliance check not possible.", true);
+ return;
+ }
+
+ Policy = await _apiConnection.SendQueryAsync(ComplianceQueries.getPolicyById, new { id = _complianceCheckPolicyId });
+
+ if (Policy == null)
+ {
+ Logger.TryWriteError("Compliance Check", $"Policy with id {_complianceCheckPolicyId} not found.", true);
+ return;
+ }
+
+ Managements = await _apiConnection.SendQueryAsync>(DeviceQueries.getManagementNames);
+ Managements = GetRelevantManagements(globalConfig, Managements);
+
+ if (Managements == null || Managements.Count == 0)
+ {
+ Logger.TryWriteInfo("Compliance Check", "No relevant managements found. Compliance check not possible.", true);
+ return;
+ }
+
+ Logger.TryWriteInfo("Compliance Check", $"Using policy {_complianceCheckPolicyId}", LocalSettings.ComplianceCheckVerbose);
+
+ Logger.TryWriteInfo("Compliance Check", $"Policy criteria: {Policy.Criteria.Count} criteria found.", LocalSettings.ComplianceCheckVerbose);
+
+ if (Policy.Criteria.Count == 0)
+ {
+ Logger.TryWriteInfo("Compliance Check", $"Policy without criteria. Compliance check not possible.", LocalSettings.ComplianceCheckVerbose);
+ return;
+ }
+
+ foreach (var criterion in Policy.Criteria)
+ {
+ Logger.TryWriteInfo("Compliance Check", $"Criterion: {criterion.Content.Name} ({criterion.Content.CriterionType}).", LocalSettings.ComplianceCheckVerbose);
+ }
+
+ // Clear previous check data
+
+ RulesInCheck = [];
+ CurrentViolationsInCheck.Clear();
+ _currentViolations.Clear();
+
+ // Load data for evaluation.
+
+ await LoadNetworkZones();
+
+ // Perform check.
+
+ RulesInCheck = await PerformCheckAsync(Managements!.Select(m => m.Id).ToList());
+
+ if (RulesInCheck == null || RulesInCheck.Count == 0)
+ {
+ Logger.TryWriteInfo("Compliance Check", "No relevant rules found. Compliance check not possible.", true);
+ return;
+ }
+
+ TimeSpan elapsed = DateTime.UtcNow - startTime;
+
+ Logger.TryWriteInfo("Compliance Check", $"Compliance check evaluated {RulesInCheck.Count} rules in {elapsed.TotalSeconds} seconds.", true);
+ Logger.TryWriteInfo("Compliance Check", "Compliance check completed.", true);
+
+ }
+ catch (Exception e)
+ {
+ TimeSpan elapsed = DateTime.UtcNow - startTime;
+ Logger.TryWriteInfo("Compliance Check", $"Compliance check failed after {elapsed.TotalSeconds} seconds.", true);
+ Logger.TryWriteError("Compliance Check", e, true);
+ }
+
+ }
+
+ ///
+ /// Retrieves rules with violations from DB, calculates current violations, and prepares diff arguments.
+ ///
+ /// Management identifiers whose rules should be checked.
+ /// List of all rules that have been analyzed.
+ public async Task> PerformCheckAsync(List managementIds)
+ {
+ // Getting max import id for query vars.
+
+ long? maxImportId = 0;
+
+
+ Import? import = await _apiConnection.SendQueryAsync(ImportQueries.getMaxImportId);
+
+ if (import != null && import.ImportAggregate != null && import.ImportAggregate.ImportAggregateMax != null)
+ {
+ maxImportId = import.ImportAggregate.ImportAggregateMax.RelevantImportId ?? 0;
+
+ }
+
+ // Getting total number of rules, for calculating chunks.
+
+ AggregateCount? result = await _apiConnection.SendQueryAsync(RuleQueries.countActiveRules);
+ int activeRulesCount = result?.Aggregate?.Count ?? 0;
+
+ Logger.TryWriteInfo("Compliance Check", $"Loading {activeRulesCount} active rules in chunks of {_elementsPerFetch} for managements: {string.Join(",", managementIds)}.", LocalSettings.ComplianceCheckVerbose);
+
+ // Retrieve rules and check current compliance for every rule.
+
+ _parallelProcessor.SetUp(activeRulesCount, _maxDegreeOfParallelism, _elementsPerFetch);
+
+ List[]? chunks = await _parallelProcessor.SendParallelizedQueriesAsync(RuleQueries.getRulesForSelectedManagements, CalculateCompliance, managementIds, maxImportId);
+
+ if (chunks == null)
+ {
+ Logger.TryWriteInfo("Compliance Check", $"Chunks could not be loaded from the database.", LocalSettings.ComplianceCheckVerbose);
+ return [];
+ }
+
+ Logger.TryWriteInfo("Compliance Check", $"Attempted to load {chunks.Length} chunks of rules.", LocalSettings.ComplianceCheckVerbose);
+
+ List? rules = chunks
+ .SelectMany(rule => rule)
+ .ToList();
+
+ Logger.TryWriteInfo("Compliance Check", $"Loaded {rules.Count} rules.", LocalSettings.ComplianceCheckVerbose);
+
+ CurrentViolationsInCheck = _currentViolations.ToList();
+
+ Logger.TryWriteInfo("Compliance Check", $"Found {CurrentViolationsInCheck.Count} violations.", LocalSettings.ComplianceCheckVerbose);
+
+ Logger.TryWriteInfo("Compliance Check", $"Post-processing {rules.Count} rules.", LocalSettings.ComplianceCheckVerbose);
+
+ // Create diffs and fill argument bags.
+
+ await PostProcessRulesAsync(rules);
+
+ return rules;
+ }
+
+ ///
+ /// Creates insert/remove violation lists by comparing DB state with current check results.
+ ///
+ /// Rules including the violations persisted in the database.
+ public Task PostProcessRulesAsync(List ruleFromDb)
+ {
+ List<(ComplianceViolation Violation, string Key)> dbViolationsWithKeys = ruleFromDb
+ .SelectMany(rule => rule.Violations)
+ .Select(violation => (violation, CreateUniqueViolationKey(violation)))
+ .ToList();
+
+ List<(ComplianceViolation Violation, string Key)> currentViolationsWithKeys = CurrentViolationsInCheck
+ .Select(violation => (violation, CreateUniqueViolationKey(violation)))
+ .ToList();
+
+ HashSet currentKeySet = currentViolationsWithKeys.Select(v => v.Key).ToHashSet(StringComparer.Ordinal);
+ HashSet dbKeySet = dbViolationsWithKeys.Select(v => v.Key).ToHashSet(StringComparer.Ordinal);
+
+ ParallelOptions parallelOptions = new()
+ {
+ MaxDegreeOfParallelism = Math.Max(1, _maxDegreeOfParallelism)
+ };
+
+ // Get remove args.
+
+ Logger.TryWriteInfo("Compliance Check", $"Getting violations to remove.", LocalSettings.ComplianceCheckVerbose);
+
+ _violationsToRemove.Clear();
+
+ Parallel.ForEach(
+ dbViolationsWithKeys,
+ parallelOptions,
+ pair =>
+ {
+ if (!currentKeySet.Contains(pair.Key))
+ {
+ _violationsToRemove.Add(pair.Violation);
+ }
+ });
+
+ Logger.TryWriteInfo("Compliance Check", $"Got {_violationsToRemove.Count} violations to remove.", LocalSettings.ComplianceCheckVerbose);
+
+ // Get insert args.
+
+ Logger.TryWriteInfo("Compliance Check", $"Getting violations to insert.", LocalSettings.ComplianceCheckVerbose);
+
+ _violationsToAdd.Clear();
+
+ Parallel.ForEach(
+ currentViolationsWithKeys,
+ parallelOptions,
+ pair =>
+ {
+ if (!dbKeySet.Contains(pair.Key))
+ {
+ ComplianceViolationBase violationBase = ComplianceViolationBase.CreateBase(pair.Violation);
+ _violationsToAdd.Add(violationBase);
+ }
+ });
+
+ Logger.TryWriteInfo("Compliance Check", $"Got {_violationsToAdd.Count} violations to insert.", LocalSettings.ComplianceCheckVerbose);
+
+ return Task.CompletedTask;
+ }
+
+ ///
+ /// Updates the violation db table.
+ ///
+ public async Task PersistDataAsync()
+ {
+ try
+ {
+ Logger.TryWriteInfo("Compliance Check", "Persisting violations.", true);
+
+ if (_violationsToAdd.Count == 0)
+ {
+ Logger.TryWriteInfo("Compliance Check", "No new violations to persist.", LocalSettings.ComplianceCheckVerbose);
+ }
+ else
+ {
+ List violations = _violationsToAdd.Cast().ToList();
+ object variablesAdd = new
+ {
+ violations
+ };
+
+ await _apiConnection.SendQueryAsync(ComplianceQueries.addViolations, variablesAdd);
+
+ Logger.TryWriteInfo("Compliance Check", $"Persisted {_violationsToAdd.Count} new violations.", LocalSettings.ComplianceCheckVerbose);
+ }
+
+ List ids = _violationsToRemove.Select(violation => violation.Id).ToList();
+
+ if (ids.Count == 0)
+ {
+ Logger.TryWriteInfo("Compliance Check", "No violations to remove.", LocalSettings.ComplianceCheckVerbose);
+ }
+ else
+ {
+ Logger.TryWriteInfo("Compliance Check", $"{ids.Count} violations to remove.", LocalSettings.ComplianceCheckVerbose);
+
+ DateTime removedAt = DateTime.UtcNow;
+
+ object variablesRemove = new
+ {
+ ids,
+ removedAt
+ };
+
+ await _apiConnection.SendQueryAsync(ComplianceQueries.removeViolations, variablesRemove);
+
+ Logger.TryWriteInfo("Compliance Check", $"Removed {ids.Count} violations.", LocalSettings.ComplianceCheckVerbose && ids.Count > 0);
+ }
+
+ Logger.TryWriteInfo("Compliance Check", "Persisting of violations completed.", true);
+ }
+ catch (Exception e)
+ {
+ Logger.TryWriteError("ComplianceCheck - PersistDataAsync", e, true);
+ }
+ }
+
+ ///
+ /// Checks whether a rule can be assessed, i.e. contains only evaluable network objects.
+ ///
+ /// Rule that is currently under test.
+ /// Fully resolved source objects.
+ /// Fully resolved destination objects.
+ /// Compliance criterion for assessability.
+ /// True if the rule can be assessed, otherwise false.
+ public Task CheckAssessability(Rule rule, List resolvedSources, List resolvedDestinations, ComplianceCriterion criterion)
+ {
+ bool isAssessable = true;
+
+ // If treated as part of internet zone dynamic and domain objects are irrelevant for the assessability check.
+
+ resolvedSources = TryFilterDynamicAndDomainObjects(resolvedSources);
+ resolvedDestinations = TryFilterDynamicAndDomainObjects(resolvedDestinations);
+
+ // Check only accept rules for assessability.
+
+ if (rule.Action == "accept")
+ {
+ foreach (NetworkObject networkObject in resolvedSources.Concat(resolvedDestinations))
+ {
+ // Get assessability issue type if existing.
+
+ AssessabilityIssue? assessabilityIssue = TryGetAssessabilityIssue(networkObject);
+
+ if (assessabilityIssue != null)
+ {
+ // Create check result object.
+
+ ComplianceCheckResult complianceCheckResult;
+
+ if (resolvedSources.Contains(networkObject))
+ {
+ complianceCheckResult = new(rule, ComplianceViolationType.NotAssessable)
+ {
+ Source = networkObject
+ };
+ }
+ else
+ {
+ complianceCheckResult = new(rule, ComplianceViolationType.NotAssessable)
+ {
+ Destination = networkObject
+ };
+ }
+
+ complianceCheckResult.AssessabilityIssue = assessabilityIssue;
+ complianceCheckResult.Criterion = criterion;
+
+ // Create violation.
+
+ CreateViolation(ComplianceViolationType.NotAssessable, rule, complianceCheckResult);
+ isAssessable = false;
+ }
+ }
+ }
+
+ return Task.FromResult(isAssessable);
+ }
+
+ ///
+ /// Evaluates a rule against all configured compliance criteria.
+ ///
+ /// Rule whose compliance should be checked.
+ /// Set of criteria derived from the policy.
+ /// True if the rule is compliant with every criterion.
+ public async Task CheckRuleCompliance(Rule rule, IEnumerable criteria)
+ {
+ bool ruleIsCompliant = true;
+
+ if (rule.Action == "accept")
+ {
+ // Resolve network locations
+
+ NetworkLocation[] networkLocations = rule.Froms.Concat(rule.Tos).ToArray();
+ List resolvedNetworkLocations = RuleDisplayBase.GetResolvedNetworkLocations(networkLocations);
+
+ List resolvedSources = RuleDisplayBase
+ .GetResolvedNetworkLocations(rule.Froms)
+ .Select(from => from.Object)
+ .ToList();
+
+ List resolvedDestinations = RuleDisplayBase
+ .GetResolvedNetworkLocations(rule.Tos)
+ .Select(to => to.Object)
+ .ToList();
+
+ try
+ {
+ foreach (var criterion in criteria)
+ {
+ switch (criterion.CriterionType)
+ {
+ case nameof(CriterionType.Assessability):
+ ruleIsCompliant &= CheckAssessability(rule, resolvedSources, resolvedDestinations, criterion).Result;
+ break;
+ case nameof(CriterionType.Matrix):
+ ruleIsCompliant &= await CheckMatrixCompliance(rule, criterion, resolvedSources, resolvedDestinations);
+ break;
+ case nameof(CriterionType.ForbiddenService):
+ ruleIsCompliant &= CheckForForbiddenService(rule, criterion);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ catch (System.Exception e)
+ {
+ Logger.TryWriteError("Compliance Check", e, true);
+ }
+
+ }
+
+ return ruleIsCompliant;
+ }
+
+ ///
+ /// Extracts the IP ranges represented by a network object in all supported forms.
+ ///
+ /// Network object to parse.
+ /// List of ranges (empty if parsing is not possible).
+ public static List ParseIpRange(NetworkObject networkObject)
+ {
+ List ranges = [];
+
+ if (networkObject.Type.Name == ObjectType.IPRange || (networkObject.Type.Name == ObjectType.Network && networkObject.IP.Equals(networkObject.IpEnd) == false))
+ {
+ if (IPAddress.TryParse(networkObject.IP.StripOffNetmask(), out IPAddress? ipStart) && IPAddress.TryParse(networkObject.IpEnd.StripOffNetmask(), out IPAddress? ipEnd))
+ {
+ ranges.Add(new IPAddressRange(ipStart, ipEnd));
+ }
+ }
+ else if (networkObject.Type.Name != ObjectType.Group && networkObject.ObjectGroupFlats.Length > 0)
+ {
+ for (int j = 0; j < networkObject.ObjectGroupFlats.Length; j++)
+ {
+ if (networkObject.ObjectGroupFlats[j].Object != null)
+ {
+ ranges.AddRange(ParseIpRange(networkObject.ObjectGroupFlats[j].Object!));
+ }
+ }
+ }
+ else if (networkObject.IP != null)
+ {
+ // CIDR notation or single (host) IP can be parsed directly
+ ranges.Add(IPAddressRange.Parse(networkObject.IP));
+ }
+
+ return ranges;
+ }
+
+ ///
+ /// Compliance check used in current UI implementation.
+ ///
+ /// Source range provided by the UI.
+ /// Destination range provided by the UI.
+ /// Network zones to test against the provided ranges.
+ /// List of forbidden communications found by the matrix check.
+ public List<(ComplianceNetworkZone, ComplianceNetworkZone)> CheckIpRangeInputCompliance(IPAddressRange? sourceIpRange, IPAddressRange? destinationIpRange, List networkZones)
+ {
+ NetworkZones = networkZones;
+ List<(ComplianceNetworkZone, ComplianceNetworkZone)> forbiddenCommunicationsOutput = [];
+
+ if (sourceIpRange != null && destinationIpRange != null)
+ {
+ CheckMatrixCompliance
+ (
+ [sourceIpRange],
+ [destinationIpRange],
+ out forbiddenCommunicationsOutput
+ );
+ }
+
+ return forbiddenCommunicationsOutput;
+ }
+
+ ///
+ /// Filters the provided managements so that only the configured IDs remain.
+ ///
+ /// Global configuration containing the ID list.
+ /// All managements retrieved from the API.
+ /// Subset of managements that are relevant for the compliance check.
+ public static List GetRelevantManagements(GlobalConfig globalConfig, List managements)
+ {
+ List? filteredManagements = [];
+ List relevantManagementIDs = [];
+
+ if (!string.IsNullOrEmpty(globalConfig.ComplianceCheckRelevantManagements))
+ {
+ try
+ {
+ relevantManagementIDs = globalConfig.ComplianceCheckRelevantManagements
+ .Split(',', StringSplitOptions.RemoveEmptyEntries)
+ .Select(s => int.Parse(s.Trim()))
+ .ToList();
+
+ filteredManagements = managements.Where(m => relevantManagementIDs.Contains(m.Id)).ToList();
+
+ }
+ catch (Exception e)
+ {
+ Log.TryWriteLog(LogType.Error, "Compliance Report", $"Error while parsing relevant management IDs: {e.Message}", LocalSettings.ComplianceCheckVerbose);
+ }
+ }
+
+ return filteredManagements;
+ }
+
+ ///
+ /// Performs the matrix compliance check for a rule by mapping resolved objects to zones.
+ ///
+ /// Rule under test.
+ /// Matrix criterion.
+ /// Resolved source objects.
+ /// Resolved destination objects.
+ private async Task CheckMatrixCompliance(Rule rule, ComplianceCriterion criterion, List resolvedSources, List resolvedDestinations)
+ {
+ Task ipRanges)>> fromsTask = GetNetworkObjectsWithIpRanges(resolvedSources);
+ Task ipRanges)>> tosTask = GetNetworkObjectsWithIpRanges(resolvedDestinations);
+
+ await Task.WhenAll(fromsTask, tosTask);
+
+ bool ruleIsCompliant = true;
+
+ List<(NetworkObject networkObject, List networkZones)> sourceZones = MapZonesToNetworkObjects(fromsTask.Result);
+ List<(NetworkObject networkObject, List networkZones)> destinationZones = MapZonesToNetworkObjects(tosTask.Result);
+
+ Dictionary> sourceObjectsByZone = MapObjectsByZone(sourceZones);
+ Dictionary> destinationObjectsByZone = MapObjectsByZone(destinationZones);
+
+ foreach ((ComplianceNetworkZone sourceZone, List sourceObjects) in sourceObjectsByZone)
+ {
+ foreach ((ComplianceNetworkZone destinationZone, List destinationObjects) in destinationObjectsByZone)
+ {
+ if (!sourceZone.CommunicationAllowedTo(destinationZone))
+ {
+ ruleIsCompliant = false;
+ string sourceObjectsString = string.Join(", ", sourceObjects.Select(GetNwObjectString).Distinct());
+ string destinationObjectsString = string.Join(", ", destinationObjects.Select(GetNwObjectString).Distinct());
+
+ string details = $"{_userConfig.GetText("H5839")}: {sourceZone.Name} ({sourceObjectsString}) -> {destinationZone.Name} ({destinationObjectsString})";
+
+ ComplianceCheckResult complianceCheckResult = new(rule, ComplianceViolationType.MatrixViolation)
+ {
+ Criterion = criterion,
+ SourceZone = sourceZone,
+ DestinationZone = destinationZone
+ };
+
+ CreateViolation(ComplianceViolationType.MatrixViolation, rule, complianceCheckResult, details);
+ }
+ }
+ }
+
+ return ruleIsCompliant;
+ }
+
+ ///
+ /// Creates a violation entry from a compliance check result and stores it in the current run buffer.
+ ///
+ /// Type of violation to record.
+ /// Impacted rule.
+ /// Details assembled during the check.
+ /// Optional string used if details need to be customized.
+ private void CreateViolation(ComplianceViolationType violationType, Rule rule, ComplianceCheckResult complianceCheckResult, string? detailsOverride = null)
+ {
+ ComplianceViolation violation = new()
+ {
+ RuleId = (int)rule.Id,
+ RuleUid = rule.Uid ?? "",
+ MgmtUid = Managements?.FirstOrDefault(m => m.Id == rule.MgmtId)?.Uid ?? "",
+ PolicyId = Policy?.Id ?? 0,
+ CriterionId = complianceCheckResult.Criterion!.Id
+ };
+
+ switch (violationType)
+ {
+ case ComplianceViolationType.MatrixViolation:
+
+ if (!string.IsNullOrEmpty(detailsOverride))
+ {
+ violation.Details = detailsOverride;
+ }
+ else if (complianceCheckResult.Source is NetworkObject s && complianceCheckResult.Destination is NetworkObject d)
+ {
+ string sourceString = GetNwObjectString(s);
+ string destinationString = GetNwObjectString(d);
+ violation.Details = $"{_userConfig.GetText("H5839")}: {sourceString} (Zone: {complianceCheckResult.SourceZone?.Name ?? ""}) -> {destinationString} (Zone: {complianceCheckResult.DestinationZone?.Name ?? ""})";
+ }
+
+ break;
+
+ case ComplianceViolationType.ServiceViolation:
+
+ if (complianceCheckResult.Service is NetworkService svc)
+ {
+ violation.Details = $"{_userConfig.GetText("H5840")}: {svc.Name}";
+ }
+ else
+ {
+ throw new ArgumentNullException(paramName: "complianceCheckResult.Service", message: "The service argument must be non-null when creating a service violation.");
+ }
+
+ break;
+
+ case ComplianceViolationType.NotAssessable:
+
+ if (complianceCheckResult.AssessabilityIssue != null)
+ {
+ string networkObject = "";
+
+ if (complianceCheckResult.Source != null)
+ {
+ networkObject = GetNwObjectString(complianceCheckResult.Source);
+ }
+ else if (complianceCheckResult.Destination != null)
+ {
+ networkObject = GetNwObjectString(complianceCheckResult.Destination);
+ }
+
+ string assessabilityIssueType = complianceCheckResult.AssessabilityIssue.Value.ToAssessabilityIssueString();
+
+ violation.Details = $"{_userConfig.GetText("H5841")}: {_userConfig.GetText(assessabilityIssueType)}({networkObject})";
+ }
+
+ break;
+
+ default:
+
+ return;
+ }
+
+ _currentViolations.Add(violation);
+ }
+
+ ///
+ /// Returns a readable representation of a network object including its IP range.
+ ///
+ /// Network object to display.
+ private string GetNwObjectString(NetworkObject networkObject)
+ {
+ string networkObjectString = "";
+
+ networkObjectString += networkObject.Name;
+ networkObjectString += NwObjDisplay.DisplayIp(networkObject.IP, networkObject.IpEnd, networkObject.Type.Name, true);
+
+ return networkObjectString;
+ }
+
+ ///
+ /// Checks two IP range sets against the network zone matrix.
+ ///
+ /// Source ranges.
+ /// Destination ranges.
+ /// Output list of forbidden zone combinations.
+ private bool CheckMatrixCompliance(List source, List destination, out List<(ComplianceNetworkZone, ComplianceNetworkZone)> forbiddenCommunication)
+ {
+ // Determine all matching source zones
+ List sourceZones = DetermineZones(source);
+
+ // Determine all matching destination zones
+ List destinationZones = DetermineZones(destination);
+
+ forbiddenCommunication = [];
+
+ foreach (ComplianceNetworkZone sourceZone in sourceZones)
+ {
+ foreach (ComplianceNetworkZone destinationZone in destinationZones.Where(d => !sourceZone.CommunicationAllowedTo(d)))
+ {
+ forbiddenCommunication.Add((sourceZone, destinationZone));
+ }
+ }
+
+ return forbiddenCommunication.Count == 0;
+ }
+
+ ///
+ /// Validates whether a rule uses a service forbidden by the given criterion.
+ ///
+ /// Rule that may contain forbidden services.
+ /// Criterion defining the restricted service set.
+ private bool CheckForForbiddenService(Rule rule, ComplianceCriterion criterion)
+ {
+ bool ruleIsCompliant = true;
+
+ List restrictedServices = [.. criterion.Content.Split(',').Select(s => s.Trim())
+ .Where(s => !string.IsNullOrEmpty(s))];
+
+ if (restrictedServices.Count > 0)
+ {
+ foreach (var service in rule.Services.Where(s => restrictedServices.Contains(s.Content.Uid)))
+ {
+ ComplianceCheckResult complianceCheckResult = new(rule, ComplianceViolationType.ServiceViolation)
+ {
+ Criterion = criterion,
+ Service = service.Content
+ };
+
+ CreateViolation(ComplianceViolationType.ServiceViolation, rule, complianceCheckResult);
+ ruleIsCompliant = false;
+ }
+ }
+
+ return ruleIsCompliant;
+ }
+
+ ///
+ /// Builds a helper structure combining network objects with the IP ranges they represent.
+ ///
+ /// Objects that should be resolved to ranges.
+ private static Task ipRanges)>> GetNetworkObjectsWithIpRanges(List networkObjects)
+ {
+ List<(NetworkObject networkObject, List ipRanges)> networkObjectsWithIpRange = [];
+
+ foreach (NetworkObject networkObject in networkObjects)
+ {
+ networkObjectsWithIpRange.Add((networkObject, ParseIpRange(networkObject)));
+ }
+
+ return Task.FromResult(networkObjectsWithIpRange);
+ }
+
+
+ ///
+ /// Loads all network zones referenced by the policy matrix criterion.
+ ///
+ private async Task LoadNetworkZones()
+ {
+ if (Policy != null)
+ {
+ // ToDo later: work with several matrices?
+ int? matrixId = Policy.Criteria.FirstOrDefault(c => c.Content.CriterionType == CriterionType.Matrix.ToString())?.Content.Id;
+ if (matrixId != null)
+ {
+ Logger.TryWriteInfo("Compliance Check", $"Loading network zones for Matrix {matrixId}.", LocalSettings.ComplianceCheckVerbose);
+ NetworkZones = await _apiConnection.SendQueryAsync>(ComplianceQueries.getNetworkZonesForMatrix, new { criterionId = matrixId });
+ Logger.TryWriteInfo("Compliance Check", $"Loaded {NetworkZones.Count} network zones for Matrix {matrixId}.", LocalSettings.ComplianceCheckVerbose);
+ }
+ }
+ }
+
+
+ ///
+ /// Builds a unique key identifying a violation over management, rule, policy, criterion, and detail.
+ ///
+ private string CreateUniqueViolationKey(ComplianceViolation violation)
+ {
+ string key = "";
+
+ try
+ {
+ key = $"{violation.MgmtUid}_{violation.RuleUid}_{violation.PolicyId}_{violation.CriterionId}_{violation.Details}";
+ }
+ catch (Exception e)
+ {
+ Logger.TryWriteError("Compliance Check", e, true);
+ }
+
+ return key;
+ }
+
+ ///
+ /// Calculates compliance for all provided rules (or the rules from the last check) and stores violations.
+ ///
+ /// Explicit set of rules; when null, the rules prepared by are used.
+ /// List of rules that have been processed.
+ public async Task> CalculateCompliance(List? rulesToCheck = null)
+ {
+ List rules = rulesToCheck ?? RulesInCheck ?? [];
+
+ int nonCompliantRules = 0;
+ int checkedRules = 0;
+
+ Logger.TryWriteInfo("Compliance Check", $"Checking compliance for {rules.Count} rules.", LocalSettings.ComplianceCheckVerbose);
+
+ if (Policy == null || Policy.Criteria == null)
+ {
+ Logger.TryWriteError("Compliance Check", $"Checking compliance for rules not possible, because criteria could not be loaded.", true);
+ return await Task.FromResult(rules);
+ }
+
+ if (Policy.Criteria.Count == 0)
+ {
+ Logger.TryWriteError("Compliance Check", $"Checking compliance for rules not possible, because policy does not contain criteria.", true);
+ return await Task.FromResult(rules);
+ }
+
+ List criteria = Policy.Criteria.Select(c => c.Content).ToList();
+
+ if (criteria.Count == 0)
+ {
+ Logger.TryWriteError("Compliance Check", $"Checking compliance for rules not possible, because criteria were malformed.", true);
+ return await Task.FromResult(rules);
+ }
+
+ Logger.TryWriteInfo("Compliance Check", $"Checking compliance for {Policy.Criteria.Count} criteria.", LocalSettings.ComplianceCheckVerbose);
+
+ foreach (Rule rule in rules)
+ {
+ bool ruleIsCompliant = await CheckRuleCompliance(rule, criteria);
+
+ if (!ruleIsCompliant)
+ {
+ nonCompliantRules++;
+ }
+
+ checkedRules++;
+ }
+
+ Logger.TryWriteInfo("Compliance Check", $"Checked compliance for {checkedRules} rules and found {nonCompliantRules} non-compliant rules. Total violations: {_currentViolations.Count}.", LocalSettings.ComplianceCheckVerbose);
+ return await Task.FromResult(rules);
+ }
+
+ ///
+ /// Maps previously resolved IP ranges to their matching compliance zones.
+ ///
+ /// Pairs of network objects and IP ranges.
+ private List<(NetworkObject networkObject, List networkZones)> MapZonesToNetworkObjects(List<(NetworkObject networkObject, List ipRanges)> inputData)
+ {
+ List<(NetworkObject networkObject, List networkZones)> map = [];
+
+ foreach ((NetworkObject networkObject, List ipRanges) dataItem in inputData)
+ {
+ List networkZones = [];
+
+ if (_autoCalculatedInternetZoneActive && _treatDomainAndDynamicObjectsAsInternet && (dataItem.networkObject.Type.Name == "dynamic_net_obj" || dataItem.networkObject.Type.Name == "domain"))
+ {
+ List complianceNetworkZones = NetworkZones.Where(zone => zone.IsAutoCalculatedInternetZone).ToList();
+
+ foreach (ComplianceNetworkZone zone in complianceNetworkZones)
+ {
+ networkZones.Add(zone);
+ }
+ }
+ else if (dataItem.ipRanges.Count > 0)
+ {
+ if (TryGetAssessabilityIssue(dataItem.networkObject) != null)
+ {
+ continue;
+ }
+
+ networkZones = DetermineZones(dataItem.ipRanges);
+ }
+
+ map.Add((dataItem.networkObject, networkZones));
+ }
+
+ return map;
+ }
+
+ ///
+ /// Groups network objects by their associated compliance zone.
+ ///
+ /// Network objects enriched by their zones.
+ private Dictionary> MapObjectsByZone(List<(NetworkObject networkObject, List networkZones)> objectsWithZones)
+ {
+ Dictionary> map = new();
+
+ foreach ((NetworkObject networkObject, List networkZones) item in objectsWithZones)
+ {
+ if (item.networkZones == null || item.networkZones.Count == 0)
+ {
+ continue;
+ }
+
+ foreach (ComplianceNetworkZone zone in item.networkZones)
+ {
+ if (!map.TryGetValue(zone, out List? objectsInZone))
+ {
+ objectsInZone = [];
+ map.Add(zone, objectsInZone);
+ }
+
+ objectsInZone.Add(item.networkObject);
+ }
+ }
+
+ return map;
+ }
+
+ ///
+ /// Finds every compliance zone overlapped by the provided IP ranges (plus implicit internet zone when necessary).
+ ///
+ /// Ranges to look up.
+ private List DetermineZones(List ranges)
+ {
+ List result = [];
+ List> unseenIpAddressRanges = [];
+
+ for (int i = 0; i < ranges.Count; i++)
+ {
+ unseenIpAddressRanges.Add(
+ [
+ new(ranges[i].Begin, ranges[i].End)
+ ]);
+ }
+
+ foreach (ComplianceNetworkZone zone in NetworkZones.Where(z => z.OverlapExists(ranges, unseenIpAddressRanges)))
+ {
+ result.Add(zone);
+ }
+
+ // No need to proceed if auto calculated internet zone is activated.
+
+ if (_autoCalculatedInternetZoneActive)
+ {
+ return result;
+ }
+
+ // Get ip ranges that are not in any zone
+
+ List undefinedIpRanges = [.. unseenIpAddressRanges.SelectMany(x => x)];
+
+ if (undefinedIpRanges.Count > 0)
+ {
+ result.Add
+ (
+ new ComplianceNetworkZone()
+ {
+ Name = _userConfig.GetText("internet_local_zone"),
+ }
+ );
+ }
+
+ return result;
+ }
+
+ ///
+ /// Removes dynamic/domain objects when the feature treats them implicitly as internet.
+ ///
+ /// Network objects to filter.
+ private List TryFilterDynamicAndDomainObjects(List networkObjects)
+ {
+ if (_userConfig.GlobalConfig is GlobalConfig globalConfig && globalConfig.AutoCalculateInternetZone && globalConfig.TreatDynamicAndDomainObjectsAsInternet)
+ {
+ networkObjects = networkObjects
+ .Where(n => !new List { "domain", "dynamic_net_obj" }.Contains(n.Type.Name))
+ .ToList();
+ }
+
+ return networkObjects;
+ }
+
+ ///
+ /// Detects assessability issues (like overly broad objects) for a given network object.
+ ///
+ /// Network object to evaluate.
+ private AssessabilityIssue? TryGetAssessabilityIssue(NetworkObject networkObject)
+ {
+ if (networkObject.IP == null && networkObject.IpEnd == null)
+ return AssessabilityIssue.IPNull;
+
+ if (networkObject.IP == "0.0.0.0/32" && networkObject.IpEnd == "255.255.255.255/32")
+ return AssessabilityIssue.AllIPs;
+
+ if (networkObject.IP == "::/128" && networkObject.IpEnd == "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128")
+ return AssessabilityIssue.AllIPs;
+
+ if (networkObject.IP == "255.255.255.255/32" && networkObject.IpEnd == "255.255.255.255/32")
+ return AssessabilityIssue.Broadcast;
+
+ if (networkObject.IP == "0.0.0.0/32" && networkObject.IpEnd == "0.0.0.0/32")
+ return AssessabilityIssue.HostAddress;
+
+ return null;
+ }
+
+ #endregion
+ }
+}
diff --git a/roles/lib/files/FWO.Compliance/ComplianceCheckResult.cs b/roles/lib/files/FWO.Compliance/ComplianceCheckResult.cs
new file mode 100644
index 0000000000..01346ab995
--- /dev/null
+++ b/roles/lib/files/FWO.Compliance/ComplianceCheckResult.cs
@@ -0,0 +1,28 @@
+using FWO.Basics.Enums;
+using FWO.Data;
+
+namespace FWO.Compliance
+{
+ public class ComplianceCheckResult
+ {
+ public Rule Rule { get; set; }
+ public ComplianceViolationType Compliance { get; set; }
+
+ public ComplianceCriterion? Criterion { get; set; }
+
+ public NetworkObject? Source { get; set; }
+ public ComplianceNetworkZone? SourceZone { get; set; }
+ public NetworkObject? Destination { get; set; }
+ public ComplianceNetworkZone? DestinationZone { get; set; }
+ public NetworkService? Service { get; set; }
+ public AssessabilityIssue? AssessabilityIssue { get; set; }
+
+ public ComplianceCheckResult(Rule rule, ComplianceViolationType compliance = ComplianceViolationType.None)
+ {
+ Rule = rule;
+ Compliance = compliance;
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/roles/lib/files/FWO.Compliance/FWO.Compliance.csproj b/roles/lib/files/FWO.Compliance/FWO.Compliance.csproj
new file mode 100644
index 0000000000..19cb26510f
--- /dev/null
+++ b/roles/lib/files/FWO.Compliance/FWO.Compliance.csproj
@@ -0,0 +1,22 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/roles/lib/files/FWO.Config.Api/Data/ConfigData.cs b/roles/lib/files/FWO.Config.Api/Data/ConfigData.cs
index 6287ff4faf..0e466147df 100644
--- a/roles/lib/files/FWO.Config.Api/Data/ConfigData.cs
+++ b/roles/lib/files/FWO.Config.Api/Data/ConfigData.cs
@@ -415,6 +415,121 @@ public class ConfigData : ICloneable
[JsonProperty("resolveNetworkAreas"), JsonPropertyName("resolveNetworkAreas")]
public bool ResolveNetworkAreas { get; set; } = false;
+ [JsonProperty("complianceCheckSleepTime"), JsonPropertyName("complianceCheckSleepTime")]
+ public int ComplianceCheckSleepTime { get; set; } = 0;
+
+ [JsonProperty("complianceCheckStartAt"), JsonPropertyName("complianceCheckStartAt")]
+ public DateTime ComplianceCheckStartAt { get; set; } = DateTime.Now;
+
+ [JsonProperty("complianceCheckPolicy"), JsonPropertyName("complianceCheckPolicy")]
+ public int ComplianceCheckPolicyId { get; set; } = 0;
+
+ [JsonProperty("complianceCheckMailRecipients"), JsonPropertyName("complianceCheckMailRecipients")]
+ public string ComplianceCheckMailRecipients { get; set; } = "";
+
+ [JsonProperty("complianceCheckMailSubject"), JsonPropertyName("complianceCheckMailSubject")]
+ public string ComplianceCheckMailSubject { get; set; } = "";
+
+ [JsonProperty("complianceCheckMailBody"), JsonPropertyName("complianceCheckMailBody")]
+ public string ComplianceCheckMailBody { get; set; } = "";
+
+ [JsonProperty("complianceMatrixAllowNetworkZones"), JsonPropertyName("complianceMatrixAllowNetworkZones")]
+ public bool ComplianceMatrixAllowNetworkZones { get; set; } = false;
+
+ [JsonProperty("complianceCheckScheduledDiffReportsIntervals"), JsonPropertyName("complianceCheckScheduledDiffReportsIntervals")]
+ public string ComplianceCheckScheduledDiffReportsIntervals { get; set; } = "";
+
+ [JsonProperty("complianceCheckInternetZoneObject"), JsonPropertyName("complianceCheckInternetZoneObject")]
+ public string ComplianceCheckInternetZoneObject { get; set; } = "";
+
+ [JsonProperty("complianceCheckMaxPrintedViolations"), JsonPropertyName("complianceCheckMaxPrintedViolations")]
+ public int ComplianceCheckMaxPrintedViolations { get; set; } = 0;
+
+ [JsonProperty("complianceCheckSortMatrixByID"), JsonPropertyName("complianceCheckSortMatrixByID")]
+ public bool ComplianceCheckSortMatrixByID { get; set; } = false;
+
+ [JsonProperty("complianceCheckRelevantManagements"), JsonPropertyName("complianceCheckRelevantManagements")]
+ public string ComplianceCheckRelevantManagements { get; set; } = "";
+
+ [JsonProperty("reportSchedulerConfig"), JsonPropertyName("reportSchedulerConfig")]
+ public string ReportSchedulerConfig { get; set; } = "";
+
+ [JsonProperty("debugConfig"), JsonPropertyName("debugConfig")]
+ public string DebugConfig { get; set; } = "";
+
+ [JsonProperty("autoCalculateInternetZone"), JsonPropertyName("autoCalculateInternetZone")]
+ public bool AutoCalculateInternetZone { get; set; } = true;
+
+ [JsonProperty("autoCalculateUndefinedInternalZone"), JsonPropertyName("autoCalculateUndefinedInternalZone")]
+ public bool AutoCalculateUndefinedInternalZone { get; set; } = true;
+
+ [JsonProperty("internalZoneRange_10_0_0_0_8"), JsonPropertyName("internalZoneRange_10_0_0_0_8")]
+ public bool InternalZoneRange_10_0_0_0_8 { get; set; } = true;
+
+ [JsonProperty("internalZoneRange_172_16_0_0_12"), JsonPropertyName("internalZoneRange_172_16_0_0_12")]
+ public bool InternalZoneRange_172_16_0_0_12 { get; set; } = true;
+
+ [JsonProperty("internalZoneRange_192_168_0_0_16"), JsonPropertyName("internalZoneRange_192_168_0_0_16")]
+ public bool InternalZoneRange_192_168_0_0_16 { get; set; } = true;
+
+ [JsonProperty("internalZoneRange_0_0_0_0_8"), JsonPropertyName("internalZoneRange_0_0_0_0_8")]
+ public bool InternalZoneRange_0_0_0_0_8 { get; set; } = true;
+
+ [JsonProperty("internalZoneRange_127_0_0_0_8"), JsonPropertyName("internalZoneRange_127_0_0_0_8")]
+ public bool InternalZoneRange_127_0_0_0_8 { get; set; } = true;
+
+ [JsonProperty("internalZoneRange_169_254_0_0_16"), JsonPropertyName("internalZoneRange_169_254_0_0_16")]
+ public bool InternalZoneRange_169_254_0_0_16 { get; set; } = true;
+
+ [JsonProperty("internalZoneRange_224_0_0_0_4"), JsonPropertyName("internalZoneRange_224_0_0_0_4")]
+ public bool InternalZoneRange_224_0_0_0_4 { get; set; } = true;
+
+ [JsonProperty("internalZoneRange_240_0_0_0_4"), JsonPropertyName("internalZoneRange_240_0_0_0_4")]
+ public bool InternalZoneRange_240_0_0_0_4 { get; set; } = true;
+
+ [JsonProperty("internalZoneRange_255_255_255_255_32"), JsonPropertyName("internalZoneRange_255_255_255_255_32")]
+ public bool InternalZoneRange_255_255_255_255_32 { get; set; } = true;
+
+ [JsonProperty("internalZoneRange_192_0_2_0_24"), JsonPropertyName("internalZoneRange_192_0_2_0_24")]
+ public bool InternalZoneRange_192_0_2_0_24 { get; set; } = true;
+
+ [JsonProperty("internalZoneRange_198_51_100_0_24"), JsonPropertyName("internalZoneRange_198_51_100_0_24")]
+ public bool InternalZoneRange_198_51_100_0_24 { get; set; } = true;
+
+ [JsonProperty("internalZoneRange_203_0_113_0_24"), JsonPropertyName("internalZoneRange_203_0_113_0_24")]
+ public bool InternalZoneRange_203_0_113_0_24 { get; set; } = true;
+
+ [JsonProperty("internalZoneRange_100_64_0_0_10"), JsonPropertyName("internalZoneRange_100_64_0_0_10")]
+ public bool InternalZoneRange_100_64_0_0_10 { get; set; } = true;
+
+ [JsonProperty("internalZoneRange_192_0_0_0_24"), JsonPropertyName("internalZoneRange_192_0_0_0_24")]
+ public bool InternalZoneRange_192_0_0_0_24 { get; set; } = true;
+
+ [JsonProperty("internalZoneRange_192_88_99_0_24"), JsonPropertyName("internalZoneRange_192_88_99_0_24")]
+ public bool InternalZoneRange_192_88_99_0_24 { get; set; } = true;
+
+ [JsonProperty("internalZoneRange_198_18_0_0_15"), JsonPropertyName("internalZoneRange_198_18_0_0_15")]
+ public bool InternalZoneRange_198_18_0_0_15 { get; set; } = true;
+
+ [JsonProperty("autoCalculatedZonesAtTheEnd"), JsonPropertyName("autoCalculatedZonesAtTheEnd")]
+ public bool AutoCalculatedZonesAtTheEnd { get; set; } = true;
+
+ [JsonProperty("treatDynamicAndDomainObjectsAsInternet"), JsonPropertyName("treatDynamicAndDomainObjectsAsInternet")]
+ public bool TreatDynamicAndDomainObjectsAsInternet { get; set; } = true;
+
+ [JsonProperty("showShortColumnsInComplianceReports"), JsonPropertyName("showShortColumnsInComplianceReports")]
+ public bool ShowShortColumnsInComplianceReports { get; set; } = true;
+
+ [JsonProperty("importedMatrixReadOnly"), JsonPropertyName("importedMatrixReadOnly")]
+ public bool ImportedMatrixReadOnly { get; set; } = true;
+
+ [JsonProperty("complianceCheckElementsPerFetch"), JsonPropertyName("complianceCheckElementsPerFetch")]
+ public int ComplianceCheckElementsPerFetch { get; set; } = 500;
+
+ [JsonProperty("complianceCheckAvailableProcessors"), JsonPropertyName("complianceCheckAvailableProcessors")]
+ public int ComplianceCheckAvailableProcessors { get; set; } = 4;
+
+
public ConfigData(bool editable = false)
{
Editable = editable;
diff --git a/roles/lib/files/FWO.Config.Api/UserConfig.cs b/roles/lib/files/FWO.Config.Api/UserConfig.cs
index 89b416fe74..ce447a23d0 100644
--- a/roles/lib/files/FWO.Config.Api/UserConfig.cs
+++ b/roles/lib/files/FWO.Config.Api/UserConfig.cs
@@ -15,6 +15,7 @@ namespace FWO.Config.Api
///
public class UserConfig : Config, IDisposable
{
+ public GlobalConfig? GlobalConfig => globalConfig;
private readonly GlobalConfig? globalConfig;
private bool disposedValue;
@@ -23,6 +24,8 @@ public class UserConfig : Config, IDisposable
public UiUser User { private set; get; }
+
+
public static async Task ConstructAsync(GlobalConfig globalConfig, ApiConnection apiConnection, int userId)
{
UiUser[] users = await apiConnection.SendQueryAsync(AuthQueries.getUserByDbId, new { userId = userId });
@@ -45,12 +48,16 @@ public UserConfig(GlobalConfig globalConfig, ApiConnection apiConnection, UiUser
}
// Warning: only for Texts, ConfigItems contain Default content, correct ConfigItems are only in this.globalConfig
- public UserConfig(GlobalConfig globalConfig) : base()
+ public UserConfig(GlobalConfig globalConfig, bool registerOnChangeHandler = true) : base()
{
User = new UiUser();
Translate = globalConfig.LangDict[globalConfig.DefaultLanguage];
this.globalConfig = globalConfig;
- globalConfig.OnChange += OnGlobalConfigChange;
+
+ if (registerOnChangeHandler)
+ {
+ globalConfig.OnChange += OnGlobalConfigChange;
+ }
}
public UserConfig() : base()
diff --git a/roles/lib/files/FWO.Data/Alert.cs b/roles/lib/files/FWO.Data/Alert.cs
index 2ca1db870b..6e61df1363 100644
--- a/roles/lib/files/FWO.Data/Alert.cs
+++ b/roles/lib/files/FWO.Data/Alert.cs
@@ -18,7 +18,7 @@ public enum AlertCode
ImportBrokenObjectReferences = 16,
MalformedIpAddress = 17,
RecursionLimitReached = 18,
-
+
Autodiscovery = 21,
AutoDiscoveryErrorUnspecific = 22,
@@ -31,8 +31,10 @@ public enum AlertCode
ImportChangeNotify = 51,
ExternalRequest = 61,
-
- VarianceAnalysis = 71
+
+ VarianceAnalysis = 71,
+
+ ComplianceCheck = 81
}
public class Alert
diff --git a/roles/lib/files/FWO.Data/ApiCrudHelper.cs b/roles/lib/files/FWO.Data/ApiCrudHelper.cs
index 91ada11401..7f58adcdd0 100644
--- a/roles/lib/files/FWO.Data/ApiCrudHelper.cs
+++ b/roles/lib/files/FWO.Data/ApiCrudHelper.cs
@@ -1,5 +1,6 @@
-using System.Text.Json.Serialization;
-using Newtonsoft.Json;
+using System.Text.Json.Serialization;
+using Newtonsoft.Json;
+using FWO.Data;
namespace FWO.Data
{
@@ -14,6 +15,9 @@ public class ReturnId
[JsonProperty("updatedId"), JsonPropertyName("updatedId")]
public int UpdatedId { get; set; }
+ [JsonProperty("id"), JsonPropertyName("id")]
+ public long? Id { get; set; }
+
[JsonProperty("updatedIdLong"), JsonPropertyName("updatedIdLong")]
public long UpdatedIdLong { get; set; }
@@ -35,22 +39,45 @@ public class ReturnId
[JsonProperty("uiuser_password_must_be_changed"), JsonPropertyName("uiuser_password_must_be_changed")]
public bool PasswordMustBeChanged { get; set; }
}
-
+
public class ReturnIdWrapper
{
[JsonProperty("returning"), JsonPropertyName("returning")]
public ReturnId[]? ReturnIds { get; set; }
}
+ public class AggregateCountLastHit
+ // NOSONAR - temporarily disabled {
+ // [JsonProperty("device"), JsonPropertyName("device")]
+ // public List Devices {get; set;} = [];
+ // }
+ // public class DeviceLastHit
+ {
+ [JsonProperty("rulebase_link"), JsonPropertyName("rulebase_link")]
+ public List RulebasesOnGateway { get; set; } = [];
+ }
+
+ public class RulebaseOnGatewaysLastHit
+ {
+ [JsonProperty("rulebase"), JsonPropertyName("rulebase")]
+ public RulebaseLastHit Rulebase { get; set; } = new RulebaseLastHit();
+ }
+
+ public class RulebaseLastHit
+ {
+ [JsonProperty("rulesWithHits"), JsonPropertyName("rulesWithHits")]
+ public AggregateCount RulesWithHits { get; set; } = new AggregateCount();
+ }
+
public class AggregateCount
{
[JsonProperty("aggregate"), JsonPropertyName("aggregate")]
- public Aggregate Aggregate {get; set;} = new Aggregate();
+ public Aggregate Aggregate { get; set; } = new Aggregate();
}
public class Aggregate
{
- [JsonProperty("count"), JsonPropertyName("count")]
+ [JsonProperty("count"), JsonPropertyName("count")]
public int Count { get; set; }
}
}
diff --git a/roles/lib/files/FWO.Data/Color.cs b/roles/lib/files/FWO.Data/Color.cs
new file mode 100644
index 0000000000..cf594856ce
--- /dev/null
+++ b/roles/lib/files/FWO.Data/Color.cs
@@ -0,0 +1,12 @@
+
+using System.Text.Json.Serialization;
+using Newtonsoft.Json;
+
+namespace FWO.Data
+{
+ public class Color
+ {
+ [JsonProperty("color_name"), JsonPropertyName("color_name")]
+ public string Name { get; set; } = "";
+ }
+}
\ No newline at end of file
diff --git a/roles/lib/files/FWO.Data/ComplianceCriterion.cs b/roles/lib/files/FWO.Data/ComplianceCriterion.cs
new file mode 100644
index 0000000000..71eca20e1a
--- /dev/null
+++ b/roles/lib/files/FWO.Data/ComplianceCriterion.cs
@@ -0,0 +1,49 @@
+using Newtonsoft.Json;
+using System.Text.Json.Serialization;
+
+namespace FWO.Data
+{
+ public enum CriterionType
+ {
+ Matrix = 1,
+ Assessability = 2,
+
+ ForbiddenService = 10,
+ ForbiddenSource = 11,
+ ForbiddenDestination = 12,
+ ForbiddenTrack = 13
+ }
+
+ public class ComplianceCriterion
+ {
+ [JsonProperty("id"), JsonPropertyName("id")]
+ public int Id { get; set; }
+
+ [JsonProperty("name"), JsonPropertyName("name")]
+ public string Name { get; set; } = "";
+
+ [JsonProperty("import_source"), JsonPropertyName("import_source")]
+ public string ImportSource { get; set; } = "";
+
+ [JsonProperty("criterion_type"), JsonPropertyName("criterion_type")]
+ public string CriterionType { get; set; } = "";
+
+ [JsonProperty("content"), JsonPropertyName("content")]
+ public string Content { get; set; } = "";
+
+ [JsonProperty("comment"), JsonPropertyName("comment")]
+ public string? Comment { get; set; }
+
+ [JsonProperty("created"), JsonPropertyName("created")]
+ public DateTime Created { get; set; } = DateTime.UtcNow;
+
+ [JsonProperty("removed"), JsonPropertyName("removed")]
+ public DateTime? Removed { get; set; }
+ }
+
+ public class ComplianceCriterionWrapper
+ {
+ [JsonProperty("criterion"), JsonPropertyName("criterion")]
+ public virtual ComplianceCriterion Content { get; set; } = new();
+ }
+}
diff --git a/roles/lib/files/FWO.Data/ComplianceNetworkZone.cs b/roles/lib/files/FWO.Data/ComplianceNetworkZone.cs
index 80e2fa2a98..36e24779e2 100644
--- a/roles/lib/files/FWO.Data/ComplianceNetworkZone.cs
+++ b/roles/lib/files/FWO.Data/ComplianceNetworkZone.cs
@@ -14,6 +14,9 @@ public class ComplianceNetworkZone
[JsonProperty("name"), JsonPropertyName("name")]
public string Name { get; set; } = "";
+ [JsonProperty("id_string"), JsonPropertyName("id_string")]
+ public string IdString { get; set; } = "";
+
[JsonProperty("description"), JsonPropertyName("description")]
public string Description { get; set; } = "";
@@ -34,6 +37,20 @@ public class ComplianceNetworkZone
ItemConverterParameters = ["to_network_zone"]), JsonPropertyName("network_zone_communication_destinations")]
public ComplianceNetworkZone[] AllowedCommunicationDestinations { get; set; } = [];
+ [JsonProperty("created"), JsonPropertyName("created")]
+ public DateTime Created { get; set; }
+
+ [JsonProperty("removed"), JsonPropertyName("removed")]
+ public DateTime? Removed { get; set; }
+
+ [JsonProperty("criterion_id"), JsonPropertyName("criterion_id")]
+ public int CriterionId { get; set; } = 0;
+
+ [JsonProperty("is_auto_calculated_internet_zone"), JsonPropertyName("is_auto_calculated_internet_zone")]
+ public bool IsAutoCalculatedInternetZone { get; set; } = false;
+
+ [JsonProperty("is_auto_calculated_undefined_internal_zone"), JsonPropertyName("is_auto_calculated_undefined_internal_zone")]
+ public bool IsAutoCalculatedUndefinedInternalZone { get; set; } = false;
public bool CommunicationAllowedFrom(ComplianceNetworkZone from)
{
@@ -107,28 +124,29 @@ public object Clone()
ipRangesClone[i] = new IPAddressRange(IPRanges[i].Begin, IPRanges[i].End);
}
- return new ComplianceNetworkZone()
+ return new ComplianceNetworkZone()
{
Id = Id,
Superzone = (ComplianceNetworkZone?)Superzone?.Clone(),
Name = Name,
Description = Description,
IPRanges = ipRangesClone,
+ CriterionId = CriterionId,
Subzones = CloneArray(Subzones),
- AllowedCommunicationSources = CloneArray(AllowedCommunicationSources),
- AllowedCommunicationDestinations = CloneArray(AllowedCommunicationDestinations)
+ AllowedCommunicationSources = CloneArray(AllowedCommunicationSources),
+ AllowedCommunicationDestinations = CloneArray(AllowedCommunicationDestinations)
};
}
private static ComplianceNetworkZone[] CloneArray(ComplianceNetworkZone[] array)
{
- ComplianceNetworkZone[] arrayClone = new ComplianceNetworkZone[array.Length];
- for (int i = 0; i < array.Length; i++)
- {
- arrayClone[i] = (ComplianceNetworkZone)array[i].Clone();
- }
+ ComplianceNetworkZone[] arrayClone = new ComplianceNetworkZone[array.Length];
+ for (int i = 0; i < array.Length; i++)
+ {
+ arrayClone[i] = (ComplianceNetworkZone)array[i].Clone();
+ }
return arrayClone;
- }
+ }
public override bool Equals(object? obj)
{
@@ -136,9 +154,9 @@ public override bool Equals(object? obj)
return ((ComplianceNetworkZone)obj).Id == Id;
}
- public override int GetHashCode()
- {
- return HashCode.Combine(Id);
- }
- }
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(Id);
+ }
+ }
}
diff --git a/roles/lib/files/FWO.Data/CompliancePolicy.cs b/roles/lib/files/FWO.Data/CompliancePolicy.cs
new file mode 100644
index 0000000000..adaae985d7
--- /dev/null
+++ b/roles/lib/files/FWO.Data/CompliancePolicy.cs
@@ -0,0 +1,32 @@
+using Newtonsoft.Json;
+using System.Text.Json.Serialization;
+
+namespace FWO.Data
+{
+ public class CompliancePolicy
+ {
+ [JsonProperty("id"), JsonPropertyName("id")]
+ public int Id { get; set; }
+
+ [JsonProperty("name"), JsonPropertyName("name")]
+ public string Name { get; set; } = "";
+
+ [JsonProperty("created_date"), JsonPropertyName("created_date")]
+ public DateTime CreatedDate { get; set; } = DateTime.Now;
+
+ [JsonProperty("disabled"), JsonPropertyName("disabled")]
+ public bool Disabled { get; set; } = false;
+
+ [JsonProperty("criteria"), JsonPropertyName("criteria")]
+ public List Criteria { get; set; } = [];
+ }
+
+ public struct LinkedPolicy
+ {
+ [JsonProperty("criterion_id"), JsonPropertyName("criterion_id")]
+ public int CriterionId { get; set; }
+
+ [JsonProperty("policy_id"), JsonPropertyName("policy_id")]
+ public int PolicyId { get; set; }
+ }
+}
diff --git a/roles/lib/files/FWO.Data/ComplianceViolation.cs b/roles/lib/files/FWO.Data/ComplianceViolation.cs
new file mode 100644
index 0000000000..2ad086bdd3
--- /dev/null
+++ b/roles/lib/files/FWO.Data/ComplianceViolation.cs
@@ -0,0 +1,111 @@
+using FWO.Basics.Interfaces;
+using Newtonsoft.Json;
+using System.Text.Json.Serialization;
+
+
+namespace FWO.Data
+{
+
+ [Newtonsoft.Json.JsonConverter(typeof(ComplianceViolationConverter))]
+ public class ComplianceViolation : ComplianceViolationBase, IComplianceViolation
+ {
+ [JsonProperty("id"), JsonPropertyName("id")]
+ public int Id { get; set; } = 0;
+
+ public ComplianceViolationType Type { get; set; } = ComplianceViolationType.None;
+
+ public ComplianceViolation()
+ {
+
+ }
+
+ public ComplianceViolation(int id, ComplianceViolationBase baseObj)
+ {
+ Id = id;
+ RuleId = baseObj.RuleId;
+ RuleUid = baseObj.RuleUid;
+ MgmtUid = baseObj.MgmtUid;
+ FoundDate = baseObj.FoundDate;
+ RemovedDate = baseObj.RemovedDate;
+ Details = baseObj.Details;
+ RiskScore = baseObj.RiskScore;
+ PolicyId = baseObj.PolicyId;
+ CriterionId = baseObj.CriterionId;
+ Criterion = baseObj.Criterion;
+ }
+
+
+ public ComplianceViolationType ParseViolationType(ComplianceCriterion? criterion)
+ {
+ if (criterion == null)
+ {
+ return ComplianceViolationType.None;
+ }
+
+ switch (criterion.CriterionType)
+ {
+ case "Matrix":
+ return ComplianceViolationType.MatrixViolation;
+
+ case "Assessability":
+ return ComplianceViolationType.NotAssessable;
+
+ case "ForbiddenService":
+ return ComplianceViolationType.ServiceViolation;
+
+ // TODO : implement for all criterion types
+
+ default:
+ return ComplianceViolationType.None;
+ }
+ }
+ }
+
+ ///
+ /// Insertable base class for compliance violations.
+ /// This class is used to insert new compliance violations into the database.
+ ///
+ public class ComplianceViolationBase
+ {
+ [JsonProperty("rule_id"), JsonPropertyName("rule_id")]
+ public int RuleId { get; set; }
+ [JsonProperty("rule_uid"), JsonPropertyName("rule_uid")]
+ public string RuleUid { get; set; } = "";
+ [JsonProperty("mgmt_uid"), JsonPropertyName("mgmt_uid")]
+ public string MgmtUid { get; set; } = "";
+ [JsonProperty("found_date"), JsonPropertyName("found_date")]
+ public DateTime FoundDate { get; set; } = DateTime.Now;
+ [JsonProperty("removed_date"), JsonPropertyName("removed_date")]
+ public DateTime? RemovedDate { get; set; } = null;
+ [JsonProperty("details"), JsonPropertyName("details")]
+ public string Details { get; set; } = "";
+ [JsonProperty("risk_score"), JsonPropertyName("risk_score")]
+ public long RiskScore { get; set; }
+ [JsonProperty("policy_id"), JsonPropertyName("policy_id")]
+ public int PolicyId { get; set; }
+ [JsonProperty("criterion_id"), JsonPropertyName("criterion_id")]
+ public int CriterionId { get; set; }
+ [JsonProperty("criterion"), JsonPropertyName("criterion")]
+ public ComplianceCriterion? Criterion { get; set; }
+
+ public static ComplianceViolationBase CreateBase(ComplianceViolation violation )
+ {
+ return new()
+ {
+ RuleId = violation.RuleId,
+ RuleUid = violation.RuleUid,
+ MgmtUid = violation.MgmtUid,
+ FoundDate = violation.FoundDate,
+ RemovedDate = violation.RemovedDate,
+ Details = violation.Details,
+ RiskScore = violation.RiskScore,
+ PolicyId = violation.PolicyId,
+ CriterionId = violation.CriterionId,
+ Criterion = violation.Criterion
+ };
+
+ }
+ }
+}
+
+
diff --git a/roles/lib/files/FWO.Data/ComplianceViolationType.cs b/roles/lib/files/FWO.Data/ComplianceViolationType.cs
new file mode 100644
index 0000000000..aaa3f45676
--- /dev/null
+++ b/roles/lib/files/FWO.Data/ComplianceViolationType.cs
@@ -0,0 +1,12 @@
+namespace FWO.Data
+{
+ public enum ComplianceViolationType
+ {
+ None, // rule is compliant
+ NotAssessable, // compliance cant be evaluated (e.g. zone internet)
+ MatrixViolation,
+ ServiceViolation,
+ MultipleViolations
+
+ }
+}
\ No newline at end of file
diff --git a/roles/lib/files/FWO.Data/Device.cs b/roles/lib/files/FWO.Data/Device.cs
index d89d11ba93..3855f88bb1 100644
--- a/roles/lib/files/FWO.Data/Device.cs
+++ b/roles/lib/files/FWO.Data/Device.cs
@@ -9,6 +9,9 @@ public class Device
[JsonProperty("id"), JsonPropertyName("id")]
public int Id { get; set; }
+ [JsonProperty("uid"), JsonPropertyName("uid")]
+ public string? Uid { get; set; }
+
[JsonProperty("name"), JsonPropertyName("name")]
public string? Name { get; set; }
@@ -24,6 +27,9 @@ public class Device
[JsonProperty("global_rulebase_name"), JsonPropertyName("global_rulebase_name")]
public string? GlobalRulebase { get; set; }
+ [JsonProperty("global_rulebase_uid"), JsonPropertyName("global_rulebase_uid")]
+ public string? GlobalRulebaseUid { get; set; }
+
[JsonProperty("package_name"), JsonPropertyName("package_name")]
public string? Package { get; set; }
@@ -36,6 +42,9 @@ public class Device
[JsonProperty("comment"), JsonPropertyName("comment")]
public string? Comment { get; set; }
+ [JsonProperty("rulebase_links"), JsonPropertyName("rulebase_links")]
+ public RulebaseLink[] RulebaseLinks { get; set; } = [];
+
public bool Selected { get; set; } = false;
public bool Relevant { get; set; }
public bool AwaitMgmt { get; set; }
@@ -49,6 +58,7 @@ public Device(Device device)
{
Id = device.Id;
Name = device.Name;
+ Uid = device.Uid;
DeviceType = new DeviceType(device.DeviceType);
Management = new Management(device.Management);
LocalRulebase = device.LocalRulebase;
@@ -63,10 +73,16 @@ public Device(Device device)
ActionId = device.ActionId;
}
+ public bool Equals(Device device)
+ {
+ return Name.GenerousCompare(device.Name) && Uid.GenerousCompare(device.Uid);
+ }
+
public bool Sanitize()
{
bool shortened = false;
Name = Name.SanitizeOpt(ref shortened);
+ Uid = Uid.SanitizeOpt(ref shortened);
LocalRulebase = LocalRulebase.SanitizeOpt(ref shortened);
GlobalRulebase = GlobalRulebase.SanitizeOpt(ref shortened);
Package = Package.SanitizeOpt(ref shortened);
diff --git a/roles/lib/files/FWO.Data/DeviceWrapper.cs b/roles/lib/files/FWO.Data/DeviceWrapper.cs
new file mode 100644
index 0000000000..0525347197
--- /dev/null
+++ b/roles/lib/files/FWO.Data/DeviceWrapper.cs
@@ -0,0 +1,11 @@
+using System.Text.Json.Serialization;
+using Newtonsoft.Json;
+
+namespace FWO.Data
+{
+ public class DeviceWrapper
+ {
+ [JsonProperty("device"), JsonPropertyName("device")]
+ public Device Content { get; set; } = new Device();
+ }
+}
diff --git a/roles/lib/files/FWO.Data/DisplayBase.cs b/roles/lib/files/FWO.Data/DisplayBase.cs
index 44c695c481..8557ee368d 100644
--- a/roles/lib/files/FWO.Data/DisplayBase.cs
+++ b/roles/lib/files/FWO.Data/DisplayBase.cs
@@ -8,6 +8,12 @@ namespace FWO.Data
{
public static class DisplayBase
{
+ public static StringBuilder DisplayGateway(Device gateway, bool isTechReport, string? gatewayName = null)
+ {
+ StringBuilder result = new ();
+ result.Append($" {gateway.Name}");
+ return result;
+ }
public static StringBuilder DisplayService(NetworkService service, bool isTechReport, string? serviceName = null)
{
StringBuilder result = new ();
@@ -143,12 +149,31 @@ public static string DisplayIp(string ip1, string ip2, string nwObjType, bool in
result = inBrackets ? " (" : "";
if (nwObjType == ObjectType.Network)
{
- if(IpStart.GetNetmask() == "")
+ if (IpStart.GetNetmask() == "")
{
- IPAddressRange ipRange = new (IPAddress.Parse(IpStart), IPAddress.Parse(IpEnd));
+ if (IpStart.IsGreater(IpEnd))
+ {
+ // swap
+ Log.WriteWarning("Ip displaying", $"Wrong ip format {IpStart} - {IpEnd} - swapping values");
+ string temp = IpStart;
+ IpStart = IpEnd;
+ IpEnd = temp;
+ }
+ IPAddressRange ipRange = new(IPAddress.Parse(IpStart), IPAddress.Parse(IpEnd));
if (ipRange != null)
{
- result += ipRange.ToCidrString();
+ try
+ {
+ // tryint to convert range to network
+ string rangeString = ipRange.ToCidrString();
+ result += rangeString;
+ }
+ catch (Exception exc)
+ {
+ Log.WriteWarning("Ip displaying", $"Wrong ip format {IpStart} - {IpEnd} is not a network\nMessage: {exc.Message}");
+ // we display the incorrect ip data nevertheless without throwing errors
+ result += $"{IpStart}-{IpEnd}";
+ }
}
}
else
@@ -168,7 +193,10 @@ public static string DisplayIp(string ip1, string ip2, string nwObjType, bool in
}
catch (Exception exc)
{
- Log.WriteError("Ip displaying", $"Wrong ip format {IpStart} - {IpEnd}\nMessage: {exc.Message}");
+ Log.WriteWarning("Ip displaying", $"Wrong ip format {IpStart} - {IpEnd}\nMessage: {exc.Message}");
+ // we display the incorrect ip data nevertheless without throwing errors
+ result += $"{IpStart}-{IpEnd}";
+ result += inBrackets ? ")" : "";
}
}
}
@@ -235,7 +263,7 @@ public static string DisplayIpRange(string ip1, string ip2, bool inBrackets = fa
return result;
}
- public static string DisplayPort(int? port, int? portEnd, bool inBrackets = false)
+ public static string DisplayPort(int? port, int? portEnd, bool inBrackets = false, string? proto = null)
{
string result = "";
if (port != null)
@@ -249,6 +277,7 @@ public static string DisplayPort(int? port, int? portEnd, bool inBrackets = fals
{
result += $"{port}-{portEnd}";
}
+ result += proto != null ? $"/{proto}" : "";
result += inBrackets ? ")" : "";
}
return result;
diff --git a/roles/lib/files/FWO.Data/Extensions/ComplianceExtensions.cs b/roles/lib/files/FWO.Data/Extensions/ComplianceExtensions.cs
new file mode 100644
index 0000000000..516e188a47
--- /dev/null
+++ b/roles/lib/files/FWO.Data/Extensions/ComplianceExtensions.cs
@@ -0,0 +1,19 @@
+using FWO.Basics.Enums;
+
+namespace FWO.Data.Extensions
+{
+ public static partial class ComplianceExtensions
+ {
+ public static string ToAssessabilityIssueString(this AssessabilityIssue assessabilityIssue)
+ {
+ return assessabilityIssue switch
+ {
+ AssessabilityIssue.IPNull => "assess_ip_null",
+ AssessabilityIssue.AllIPs => "assess_all_ips",
+ AssessabilityIssue.HostAddress => "assess_host_address",
+ AssessabilityIssue.Broadcast => "assess_broadcast",
+ _ => throw new NotImplementedException()
+ };
+ }
+ }
+}
diff --git a/roles/lib/files/FWO.Data/FwoOwner.cs b/roles/lib/files/FWO.Data/FwoOwner.cs
index 2450d4cb23..616756b820 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/JsonCustomConverters.cs b/roles/lib/files/FWO.Data/JsonCustomConverters.cs
index ff5e0a0e85..1b940b7e50 100644
--- a/roles/lib/files/FWO.Data/JsonCustomConverters.cs
+++ b/roles/lib/files/FWO.Data/JsonCustomConverters.cs
@@ -1,14 +1,8 @@
-using NetTools;
+
+using NetTools;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
using System.Net;
-using System.Text;
-using System.Text.Json.Nodes;
-using System.Threading.Tasks;
namespace FWO.Data
{
@@ -59,7 +53,7 @@ public override IPAddressRange ReadJson(JsonReader reader, Type objectType, IPAd
// Load the JSON as a JObject
JObject jsonObject = JObject.Load(reader);
// Deserialize the IP address range based on the properties ip_range_start and ip_range_end
- IPAddress start = IPAddress.Parse((jsonObject.GetValue("ip_range_start")?.ToObject() ?? throw new ArgumentNullException("ip_range_start")).Replace("/32", ""));
+ IPAddress start = IPAddress.Parse((jsonObject.GetValue("ip_range_start")?.ToObject() ?? throw new ArgumentNullException("ip_range_start")).Replace("/32", ""));
IPAddress end = IPAddress.Parse((jsonObject.GetValue("ip_range_end")?.ToObject() ?? throw new ArgumentNullException("ip_range_start")).Replace("/32", ""));
return new IPAddressRange(start, end);
}
@@ -79,4 +73,59 @@ public override void WriteJson(JsonWriter writer, IPAddressRange? value, JsonSer
}
}
}
+
+ public class ComplianceViolationConverter : JsonConverter
+ {
+ public override ComplianceViolation? ReadJson(JsonReader reader, Type objectType, ComplianceViolation? existingValue, bool hasExistingValue, JsonSerializer serializer)
+ {
+ ComplianceViolation? violation = null;
+
+ if (reader.TokenType != JsonToken.Null)
+ {
+ // Try deserialize base.
+
+ JObject jsonObject = JObject.Load(reader);
+ ComplianceViolationBase? violationBase = jsonObject.ToObject(serializer);
+
+ if (violationBase != null)
+ {
+ // Get id from json object.
+
+ int id = jsonObject.GetValue("id")?.ToObject() ?? 0;
+
+ // Create instance from base and set id.
+
+ violation = new(id, violationBase);
+
+ // Parse Violation Type via criterion.
+
+ violation.Type = violation.ParseViolationType(violation.Criterion);
+ }
+ }
+
+ return violation;
+ }
+
+ public override void WriteJson(JsonWriter writer, ComplianceViolation? value, JsonSerializer serializer)
+ {
+ if (value == null)
+ {
+ writer.WriteNull();
+ return;
+ }
+
+ if (value.Criterion != null)
+ {
+ value.Criterion.CriterionType = value.Type switch
+ {
+ ComplianceViolationType.MatrixViolation => "Matrix",
+ ComplianceViolationType.NotAssessable => "Assessability",
+ ComplianceViolationType.ServiceViolation => "ForbiddenService",
+ _ => value.Criterion.CriterionType
+ };
+ }
+
+ serializer.Serialize(writer, value);
+ }
+ }
}
diff --git a/roles/lib/files/FWO.Data/LinkType.cs b/roles/lib/files/FWO.Data/LinkType.cs
new file mode 100644
index 0000000000..0f39031306
--- /dev/null
+++ b/roles/lib/files/FWO.Data/LinkType.cs
@@ -0,0 +1,15 @@
+
+using System.Text.Json.Serialization;
+using Newtonsoft.Json;
+
+namespace FWO.Data
+{
+ public class LinkType
+ {
+ [JsonProperty("id"), JsonPropertyName("id")]
+ public int Id { get; set; }
+
+ [JsonProperty("name"), JsonPropertyName("name")]
+ public string Name { get; set; } = "";
+ }
+}
\ No newline at end of file
diff --git a/roles/lib/files/FWO.Data/Management.cs b/roles/lib/files/FWO.Data/Management.cs
index a72158e3d8..57dbd1a0ba 100644
--- a/roles/lib/files/FWO.Data/Management.cs
+++ b/roles/lib/files/FWO.Data/Management.cs
@@ -21,6 +21,9 @@ public class Management
[JsonProperty("name"), JsonPropertyName("name")]
public string Name { get; set; } = "";
+ [JsonProperty("uid"), JsonPropertyName("uid")]
+ public string? Uid { get; set; } = "";
+
[JsonProperty("hostname"), JsonPropertyName("hostname")]
public string Hostname { get; set; } = "";
@@ -39,11 +42,14 @@ public class Management
[JsonProperty("cloudTenantId"), JsonPropertyName("cloudTenantId")]
public string? CloudTenantId { get; set; } = "";
- [JsonProperty("superManager"), JsonPropertyName("superManager")]
+ [JsonProperty("multi_device_manager_id"), JsonPropertyName("multi_device_manager_id")]
public int? SuperManagerId { get; set; }
+ [JsonProperty("is_super_manager"), JsonPropertyName("is_super_manager")]
+ public bool IsSupermanager { get; set; } = false;
+
[JsonProperty("importerHostname"), JsonPropertyName("importerHostname")]
- public string ImporterHostname { get; set; } = "";
+ public string? ImporterHostname { get; set; } = "";
[JsonProperty("port"), JsonPropertyName("port")]
public int Port { get; set; }
@@ -66,6 +72,21 @@ public class Management
[JsonProperty("devices"), JsonPropertyName("devices")]
public Device[] Devices { get; set; } = [];
+ [JsonProperty("rulebases"), JsonPropertyName("rulebases")]
+ public Rulebase[] Rulebases { get; set; } = [];
+
+ [JsonProperty("networkObjects"), JsonPropertyName("networkObjects")]
+ public NetworkObject[] Objects { get; set; } = [];
+
+ [JsonProperty("serviceObjects"), JsonPropertyName("serviceObjects")]
+ public NetworkService[] Services { get; set; } = [];
+
+ [JsonProperty("userObjects"), JsonPropertyName("userObjects")]
+ public NetworkUser[] Users { get; set; } = [];
+
+ [JsonProperty("zoneObjects"), JsonPropertyName("zoneObjects")]
+ public NetworkZone[] Zones { get; set; } = [];
+
[JsonProperty("deviceType"), JsonPropertyName("deviceType")]
public DeviceType DeviceType { get; set; } = new();
@@ -75,6 +96,14 @@ public class Management
[JsonProperty("extMgtData"), JsonPropertyName("extMgtData")]
public string? ExtMgtData { get; set; }
+ // only relevant for super managers, normal managers do not have rulebases
+ [JsonProperty("rulebase_name"), JsonPropertyName("rulebase_name")]
+ public string? RulebaseName { get; set; }
+
+ // only relevant for super managers, normal managers do not have rulebases
+ [JsonProperty("rulebase_uid"), JsonPropertyName("rulebase_uid")]
+ public string? RulebaseUid { get; set; }
+
public long? RelevantImportId { get; set; }
public bool Ignore { get; set; }
public bool AwaitDevice { get; set; }
@@ -82,12 +111,13 @@ public class Management
public long ActionId { get; set; }
public Management()
- {}
+ { }
public Management(Management management)
{
Id = management.Id;
Name = management.Name;
+ Uid = management.Uid;
Hostname = management.Hostname;
if (management.ImportCredential != null)
ImportCredential = new ImportCredential(management.ImportCredential);
@@ -126,15 +156,36 @@ public string Host()
{
return Hostname + ":" + Port;
}
-
+
+ public bool Equals(Management management)
+ {
+ return Uid.GenerousCompare(management.Uid) &&
+ Name.GenerousCompare(management.Name) &&
+ Hostname.GenerousCompare(management.Hostname) &&
+ ConfigPath.GenerousCompare(management.ConfigPath) &&
+ DomainUid.GenerousCompare(management.DomainUid) &&
+ CloudSubscriptionId.GenerousCompare(management.CloudSubscriptionId) &&
+ CloudTenantId.GenerousCompare(management.CloudTenantId) &&
+ SuperManagerId == management.SuperManagerId &&
+ Port == management.Port;
+ }
+
public virtual bool Sanitize()
{
bool shortened = false;
Name = Name.SanitizeMand(ref shortened);
+ Uid = Uid.SanitizeOpt(ref shortened);
Hostname = Hostname.SanitizeMand(ref shortened);
ConfigPath = ConfigPath.SanitizeOpt(ref shortened);
DomainUid = DomainUid.SanitizeOpt(ref shortened);
- ImporterHostname = ImporterHostname.SanitizeMand(ref shortened);
+ if (ImporterHostname != null)
+ {
+ ImporterHostname = ImporterHostname.SanitizeMand(ref shortened);
+ }
+ else
+ {
+ ImporterHostname = "";
+ }
Comment = Comment.SanitizeCommentOpt(ref shortened);
CloudSubscriptionId = CloudSubscriptionId.SanitizeOpt(ref shortened);
CloudTenantId = CloudTenantId.SanitizeOpt(ref shortened);
diff --git a/roles/lib/files/FWO.Data/Middleware/ComplianceParameters.cs b/roles/lib/files/FWO.Data/Middleware/ComplianceParameters.cs
new file mode 100644
index 0000000000..49bffd5731
--- /dev/null
+++ b/roles/lib/files/FWO.Data/Middleware/ComplianceParameters.cs
@@ -0,0 +1,15 @@
+namespace FWO.Data.Middleware
+{
+ public class ComplianceReportParameters
+ {
+ public List ManagementIds { get; set; } = [];
+ }
+
+ public class ComplianceImportMatrixParameters
+ {
+ public string FileName { get; set; } = "";
+ public string Data { get; set; } = "";
+ public string UserName { get; set; } = "";
+ public string UserDn { get; set; } = "";
+ }
+}
diff --git a/roles/lib/files/FWO.Data/Middleware/DebugConfig.cs b/roles/lib/files/FWO.Data/Middleware/DebugConfig.cs
new file mode 100644
index 0000000000..7207588ece
--- /dev/null
+++ b/roles/lib/files/FWO.Data/Middleware/DebugConfig.cs
@@ -0,0 +1,21 @@
+using Newtonsoft.Json;
+using System.Text.Json.Serialization;
+
+namespace FWO.Data.Middleware
+{
+ public class DebugConfig
+ {
+ [JsonProperty("debugLevel"), JsonPropertyName("debugLevel")]
+ public int DebugLevel { get; set; } = 0;
+
+ [JsonProperty("extendedLogComplianceCheck"), JsonPropertyName("extendedLogComplianceCheck")]
+ public bool ExtendedLogComplianceCheck { get; set; } = false;
+
+ [JsonProperty("extendedLogReportGeneration"), JsonPropertyName("extendedLogReportGeneration")]
+ public bool ExtendedLogReportGeneration { get; set; } = false;
+
+ [JsonProperty("extendedLogScheduler"), JsonPropertyName("extendedLogScheduler")]
+ public bool ExtendedLogScheduler { get; set; } = false;
+
+ }
+}
diff --git a/roles/lib/files/FWO.Data/Middleware/NormalizedConfigParameters.cs b/roles/lib/files/FWO.Data/Middleware/NormalizedConfigParameters.cs
new file mode 100644
index 0000000000..7946032c0f
--- /dev/null
+++ b/roles/lib/files/FWO.Data/Middleware/NormalizedConfigParameters.cs
@@ -0,0 +1,14 @@
+
+using System.Text.Json.Serialization;
+
+namespace FWO.Data.Middleware
+{
+ public class NormalizedConfigGetParameters
+ {
+ [JsonPropertyName("mgm-ids")]
+ public int[] ManagementIds { get; set; } = [];
+
+ [JsonPropertyName("config-time")]
+ public string? ConfigTime { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/roles/lib/files/FWO.Data/Modelling/ModellingVarianceResult.cs b/roles/lib/files/FWO.Data/Modelling/ModellingVarianceResult.cs
index 0f9d5c3b6c..11db714db7 100644
--- a/roles/lib/files/FWO.Data/Modelling/ModellingVarianceResult.cs
+++ b/roles/lib/files/FWO.Data/Modelling/ModellingVarianceResult.cs
@@ -29,6 +29,8 @@ public class ModellingVarianceResult
public Dictionary> MissingAppRoles { get; set; } = [];
public Dictionary> DifferingAppRoles { get; set; } = [];
public AppRoleStats AppRoleStats { get; set; } = new();
+ public Dictionary> DeviceRules { get; set; } = [];
+
public List UnModelledRulesReport { get; set; } = [];
@@ -84,25 +86,31 @@ private List MgtDataToReport(Dictionary