From c5dcd3385deb2b718feedf949b4c26c161bb7e7f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 Apr 2024 21:54:23 +0000 Subject: [PATCH 01/23] Bump GraphQL.Client.Serializer.SystemTextJson in /roles Bumps [GraphQL.Client.Serializer.SystemTextJson](https://github.com/graphql-dotnet/graphql-client) from 6.0.3 to 6.0.5. - [Release notes](https://github.com/graphql-dotnet/graphql-client/releases) - [Commits](https://github.com/graphql-dotnet/graphql-client/compare/v6.0.3...v6.0.5) --- updated-dependencies: - dependency-name: GraphQL.Client.Serializer.SystemTextJson dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- roles/lib/files/FWO.Api.Client/FWO.Api.Client.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/lib/files/FWO.Api.Client/FWO.Api.Client.csproj b/roles/lib/files/FWO.Api.Client/FWO.Api.Client.csproj index 8ab351dda..d0210bb38 100644 --- a/roles/lib/files/FWO.Api.Client/FWO.Api.Client.csproj +++ b/roles/lib/files/FWO.Api.Client/FWO.Api.Client.csproj @@ -10,7 +10,7 @@ - + From c232e6853dd1cfcf15f3071e42318a77f78d1101 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 Apr 2024 21:55:45 +0000 Subject: [PATCH 02/23] Bump Haukcode.WkHtmlToPdfDotNet from 1.5.88 to 1.5.90 in /roles Bumps [Haukcode.WkHtmlToPdfDotNet](https://github.com/HakanL/WkHtmlToPdf-DotNet) from 1.5.88 to 1.5.90. - [Release notes](https://github.com/HakanL/WkHtmlToPdf-DotNet/releases) - [Commits](https://github.com/HakanL/WkHtmlToPdf-DotNet/compare/v1.5.88...v1.5.90) --- updated-dependencies: - dependency-name: Haukcode.WkHtmlToPdfDotNet dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- roles/lib/files/FWO.Report/FWO.Report.csproj | 2 +- roles/test/files/FWO.Test/FWO.Test.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/roles/lib/files/FWO.Report/FWO.Report.csproj b/roles/lib/files/FWO.Report/FWO.Report.csproj index c2cdf7173..9a3a64891 100644 --- a/roles/lib/files/FWO.Report/FWO.Report.csproj +++ b/roles/lib/files/FWO.Report/FWO.Report.csproj @@ -7,7 +7,7 @@ - + diff --git a/roles/test/files/FWO.Test/FWO.Test.csproj b/roles/test/files/FWO.Test/FWO.Test.csproj index f4df8c8a6..b79a2a8c0 100644 --- a/roles/test/files/FWO.Test/FWO.Test.csproj +++ b/roles/test/files/FWO.Test/FWO.Test.csproj @@ -8,7 +8,7 @@ - + From 9e64b8ee5453a006128e2b970d0a020253c20ce1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 Apr 2024 21:56:24 +0000 Subject: [PATCH 03/23] Bump GraphQL.Client from 6.0.3 to 6.0.5 in /roles Bumps [GraphQL.Client](https://github.com/graphql-dotnet/graphql-client) from 6.0.3 to 6.0.5. - [Release notes](https://github.com/graphql-dotnet/graphql-client/releases) - [Commits](https://github.com/graphql-dotnet/graphql-client/compare/v6.0.3...v6.0.5) --- updated-dependencies: - dependency-name: GraphQL.Client dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- roles/lib/files/FWO.Api.Client/FWO.Api.Client.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/lib/files/FWO.Api.Client/FWO.Api.Client.csproj b/roles/lib/files/FWO.Api.Client/FWO.Api.Client.csproj index 8ab351dda..baac72be8 100644 --- a/roles/lib/files/FWO.Api.Client/FWO.Api.Client.csproj +++ b/roles/lib/files/FWO.Api.Client/FWO.Api.Client.csproj @@ -8,7 +8,7 @@ - + From 58127241312239f41a278893b90d5d8593fffca2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 14:41:55 +0000 Subject: [PATCH 04/23] Bump GraphQL.Client.Serializer.Newtonsoft from 6.0.3 to 6.0.5 in /roles Bumps [GraphQL.Client.Serializer.Newtonsoft](https://github.com/graphql-dotnet/graphql-client) from 6.0.3 to 6.0.5. - [Release notes](https://github.com/graphql-dotnet/graphql-client/releases) - [Commits](https://github.com/graphql-dotnet/graphql-client/compare/v6.0.3...v6.0.5) --- updated-dependencies: - dependency-name: GraphQL.Client.Serializer.Newtonsoft dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- roles/lib/files/FWO.Api.Client/FWO.Api.Client.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/lib/files/FWO.Api.Client/FWO.Api.Client.csproj b/roles/lib/files/FWO.Api.Client/FWO.Api.Client.csproj index baac72be8..3025a75e9 100644 --- a/roles/lib/files/FWO.Api.Client/FWO.Api.Client.csproj +++ b/roles/lib/files/FWO.Api.Client/FWO.Api.Client.csproj @@ -9,7 +9,7 @@ - + From f8618cf6e6f216d817148ba3419ee3c85c1c4a08 Mon Sep 17 00:00:00 2001 From: Tim Purschke Date: Wed, 1 May 2024 17:07:54 +0200 Subject: [PATCH 05/23] update github test installs --- .github/workflows/test-install.yml | 34 +++++++++++++++++++++++------- roles/common/tasks/main.yml | 16 +------------- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/.github/workflows/test-install.yml b/.github/workflows/test-install.yml index 9eb0b10d4..437e5461f 100644 --- a/.github/workflows/test-install.yml +++ b/.github/workflows/test-install.yml @@ -30,13 +30,13 @@ jobs: # - name: do test install in case of merged pull request # run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook -e run_on_github=yes --skip-tags test site.yml -K - test_ubuntu_20: - name: test build on ubuntu_20 - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v3 - - name: do test install in case of merged pull request - run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook -e run_on_github=yes site.yml -K + # test_ubuntu_20: + # name: test build on ubuntu_20 + # runs-on: ubuntu-20.04 + # steps: + # - uses: actions/checkout@v3 + # - name: do test install in case of merged pull request + # run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook -e run_on_github=yes site.yml -K # run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook -e run_on_github=yes --skip-tags test site.yml -K # test_ubuntu_22: @@ -46,4 +46,22 @@ jobs: # - uses: actions/checkout@v3 # - name: do test install in case of merged pull request # run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook -e run_on_github=yes site.yml -K - # run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook -e run_on_github=yes --skip-tags test site.yml -K + + # test_ubuntu_latest: + # name: test build on ubuntu latest + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v3 + # - name: do test install in case of merged pull request + # run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook -e run_on_github=yes site.yml -K + + test_ubuntu: + name: test build on {{ item }} + runs-on: "{{ item }}" + steps: + - uses: actions/checkout@v3 + - name: do test install in case of merged pull request + run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook -e run_on_github=yes site.yml -K + loop: + - ubuntu-20.04 + - ubuntu-22.04 diff --git a/roles/common/tasks/main.yml b/roles/common/tasks/main.yml index 72f6eacad..64064644a 100644 --- a/roles/common/tasks/main.yml +++ b/roles/common/tasks/main.yml @@ -1,11 +1,10 @@ - block: - - name: assert ansible version gt 2.13 + - 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 }}" @@ -97,19 +96,6 @@ (ansible_facts['distribution'] == "Ubuntu" or ansible_facts['distribution'] == "Debian") and upgradable_packages.stdout_lines|length > 1 - - # - name: fix grub-efi (for github actions) - # apt: - # upgrade: dist - # update_cache: true - # when: ansible_facts['distribution'] == "Ubuntu" or ansible_facts['distribution'] == "Debian" and run_on_github|bool - - # - name: update operating system packages .deb based (for github actions) - # apt: - # upgrade: dist - # update_cache: true - # when: ansible_facts['distribution'] == "Ubuntu" or ansible_facts['distribution'] == "Debian" and run_on_github|bool - - name: update operating system packages .rpm based (untested) yum: upgrade: dist From 5aa69658c6c9e60e0976112c1234c0298b2494df Mon Sep 17 00:00:00 2001 From: Tim Purschke Date: Wed, 1 May 2024 17:15:12 +0200 Subject: [PATCH 06/23] fix syntax --- .github/workflows/test-install.yml | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/.github/workflows/test-install.yml b/.github/workflows/test-install.yml index 437e5461f..02acffe81 100644 --- a/.github/workflows/test-install.yml +++ b/.github/workflows/test-install.yml @@ -47,21 +47,10 @@ jobs: # - name: do test install in case of merged pull request # run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook -e run_on_github=yes site.yml -K - # test_ubuntu_latest: - # name: test build on ubuntu latest - # runs-on: ubuntu-latest - # steps: - # - uses: actions/checkout@v3 - # - name: do test install in case of merged pull request - # run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook -e run_on_github=yes site.yml -K - - test_ubuntu: - name: test build on {{ item }} - runs-on: "{{ item }}" + test_ubuntu_latest: + name: test build on ubuntu latest + runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: do test install in case of merged pull request run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook -e run_on_github=yes site.yml -K - loop: - - ubuntu-20.04 - - ubuntu-22.04 From 1bd11384977adae7c211837b15766e40141ce858 Mon Sep 17 00:00:00 2001 From: Tim Purschke Date: Wed, 1 May 2024 17:26:09 +0200 Subject: [PATCH 07/23] testing with all code on github --- .github/workflows/test-install.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-install.yml b/.github/workflows/test-install.yml index 02acffe81..3ee0ac4c9 100644 --- a/.github/workflows/test-install.yml +++ b/.github/workflows/test-install.yml @@ -53,4 +53,5 @@ jobs: steps: - uses: actions/checkout@v3 - name: do test install in case of merged pull request - run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook -e run_on_github=yes site.yml -K + run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook -e run_on_github=false site.yml -K + From 3d8c2b42e30bcd4798ad5824ee15977f4dae3f35 Mon Sep 17 00:00:00 2001 From: Tim Purschke Date: Wed, 1 May 2024 17:40:26 +0200 Subject: [PATCH 08/23] force install on github test --- .github/workflows/test-install.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-install.yml b/.github/workflows/test-install.yml index 3ee0ac4c9..020d33a4b 100644 --- a/.github/workflows/test-install.yml +++ b/.github/workflows/test-install.yml @@ -53,5 +53,5 @@ jobs: steps: - uses: actions/checkout@v3 - name: do test install in case of merged pull request - run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook -e run_on_github=false site.yml -K + run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook -e run_on_github=false -e force_install=true site.yml -K From cb150a1e1cd40b2affa3944eaacb66ce7841f7ad Mon Sep 17 00:00:00 2001 From: Tim Purschke Date: Wed, 1 May 2024 17:51:05 +0200 Subject: [PATCH 09/23] remove run_on_github flag not needed anymore --- .github/workflows/test-install.yml | 10 +++++----- inventory/group_vars/all.yml | 1 - roles/common/tasks/main.yml | 2 +- roles/test/tasks/main.yml | 1 - roles/test/tasks/test-auth.yml | 1 - 5 files changed, 6 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test-install.yml b/.github/workflows/test-install.yml index 020d33a4b..15aa78416 100644 --- a/.github/workflows/test-install.yml +++ b/.github/workflows/test-install.yml @@ -28,7 +28,7 @@ jobs: # steps: # - uses: actions/checkout@v3 # - name: do test install in case of merged pull request - # run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook -e run_on_github=yes --skip-tags test site.yml -K + # run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook --skip-tags test site.yml -K # test_ubuntu_20: # name: test build on ubuntu_20 @@ -36,8 +36,8 @@ jobs: # steps: # - uses: actions/checkout@v3 # - name: do test install in case of merged pull request - # run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook -e run_on_github=yes site.yml -K -# run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook -e run_on_github=yes --skip-tags test site.yml -K + # run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook site.yml -K +# run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook --skip-tags test site.yml -K # test_ubuntu_22: # name: test build on ubuntu_22 @@ -45,7 +45,7 @@ jobs: # steps: # - uses: actions/checkout@v3 # - name: do test install in case of merged pull request - # run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook -e run_on_github=yes site.yml -K + # run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook site.yml -K test_ubuntu_latest: name: test build on ubuntu latest @@ -53,5 +53,5 @@ jobs: steps: - uses: actions/checkout@v3 - name: do test install in case of merged pull request - run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook -e run_on_github=false -e force_install=true site.yml -K + run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook -e force_install=true site.yml -K diff --git a/inventory/group_vars/all.yml b/inventory/group_vars/all.yml index 80fd8cfe8..cf21de7d9 100644 --- a/inventory/group_vars/all.yml +++ b/inventory/group_vars/all.yml @@ -22,7 +22,6 @@ sample_hostname: "{{ groups['sampleserver'].0 }}" # upgrade - installs on top of an existing system preserving any existing data in ldap, database, api installation_mode: new install_syslog: true -run_on_github: false add_demo_data: true api_docu: false force_install: false diff --git a/roles/common/tasks/main.yml b/roles/common/tasks/main.yml index 64064644a..5ec90ac8b 100644 --- a/roles/common/tasks/main.yml +++ b/roles/common/tasks/main.yml @@ -92,7 +92,7 @@ - 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 not run_on_github|bool and + not force_install|bool and (ansible_facts['distribution'] == "Ubuntu" or ansible_facts['distribution'] == "Debian") and upgradable_packages.stdout_lines|length > 1 diff --git a/roles/test/tasks/main.yml b/roles/test/tasks/main.yml index 526b04450..a02f13b80 100644 --- a/roles/test/tasks/main.yml +++ b/roles/test/tasks/main.yml @@ -62,7 +62,6 @@ - name: auth testing import_tasks: test-auth.yml - when: "not run_on_github|bool" - name: api testing import_tasks: test-api.yml diff --git a/roles/test/tasks/test-auth.yml b/roles/test/tasks/test-auth.yml index dd400917c..b91352757 100644 --- a/roles/test/tasks/test-auth.yml +++ b/roles/test/tasks/test-auth.yml @@ -7,7 +7,6 @@ connect_timeout: 1 delay: 10 timeout: 25 - when: "not run_on_github|bool" - name: middleware test get jwt valid creds uri: From 5569bb384a568f3a9c17e0b9381a1f681f405479 Mon Sep 17 00:00:00 2001 From: Tim Purschke Date: Wed, 1 May 2024 17:52:45 +0200 Subject: [PATCH 10/23] test all ubuntu versions in github --- .github/workflows/test-install.yml | 55 +++++++++++++++--------------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/.github/workflows/test-install.yml b/.github/workflows/test-install.yml index 15aa78416..80831ad38 100644 --- a/.github/workflows/test-install.yml +++ b/.github/workflows/test-install.yml @@ -21,37 +21,36 @@ on: # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: - # ubuntu18 was unstable at github (2022-07-06 - 2022-07-11) - # test_ubuntu_18: - # name: test build on ubuntu_18 - # runs-on: ubuntu-18.04 - # steps: - # - uses: actions/checkout@v3 - # - name: do test install in case of merged pull request - # run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook --skip-tags test site.yml -K - - # test_ubuntu_20: - # name: test build on ubuntu_20 - # runs-on: ubuntu-20.04 - # steps: - # - uses: actions/checkout@v3 - # - name: do test install in case of merged pull request - # run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook site.yml -K -# run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook --skip-tags test site.yml -K + ubuntu18 was unstable at github (2022-07-06 - 2022-07-11) + test_ubuntu_18: + name: test build on ubuntu_18 + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v3 + - name: do test install in case of merged pull request + run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook site.yml -K - # test_ubuntu_22: - # name: test build on ubuntu_22 - # runs-on: ubuntu-22.04 - # steps: - # - uses: actions/checkout@v3 - # - name: do test install in case of merged pull request - # run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook site.yml -K + test_ubuntu_20: + name: test build on ubuntu_20 + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v3 + - name: do test install in case of merged pull request + run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook site.yml -K - test_ubuntu_latest: - name: test build on ubuntu latest - runs-on: ubuntu-latest + test_ubuntu_22: + name: test build on ubuntu_22 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 - name: do test install in case of merged pull request - run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook -e force_install=true site.yml -K + run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook site.yml -K + + # test_ubuntu_latest: + # name: test build on ubuntu latest + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@v3 + # - name: do test install in case of merged pull request + # run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook -e force_install=true site.yml -K From a226c2789fa1c9400abccf826a29120a8ba0615a Mon Sep 17 00:00:00 2001 From: Tim Purschke Date: Wed, 1 May 2024 17:54:13 +0200 Subject: [PATCH 11/23] fix --- .github/workflows/test-install.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-install.yml b/.github/workflows/test-install.yml index 80831ad38..1e2a6a4a7 100644 --- a/.github/workflows/test-install.yml +++ b/.github/workflows/test-install.yml @@ -21,7 +21,7 @@ on: # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: - ubuntu18 was unstable at github (2022-07-06 - 2022-07-11) + # ubuntu18 was unstable at github (2022-07-06 - 2022-07-11) test_ubuntu_18: name: test build on ubuntu_18 runs-on: ubuntu-18.04 @@ -46,7 +46,7 @@ jobs: - name: do test install in case of merged pull request run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook site.yml -K - # test_ubuntu_latest: + ## test_ubuntu_latest: # name: test build on ubuntu latest # runs-on: ubuntu-latest # steps: From 371e23c7444fe37151e9f1fad9ce7fc851ce0e6b Mon Sep 17 00:00:00 2001 From: Tim Purschke Date: Wed, 1 May 2024 17:56:53 +0200 Subject: [PATCH 12/23] fix2 --- .github/workflows/test-install.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-install.yml b/.github/workflows/test-install.yml index 1e2a6a4a7..adf257b61 100644 --- a/.github/workflows/test-install.yml +++ b/.github/workflows/test-install.yml @@ -28,7 +28,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: do test install in case of merged pull request - run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook site.yml -K + run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook -e force_install=true site.yml -K test_ubuntu_20: name: test build on ubuntu_20 @@ -36,7 +36,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: do test install in case of merged pull request - run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook site.yml -K + run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook -e force_install=true site.yml -K test_ubuntu_22: name: test build on ubuntu_22 @@ -44,7 +44,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: do test install in case of merged pull request - run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook site.yml -K + run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook -e force_install=true site.yml -K ## test_ubuntu_latest: # name: test build on ubuntu latest From 66014a8e71b2ebc9c188918854db3a410b77c6d6 Mon Sep 17 00:00:00 2001 From: Tim Purschke Date: Wed, 1 May 2024 18:05:56 +0200 Subject: [PATCH 13/23] back to only testing latest --- .github/workflows/test-install.yml | 46 +++++++++++++----------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/.github/workflows/test-install.yml b/.github/workflows/test-install.yml index adf257b61..b748868c3 100644 --- a/.github/workflows/test-install.yml +++ b/.github/workflows/test-install.yml @@ -21,36 +21,30 @@ on: # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: - # ubuntu18 was unstable at github (2022-07-06 - 2022-07-11) - test_ubuntu_18: - name: test build on ubuntu_18 - runs-on: ubuntu-18.04 - steps: - - uses: actions/checkout@v3 - - name: do test install in case of merged pull request - run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook -e force_install=true site.yml -K + # # ubuntu18 was unstable at github (2022-07-06 - 2022-07-11) + # # does not seem to be supported by hithub anymore (2024-05-01) - test_ubuntu_20: - name: test build on ubuntu_20 - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v3 - - name: do test install in case of merged pull request - run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook -e force_install=true site.yml -K - - test_ubuntu_22: - name: test build on ubuntu_22 - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v3 - - name: do test install in case of merged pull request - run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook -e force_install=true site.yml -K + # test_ubuntu_20: + # name: test build on ubuntu_20 + # runs-on: ubuntu-20.04 + # steps: + # - uses: actions/checkout@v3 + # - name: do test install in case of merged pull request + # run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook -e force_install=true site.yml -K - ## test_ubuntu_latest: - # name: test build on ubuntu latest - # runs-on: ubuntu-latest + # test_ubuntu_22: + # name: test build on ubuntu_22 + # runs-on: ubuntu-22.04 # steps: # - uses: actions/checkout@v3 # - name: do test install in case of merged pull request # run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook -e force_install=true site.yml -K + # test_ubuntu_latest: + name: test build on ubuntu latest + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: do test install in case of merged pull request + run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook -e force_install=true site.yml -K + From 76ff619e17fbb67a8da8df9ea9245df4c0857d58 Mon Sep 17 00:00:00 2001 From: Tim Purschke Date: Wed, 1 May 2024 18:07:20 +0200 Subject: [PATCH 14/23] fix3 --- .github/workflows/test-install.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-install.yml b/.github/workflows/test-install.yml index b748868c3..2165aa47b 100644 --- a/.github/workflows/test-install.yml +++ b/.github/workflows/test-install.yml @@ -40,7 +40,7 @@ jobs: # - name: do test install in case of merged pull request # run: cd /home/runner/work/firewall-orchestrator/firewall-orchestrator && ansible-playbook -e force_install=true site.yml -K - # test_ubuntu_latest: + test_ubuntu_latest: name: test build on ubuntu latest runs-on: ubuntu-latest steps: From 0a1cdd9da832ba4e4f50e1ec0868a0118fd831ca Mon Sep 17 00:00:00 2001 From: Tim Purschke Date: Fri, 3 May 2024 08:20:48 +0200 Subject: [PATCH 15/23] fix misleading error message for unauthorized access --- .../files/sql/idempotent/fworch-texts.sql | 2 + .../ui/files/FWO.UI/Auth/AuthStateProvider.cs | 383 +++++++++--------- 2 files changed, 196 insertions(+), 189 deletions(-) diff --git a/roles/database/files/sql/idempotent/fworch-texts.sql b/roles/database/files/sql/idempotent/fworch-texts.sql index 47eaf9131..488db5e9f 100644 --- a/roles/database/files/sql/idempotent/fworch-texts.sql +++ b/roles/database/files/sql/idempotent/fworch-texts.sql @@ -273,6 +273,8 @@ INSERT INTO txt VALUES ('permissions_text', 'German', 'Ihre Berechtigungen wur INSERT INTO txt VALUES ('permissions_text', 'English', 'Your permissions have been changed. Re-login to update your permissions.'); INSERT INTO txt VALUES ('login_importer_error', 'German', 'Nutzer mit der Rolle "Importer" dürfen sich nicht an der Benutzeroberfläche anmelden. Diese Rolle dient einzig dem Importieren von eingebundenen Geräten.'); INSERT INTO txt VALUES ('login_importer_error', 'English', 'Users with role "importer" are not allowed to log into the user interface. The only purpose of this role is to import included devices.'); +INSERT INTO txt VALUES ('not_authorized', 'German', 'Authentisierung OK, aber keine Berechtigung/Authorisierung vorhanden.'); +INSERT INTO txt VALUES ('not_authorized', 'English', 'Authentication succeeded, but not authorized.'); -- navigation INSERT INTO txt VALUES ('reporting', 'German', 'Reporting'); diff --git a/roles/ui/files/FWO.UI/Auth/AuthStateProvider.cs b/roles/ui/files/FWO.UI/Auth/AuthStateProvider.cs index d2246234e..c3630727c 100644 --- a/roles/ui/files/FWO.UI/Auth/AuthStateProvider.cs +++ b/roles/ui/files/FWO.UI/Auth/AuthStateProvider.cs @@ -20,196 +20,201 @@ namespace FWO.Ui.Auth { - public class AuthStateProvider : AuthenticationStateProvider - { - private ClaimsPrincipal user = new ClaimsPrincipal(new ClaimsIdentity()); - - public override Task GetAuthenticationStateAsync() - { - return Task.FromResult(new AuthenticationState(user)); - } - - public async Task> Authenticate(string username, string password, ApiConnection apiConnection, MiddlewareClient middlewareClient, - GlobalConfig globalConfig, UserConfig userConfig, ProtectedSessionStorage sessionStorage, CircuitHandlerService circuitHandler) - { - // There is no jwt in session storage. Get one from auth module. - AuthenticationTokenGetParameters authenticationParameters = new AuthenticationTokenGetParameters { Username = username, Password = password }; - RestResponse apiAuthResponse = await middlewareClient.AuthenticateUser(authenticationParameters); - - if (apiAuthResponse.StatusCode == HttpStatusCode.OK) - { - string jwtString = apiAuthResponse.Data ?? throw new Exception("no response data"); - await Authenticate(jwtString, apiConnection, middlewareClient, globalConfig, userConfig, circuitHandler, sessionStorage); - Log.WriteAudit("AuthenticateUser", $"user {username} successfully authenticated"); - } - - return apiAuthResponse; - } - - public async Task Authenticate(string jwtString, ApiConnection apiConnection, MiddlewareClient middlewareClient, - GlobalConfig globalConfig, UserConfig userConfig, CircuitHandlerService circuitHandler, ProtectedSessionStorage sessionStorage) - { - // Try to auth with jwt (validates it and creates user context on UI side). - JwtReader jwtReader = new JwtReader(jwtString); - - if (await jwtReader.Validate()) - { - // importer is not allowed to login - if (jwtReader.ContainsRole(Roles.Importer)) + public class AuthStateProvider : AuthenticationStateProvider + { + private ClaimsPrincipal user = new ClaimsPrincipal(new ClaimsIdentity()); + + public override Task GetAuthenticationStateAsync() + { + return Task.FromResult(new AuthenticationState(user)); + } + + public async Task> Authenticate(string username, string password, ApiConnection apiConnection, MiddlewareClient middlewareClient, + GlobalConfig globalConfig, UserConfig userConfig, ProtectedSessionStorage sessionStorage, CircuitHandlerService circuitHandler) + { + // There is no jwt in session storage. Get one from auth module. + AuthenticationTokenGetParameters authenticationParameters = new AuthenticationTokenGetParameters { Username = username, Password = password }; + RestResponse apiAuthResponse = await middlewareClient.AuthenticateUser(authenticationParameters); + + if (apiAuthResponse.StatusCode == HttpStatusCode.OK) + { + string jwtString = apiAuthResponse.Data ?? throw new Exception("no response data"); + await Authenticate(jwtString, apiConnection, middlewareClient, globalConfig, userConfig, circuitHandler, sessionStorage); + Log.WriteAudit("AuthenticateUser", $"user {username} successfully authenticated"); + } + + return apiAuthResponse; + } + + public async Task Authenticate(string jwtString, ApiConnection apiConnection, MiddlewareClient middlewareClient, + GlobalConfig globalConfig, UserConfig userConfig, CircuitHandlerService circuitHandler, ProtectedSessionStorage sessionStorage) + { + // Try to auth with jwt (validates it and creates user context on UI side). + JwtReader jwtReader = new JwtReader(jwtString); + + if (await jwtReader.Validate()) + { + // importer is not allowed to login + if (jwtReader.ContainsRole(Roles.Importer)) + { + throw new AuthenticationException("login_importer_error"); + } + + // anonymous has no authorization to login via UI + if (jwtReader.ContainsRole(Roles.Anonymous)) { - throw new AuthenticationException("login_importer_error"); + throw new AuthenticationException("not_authorized"); } - - // Save jwt in session storage. - await sessionStorage.SetAsync("jwt", jwtString); - - // Tell api connection to use jwt as authentication - apiConnection.SetAuthHeader(jwtString); - - // Tell middleware connection to use jwt as authentication - middlewareClient.SetAuthenticationToken(jwtString); - - // Set user claims based on the jwt claims - ClaimsIdentity identity = new ClaimsIdentity - ( - claims: jwtReader.GetClaims(), - authenticationType: "ldap", - nameType: JwtRegisteredClaimNames.UniqueName, - roleType: "role" - ); - - // Set user information - user = new ClaimsPrincipal(identity); - string userDn = user.FindFirstValue("x-hasura-uuid"); - await userConfig.SetUserInformation(userDn, apiConnection); - userConfig.User.Jwt = jwtString; - userConfig.User.Tenant = await getTenantFromJwt(userConfig.User.Jwt, apiConnection); - userConfig.User.Roles = await getAllowedRoles(userConfig.User.Jwt); - userConfig.User.Ownerships = await getAssignedOwners(userConfig.User.Jwt); - circuitHandler.User = userConfig.User; - - // Add jwt expiry timer - JwtEventService.AddJwtTimers(userDn, (int)jwtReader.TimeUntilExpiry().TotalMilliseconds, 1000 * 60 * globalConfig.SessionTimeoutNoticePeriod); - - if (!userConfig.User.PasswordMustBeChanged) - { - NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(user))); - } - } - else - { - Deauthenticate(); - } - } - - public void Deauthenticate() - { - user = new ClaimsPrincipal(new ClaimsIdentity()); - NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(user))); - } - - public void ConfirmPasswordChanged() - { - NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(user ?? throw new Exception("Password cannot be changed because user was not authenticated")))); - } - - public async Task getTenantId(string jwtString) - { - JwtReader jwtReader = new JwtReader(jwtString); - int tenantId = 0; - - if (await jwtReader.Validate()) - { - ClaimsIdentity identity = new ClaimsIdentity - ( - claims: jwtReader.GetClaims(), - authenticationType: "ldap", - nameType: JwtRegisteredClaimNames.UniqueName, - roleType: "role" - ); - - // Set user information - user = new ClaimsPrincipal(identity); - - if (!int.TryParse(user.FindFirstValue("x-hasura-tenant-id"), out tenantId)) - { - // TODO: log warning - } - } - return tenantId; - } - - public async Task getTenantFromJwt(string jwtString, ApiConnection apiConnection) - { - JwtReader jwtReader = new JwtReader(jwtString); - Tenant tenant = new(); - - if (await jwtReader.Validate()) - { - ClaimsIdentity identity = new ClaimsIdentity - ( - claims: jwtReader.GetClaims(), - authenticationType: "ldap", - nameType: JwtRegisteredClaimNames.UniqueName, - roleType: "role" - ); - - // Set user information - user = new ClaimsPrincipal(identity); - - if (int.TryParse(user.FindFirstValue("x-hasura-tenant-id"), out int tenantId)) - { - tenant = await Tenant.GetSingleTenant(apiConnection, tenantId) ?? new(); - } - // else - // { - // // TODO: log warning - // } - } - return tenant; - } - - public async Task> getAllowedRoles(string jwtString) - { - return await GetClaimList(jwtString, "x-hasura-allowed-roles"); - } - - public async Task> getAssignedOwners(string jwtString) - { - List ownerIds = new(); - List ownerClaims = await GetClaimList(jwtString, "x-hasura-editable-owners"); - if(ownerClaims.Count > 0) - { - string[] separatingStrings = { ",", "{", "}" }; - string[] owners = ownerClaims[0].Split(separatingStrings, StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries); - ownerIds = Array.ConvertAll(owners, x => int.Parse(x)).ToList(); - } - return ownerIds; - } - - private async Task> GetClaimList(string jwtString, string claimType) - { - List claimList = new List(); - JwtReader jwtReader = new JwtReader(jwtString); - if (await jwtReader.Validate()) - { - ClaimsIdentity identity = new ClaimsIdentity - ( - claims: jwtReader.GetClaims(), - authenticationType: "ldap", - nameType: JwtRegisteredClaimNames.UniqueName, - roleType: "role" - ); - foreach (Claim claim in identity.Claims) - { - if (claim.Type == claimType) - { - claimList.Add(claim.Value); - } - } - } - return claimList; - } - } + // Save jwt in session storage. + await sessionStorage.SetAsync("jwt", jwtString); + + // Tell api connection to use jwt as authentication + apiConnection.SetAuthHeader(jwtString); + + // Tell middleware connection to use jwt as authentication + middlewareClient.SetAuthenticationToken(jwtString); + + // Set user claims based on the jwt claims + ClaimsIdentity identity = new ClaimsIdentity + ( + claims: jwtReader.GetClaims(), + authenticationType: "ldap", + nameType: JwtRegisteredClaimNames.UniqueName, + roleType: "role" + ); + + // Set user information + user = new ClaimsPrincipal(identity); + string userDn = user.FindFirstValue("x-hasura-uuid"); + await userConfig.SetUserInformation(userDn, apiConnection); + userConfig.User.Jwt = jwtString; + userConfig.User.Tenant = await getTenantFromJwt(userConfig.User.Jwt, apiConnection); + userConfig.User.Roles = await getAllowedRoles(userConfig.User.Jwt); + userConfig.User.Ownerships = await getAssignedOwners(userConfig.User.Jwt); + circuitHandler.User = userConfig.User; + + // Add jwt expiry timer + JwtEventService.AddJwtTimers(userDn, (int)jwtReader.TimeUntilExpiry().TotalMilliseconds, 1000 * 60 * globalConfig.SessionTimeoutNoticePeriod); + + if (!userConfig.User.PasswordMustBeChanged) + { + NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(user))); + } + } + else + { + Deauthenticate(); + } + } + + public void Deauthenticate() + { + user = new ClaimsPrincipal(new ClaimsIdentity()); + NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(user))); + } + + public void ConfirmPasswordChanged() + { + NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(user ?? throw new Exception("Password cannot be changed because user was not authenticated")))); + } + + public async Task getTenantId(string jwtString) + { + JwtReader jwtReader = new JwtReader(jwtString); + int tenantId = 0; + + if (await jwtReader.Validate()) + { + ClaimsIdentity identity = new ClaimsIdentity + ( + claims: jwtReader.GetClaims(), + authenticationType: "ldap", + nameType: JwtRegisteredClaimNames.UniqueName, + roleType: "role" + ); + + // Set user information + user = new ClaimsPrincipal(identity); + + if (!int.TryParse(user.FindFirstValue("x-hasura-tenant-id"), out tenantId)) + { + // TODO: log warning + } + } + return tenantId; + } + + public async Task getTenantFromJwt(string jwtString, ApiConnection apiConnection) + { + JwtReader jwtReader = new JwtReader(jwtString); + Tenant tenant = new(); + + if (await jwtReader.Validate()) + { + ClaimsIdentity identity = new ClaimsIdentity + ( + claims: jwtReader.GetClaims(), + authenticationType: "ldap", + nameType: JwtRegisteredClaimNames.UniqueName, + roleType: "role" + ); + + // Set user information + user = new ClaimsPrincipal(identity); + + if (int.TryParse(user.FindFirstValue("x-hasura-tenant-id"), out int tenantId)) + { + tenant = await Tenant.GetSingleTenant(apiConnection, tenantId) ?? new(); + } + // else + // { + // // TODO: log warning + // } + } + return tenant; + } + + public async Task> getAllowedRoles(string jwtString) + { + return await GetClaimList(jwtString, "x-hasura-allowed-roles"); + } + + public async Task> getAssignedOwners(string jwtString) + { + List ownerIds = new(); + List ownerClaims = await GetClaimList(jwtString, "x-hasura-editable-owners"); + if(ownerClaims.Count > 0) + { + string[] separatingStrings = { ",", "{", "}" }; + string[] owners = ownerClaims[0].Split(separatingStrings, StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries); + ownerIds = Array.ConvertAll(owners, x => int.Parse(x)).ToList(); + } + return ownerIds; + } + + private async Task> GetClaimList(string jwtString, string claimType) + { + List claimList = new List(); + JwtReader jwtReader = new JwtReader(jwtString); + if (await jwtReader.Validate()) + { + ClaimsIdentity identity = new ClaimsIdentity + ( + claims: jwtReader.GetClaims(), + authenticationType: "ldap", + nameType: JwtRegisteredClaimNames.UniqueName, + roleType: "role" + ); + foreach (Claim claim in identity.Claims) + { + if (claim.Type == claimType) + { + claimList.Add(claim.Value); + } + } + } + return claimList; + } + } } From 2a74bb431b49ae175f5c86d2dadaebf18be85c9b Mon Sep 17 00:00:00 2001 From: Tim Purschke Date: Fri, 3 May 2024 08:24:56 +0200 Subject: [PATCH 16/23] v8.2.1 vesioning --- documentation/revision-history-develop.md | 3 +++ inventory/group_vars/all.yml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/documentation/revision-history-develop.md b/documentation/revision-history-develop.md index 0bb70508e..df39a94e6 100644 --- a/documentation/revision-history-develop.md +++ b/documentation/revision-history-develop.md @@ -202,3 +202,6 @@ bugfix release: - fix demo managements (change import from deactivated to activated - does not affect test managements) - upgrade to dotnet 8.0 - adding all imported modelling users to uiuser + +# 8.2.1 - xx.05.2024 DEVELOP +- fix misleading login error message when authorisation is missing diff --git a/inventory/group_vars/all.yml b/inventory/group_vars/all.yml index cf21de7d9..19c999f6b 100644 --- a/inventory/group_vars/all.yml +++ b/inventory/group_vars/all.yml @@ -1,5 +1,5 @@ ### general settings -product_version: "8.2" +product_version: "8.2.1" ansible_user: "{{ lookup('env', 'USER') }}" ansible_become_method: sudo ansible_python_interpreter: /usr/bin/python3 From 6c8c524589953bf6ed200c9d99d0bf8c420f9588 Mon Sep 17 00:00:00 2001 From: Tim Purschke Date: Fri, 3 May 2024 12:47:40 +0200 Subject: [PATCH 17/23] cosmetics --- .../ui/files/FWO.UI/Auth/AuthStateProvider.cs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/roles/ui/files/FWO.UI/Auth/AuthStateProvider.cs b/roles/ui/files/FWO.UI/Auth/AuthStateProvider.cs index c3630727c..5457f5151 100644 --- a/roles/ui/files/FWO.UI/Auth/AuthStateProvider.cs +++ b/roles/ui/files/FWO.UI/Auth/AuthStateProvider.cs @@ -1,11 +1,8 @@ using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; -using System.Threading.Tasks; using Microsoft.AspNetCore.Components.Authorization; using FWO.Config.Api; using FWO.Api.Client; -using FWO.Api.Client.Queries; -using FWO.GlobalConstants; using FWO.Api.Data; using FWO.Ui.Services; using FWO.Middleware.Client; @@ -15,8 +12,6 @@ using FWO.Logging; using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage; using System.Security.Authentication; -using System.Security.Principal; - namespace FWO.Ui.Auth { @@ -60,11 +55,12 @@ public async Task Authenticate(string jwtString, ApiConnection apiConnection, Mi throw new AuthenticationException("login_importer_error"); } - // anonymous has no authorization to login via UI - if (jwtReader.ContainsRole(Roles.Anonymous)) - { - throw new AuthenticationException("not_authorized"); - } + // anonymous has no authorization to login via UI + if (jwtReader.ContainsRole(Roles.Anonymous)) + { + throw new AuthenticationException("not_authorized"); + } + // Save jwt in session storage. await sessionStorage.SetAsync("jwt", jwtString); @@ -183,7 +179,7 @@ public async Task> getAssignedOwners(string jwtString) { List ownerIds = new(); List ownerClaims = await GetClaimList(jwtString, "x-hasura-editable-owners"); - if(ownerClaims.Count > 0) + if (ownerClaims.Count > 0) { string[] separatingStrings = { ",", "{", "}" }; string[] owners = ownerClaims[0].Split(separatingStrings, StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries); From 33ea1505798315b19da4364f66847658dcba1a91 Mon Sep 17 00:00:00 2001 From: abarz722 Date: Fri, 3 May 2024 14:16:39 +0200 Subject: [PATCH 18/23] fixes app data import + Ldap cleanup --- .../FWO.Middleware.Server/AppDataImport.cs | 52 +- .../files/FWO.Middleware.Server/Ldap.cs | 949 ++++++++---------- 2 files changed, 478 insertions(+), 523 deletions(-) diff --git a/roles/middleware/files/FWO.Middleware.Server/AppDataImport.cs b/roles/middleware/files/FWO.Middleware.Server/AppDataImport.cs index 38bd17291..681fbc55d 100644 --- a/roles/middleware/files/FWO.Middleware.Server/AppDataImport.cs +++ b/roles/middleware/files/FWO.Middleware.Server/AppDataImport.cs @@ -263,29 +263,31 @@ private async Task AddAllGroupMembersToUiUser(string userGroupDn) { foreach (string memberDn in ldap.GetGroupMembers(userGroupDn)) { - await UiUserHandler.UpsertUiUser(apiConnection, await ConvertLdapToUiUser(apiConnection, memberDn), false); + UiUser? uiUser = await ConvertLdapToUiUser(apiConnection, memberDn); + if(uiUser != null) + { + await UiUserHandler.UpsertUiUser(apiConnection, uiUser, false); + } } - } + } } - private async Task ConvertLdapToUiUser(ApiConnection apiConnection, string userDn) + private async Task ConvertLdapToUiUser(ApiConnection apiConnection, string userDn) { // add the modelling user to local uiuser table for later ref to email address - UiUser uiUser = new(); - // find the user in all connected ldaps foreach (Ldap ldap in connectedLdaps) { - if (!ldap.UserSearchPath.IsNullOrEmpty() && userDn.ToLower().Contains(ldap.UserSearchPath.ToLower())) + if (!ldap.UserSearchPath.IsNullOrEmpty() && userDn.ToLower().Contains(ldap.UserSearchPath!.ToLower())) { - LdapEntry ldapUser = ldap.GetUserDetailsFromLdap(userDn); + LdapEntry? ldapUser = ldap.GetUserDetailsFromLdap(userDn); if (ldapUser != null) { // add data from ldap entry to uiUser - uiUser = new() + return new() { - LdapConnection = new UiLdapConnection(), + LdapConnection = new UiLdapConnection(){ Id = ldap.Id }, Dn = ldapUser.Dn, Name = ldap.GetName(ldapUser), Firstname = ldap.GetFirstName(ldapUser), @@ -293,19 +295,15 @@ private async Task ConvertLdapToUiUser(ApiConnection apiConnection, stri Email = ldap.GetEmail(ldapUser), Tenant = await DeriveTenantFromLdap(ldap, ldapUser) }; - uiUser.LdapConnection.Id = ldap.Id; - return uiUser; } } } - return uiUser; - + return null; } private async Task DeriveTenantFromLdap(Ldap ldap, LdapEntry ldapUser) { // try to derive the the user's tenant from the ldap settings - Tenant tenant = new() { Id = GlobalConst.kTenant0Id // default: tenant0 (id=1) @@ -325,7 +323,7 @@ private async Task DeriveTenantFromLdap(Ldap ldap, LdapEntry ldapUser) { if (!ldap.GlobalTenantName.IsNullOrEmpty()) { - tenantName = ldap.GlobalTenantName; + tenantName = ldap.GlobalTenantName ?? ""; } } @@ -336,9 +334,7 @@ private async Task DeriveTenantFromLdap(Ldap ldap, LdapEntry ldapUser) tenant.Id = tenants[0].Id; } } - return tenant; - } private string CreateUserGroup(ModellingImportAppData incomingApp) @@ -403,9 +399,31 @@ private string UpdateUserGroup(ModellingImportAppData incomingApp, string groupD internalLdap.RemoveUserFromEntry(member, groupDn); } } + UpdateRoles(groupDn); return groupDn; } + private void UpdateRoles(string groupDn) + { + List roles = internalLdap.GetRoles([groupDn]); + if(!roles.Contains(Roles.Modeller)) + { + internalLdap.AddUserToEntry(groupDn, modellerRoleDn); + } + if(!roles.Contains(Roles.Requester)) + { + internalLdap.AddUserToEntry(groupDn, requesterRoleDn); + } + if(!roles.Contains(Roles.Implementer)) + { + internalLdap.AddUserToEntry(groupDn, implementerRoleDn); + } + if(!roles.Contains(Roles.Reviewer)) + { + internalLdap.AddUserToEntry(groupDn, reviewerRoleDn); + } + } + private async Task ImportAppServers(ModellingImportAppData incomingApp, int applId) { int successCounter = 0; diff --git a/roles/middleware/files/FWO.Middleware.Server/Ldap.cs b/roles/middleware/files/FWO.Middleware.Server/Ldap.cs index c5779e5f9..376e1ab15 100644 --- a/roles/middleware/files/FWO.Middleware.Server/Ldap.cs +++ b/roles/middleware/files/FWO.Middleware.Server/Ldap.cs @@ -38,9 +38,9 @@ private LdapConnection Connect() { try { - LdapConnectionOptions ldapOptions = new LdapConnectionOptions(); + LdapConnectionOptions ldapOptions = new (); if (Tls) ldapOptions.ConfigureRemoteCertificateValidationCallback((object sen, X509Certificate? cer, X509Chain? cha, SslPolicyErrors err) => true); // todo: allow real cert validation - LdapConnection connection = new LdapConnection(ldapOptions) { SecureSocketLayer = Tls, ConnectionTimeout = timeOutInMs }; + LdapConnection connection = new (ldapOptions) { SecureSocketLayer = Tls, ConnectionTimeout = timeOutInMs }; connection.Connect(Address, Port); return connection; @@ -79,18 +79,16 @@ private static bool TryBind(LdapConnection connection, string user, string passw /// public void TestConnection() { - using (LdapConnection connection = Connect()) - { - if (!string.IsNullOrEmpty(SearchUser)) - { - if (!TryBind(connection, SearchUser, SearchUserPwd)) throw new Exception("Binding failed for search user"); - } - if (!string.IsNullOrEmpty(WriteUser)) - { - if (!TryBind(connection, WriteUser, WriteUserPwd)) throw new Exception("Binding failed for write user"); - } - } - } + using LdapConnection connection = Connect(); + if (!string.IsNullOrEmpty(SearchUser)) + { + if (!TryBind(connection, SearchUser, SearchUserPwd)) throw new Exception("Binding failed for search user"); + } + if (!string.IsNullOrEmpty(WriteUser)) + { + if (!TryBind(connection, WriteUser, WriteUserPwd)) throw new Exception("Binding failed for write user"); + } + } private string GetUserSearchFilter(string searchPattern) { @@ -111,7 +109,7 @@ private string GetUserSearchFilter(string searchPattern) userFilter = "(&(|(objectclass=user)(objectclass=person)(objectclass=inetOrgPerson)(objectclass=organizationalPerson))(!(objectclass=computer)))"; searchFilter = $"(|(cn={searchPattern})(uid={searchPattern})(userPrincipalName={searchPattern})(mail={searchPattern}))"; } - return ((searchPattern == null || searchPattern == "") ? userFilter : $"(&{userFilter}{searchFilter})"); + return (searchPattern == null || searchPattern == "") ? userFilter : $"(&{userFilter}{searchFilter})"; } private string GetGroupSearchFilter(string searchPattern) @@ -145,63 +143,60 @@ private string GetGroupSearchFilter(string searchPattern) Log.WriteDebug("User Validation", $"Validating User: \"{user.Name}\" ..."); try { - // Connecting to Ldap - using (LdapConnection connection = Connect()) - { - TryBind(connection, SearchUser, SearchUserPwd); - - LdapSearchConstraints cons = connection.SearchConstraints; - cons.ReferralFollowing = true; - connection.Constraints = cons; - - List possibleUserEntries = new List(); - - // If dn was already provided - if (!user.Dn.IsNullOrEmpty()) - { - // Try to read user entry directly - LdapEntry? userEntry = connection.Read(user.Dn); - if (userEntry != null) - { - possibleUserEntries.Add(userEntry); - } - } - else // Dn was not provided, search for user name - { - string[] attrList = new string[] { "*", "memberof" }; - string userSearchFilter = GetUserSearchFilter(user.Name); - - // Search for users in ldap with same name as user to validate - possibleUserEntries = ((LdapSearchResults)connection.Search( - UserSearchPath, // top-level path under which to search for user - LdapConnection.ScopeSub, // search all levels beneath - userSearchFilter, - attrList, - typesOnly: false - )).ToList(); - } - - // If credentials are not checked return user that was found first - // It could happen that multiple users with the same name were found (impossible if dn was provided) - if (!validateCredentials && possibleUserEntries.Count > 0) - { - return possibleUserEntries.First(); - } - // If credentials should be checked - else if (validateCredentials) - { - // Multiple users with the same name could have been found (impossible if dn was provided) - foreach (LdapEntry possibleUserEntry in possibleUserEntries) - { - // Check credentials - if multiple users were found and the credentials are valid this is most definitely the correct user - if (CredentialsValid(connection, possibleUserEntry.Dn, user.Password)) - { - return possibleUserEntry; - } - } - } - } - } + using LdapConnection connection = Connect(); + TryBind(connection, SearchUser, SearchUserPwd); + + LdapSearchConstraints cons = connection.SearchConstraints; + cons.ReferralFollowing = true; + connection.Constraints = cons; + + List possibleUserEntries = []; + + // If dn was already provided + if (!user.Dn.IsNullOrEmpty()) + { + // Try to read user entry directly + LdapEntry? userEntry = connection.Read(user.Dn); + if (userEntry != null) + { + possibleUserEntries.Add(userEntry); + } + } + else // Dn was not provided, search for user name + { + string[] attrList = ["*", "memberof"]; + string userSearchFilter = GetUserSearchFilter(user.Name); + + // Search for users in ldap with same name as user to validate + possibleUserEntries = ((LdapSearchResults)connection.Search( + UserSearchPath, // top-level path under which to search for user + LdapConnection.ScopeSub, // search all levels beneath + userSearchFilter, + attrList, + typesOnly: false + )).ToList(); + } + + // If credentials are not checked return user that was found first + // It could happen that multiple users with the same name were found (impossible if dn was provided) + if (!validateCredentials && possibleUserEntries.Count > 0) + { + return possibleUserEntries.First(); + } + // If credentials should be checked + else if (validateCredentials) + { + // Multiple users with the same name could have been found (impossible if dn was provided) + foreach (LdapEntry possibleUserEntry in possibleUserEntries) + { + // Check credentials - if multiple users were found and the credentials are valid this is most definitely the correct user + if (CredentialsValid(connection, possibleUserEntry.Dn, user.Password)) + { + return possibleUserEntry; + } + } + } + } catch (LdapException ldapException) { Log.WriteInfo("Ldap entry exception", $"Ldap entry search at \"{Address}:{Port}\" lead to exception: {ldapException.Message}"); @@ -215,34 +210,24 @@ private string GetGroupSearchFilter(string searchPattern) return null; } + /// + /// Get the LdapEntry for the given user by Dn + /// + /// LdapEntry for the given user if found public LdapEntry? GetUserDetailsFromLdap(string distinguishedName) { try { - // Connecting to Ldap - using (LdapConnection connection = Connect()) - { - TryBind(connection, SearchUser, SearchUserPwd); + using LdapConnection connection = Connect(); + TryBind(connection, SearchUser, SearchUserPwd); - LdapSearchConstraints cons = connection.SearchConstraints; - cons.ReferralFollowing = true; - connection.Constraints = cons; + LdapSearchConstraints cons = connection.SearchConstraints; + cons.ReferralFollowing = true; + connection.Constraints = cons; - List possibleUserEntries = []; - - // Try to read user entry directly - LdapEntry? userEntry = connection.Read(distinguishedName); - if (userEntry != null) - { - possibleUserEntries.Add(userEntry); - } - - if (possibleUserEntries.Count > 0) - { - return possibleUserEntries.First(); - } - } - } + // Try to read user entry directly + return connection.Read(distinguishedName); + } catch (LdapException ldapException) { Log.WriteInfo("Ldap entry exception", $"Ldap entry search at \"{Address}:{Port}\" lead to exception: {ldapException.Message}"); @@ -351,7 +336,7 @@ public List GetGroups(LdapEntry user) // - Probably this doesn't work for nested groups. // - Some systems may only save the "primaryGroupID", then we would have to resolve the name. // - Some others may force us to look into all groups to find the membership. - List groups = new List(); + List groups = []; foreach (var attribute in user.GetAttributeSet()) { if (attribute.Name.ToLower() == "memberof") @@ -376,25 +361,22 @@ public string ChangePassword(string userDn, string oldPassword, string newPasswo { try { - // Connecting to Ldap - using (LdapConnection connection = Connect()) - { - // Try to authenticate as user with old password - if (TryBind(connection, userDn, oldPassword)) - { - // authentication was successful (user is bound): set new password - LdapAttribute attribute = new("userPassword", newPassword); - LdapModification[] mods = { new LdapModification(LdapModification.Replace, attribute) }; - - connection.Modify(userDn, mods); - Log.WriteDebug("Change password", $"Password for user {userDn} changed in {Address}:{Port}"); - } - else - { - return "wrong old password"; - } - } - } + using LdapConnection connection = Connect(); + // Try to authenticate as user with old password + if (TryBind(connection, userDn, oldPassword)) + { + // authentication was successful (user is bound): set new password + LdapAttribute attribute = new("userPassword", newPassword); + LdapModification[] mods = [new LdapModification(LdapModification.Replace, attribute)]; + + connection.Modify(userDn, mods); + Log.WriteDebug("Change password", $"Password for user {userDn} changed in {Address}:{Port}"); + } + else + { + return "wrong old password"; + } + } catch (Exception exception) { return exception.Message; @@ -410,24 +392,21 @@ public string SetPassword(string userDn, string newPassword) { try { - // Connecting to Ldap - using (LdapConnection connection = Connect()) - { - if (TryBind(connection, WriteUser, WriteUserPwd)) - { - // authentication was successful: set new password - LdapAttribute attribute = new LdapAttribute("userPassword", newPassword); - LdapModification[] mods = { new LdapModification(LdapModification.Replace, attribute) }; - - connection.Modify(userDn, mods); - Log.WriteDebug("Change password", $"Password for user {userDn} changed in {Address}:{Port}"); - } - else - { - return "error in write user authentication"; - } - } - } + using LdapConnection connection = Connect(); + if (TryBind(connection, WriteUser, WriteUserPwd)) + { + // authentication was successful: set new password + LdapAttribute attribute = new ("userPassword", newPassword); + LdapModification[] mods = [new LdapModification(LdapModification.Replace, attribute)]; + + connection.Modify(userDn, mods); + Log.WriteDebug("Change password", $"Password for user {userDn} changed in {Address}:{Port}"); + } + else + { + return "error in write user authentication"; + } + } catch (Exception exception) { return exception.Message; @@ -462,46 +441,43 @@ private List GetMemberships(List dnList, string? searchPath) { try { - // Connect to Ldap - using (LdapConnection connection = Connect()) - { - // Authenticate as search user - TryBind(connection, SearchUser, AesEnc.Decrypt(SearchUserPwd, AesEnc.GetMainKey())); - - // Search for Ldap roles / groups in given directory - int searchScope = LdapConnection.ScopeSub; // TODO: Correct search scope? - string searchFilter = $"(&(objectClass=groupOfUniqueNames)(cn=*))"; - LdapSearchResults searchResults = (LdapSearchResults)connection.Search(searchPath, searchScope, searchFilter, null, false); - - // convert dnList to lower case to avoid case problems - dnList = dnList.ConvertAll(dn => dn.ToLower()); - - // Foreach found role / group - foreach (LdapEntry entry in searchResults) - { - Log.WriteDebug("Ldap Roles/Groups", $"Try to get roles / groups from ldap entry {entry.GetAttribute("cn").StringValue}"); - - // Get dn of users having current role / group - LdapAttribute members = entry.GetAttribute("uniqueMember"); - string[] memberDn = members.StringValueArray; - - // Foreach user - foreach (string currentDn in memberDn) - { - Log.WriteDebug("Ldap Roles/Groups", $"Checking if current Dn: \"{currentDn}\" is user Dn. Then user has current role / group."); - - // Check if current user dn is matching with given user dn => Given user has current role / group - if (dnList.Contains(currentDn.ToLower())) - { - // Get name and add it to list of roles / groups of given user - string name = entry.GetAttribute("cn").StringValue; - userMemberships.Add(name); - break; - } - } - } - } - } + using LdapConnection connection = Connect(); + // Authenticate as search user + TryBind(connection, SearchUser, AesEnc.Decrypt(SearchUserPwd, AesEnc.GetMainKey())); + + // Search for Ldap roles / groups in given directory + int searchScope = LdapConnection.ScopeSub; // TODO: Correct search scope? + string searchFilter = $"(&(objectClass=groupOfUniqueNames)(cn=*))"; + LdapSearchResults searchResults = (LdapSearchResults)connection.Search(searchPath, searchScope, searchFilter, null, false); + + // convert dnList to lower case to avoid case problems + dnList = dnList.ConvertAll(dn => dn.ToLower()); + + // Foreach found role / group + foreach (LdapEntry entry in searchResults) + { + Log.WriteDebug("Ldap Roles/Groups", $"Try to get roles / groups from ldap entry {entry.GetAttribute("cn").StringValue}"); + + // Get dn of users having current role / group + LdapAttribute members = entry.GetAttribute("uniqueMember"); + string[] memberDn = members.StringValueArray; + + // Foreach user + foreach (string currentDn in memberDn) + { + Log.WriteDebug("Ldap Roles/Groups", $"Checking if current Dn: \"{currentDn}\" is user Dn. Then user has current role / group."); + + // Check if current user dn is matching with given user dn => Given user has current role / group + if (dnList.Contains(currentDn.ToLower())) + { + // Get name and add it to list of roles / groups of given user + string name = entry.GetAttribute("cn").StringValue; + userMemberships.Add(name); + break; + } + } + } + } catch (Exception exception) { Log.WriteError($"Non-LDAP exception {Address}:{Port}", "Unexpected error while trying to get memberships", exception); @@ -525,36 +501,33 @@ public List GetAllRoles() { try { - // Connect to Ldap - using (LdapConnection connection = Connect()) - { - // Authenticate as search user - TryBind(connection, SearchUser, SearchUserPwd); - - // Search for Ldap roles in given directory - int searchScope = LdapConnection.ScopeSub; // TODO: Correct search scope? - string searchFilter = $"(&(objectClass=groupOfUniqueNames)(cn=*))"; - LdapSearchResults searchResults = (LdapSearchResults)connection.Search(RoleSearchPath, searchScope, searchFilter, null, false); - - // Foreach found role - foreach (LdapEntry entry in searchResults) - { - List attributes = []; - string roleDesc = entry.GetAttribute("description").StringValue; - attributes.Add(new RoleAttribute() { Key = "description", Value = roleDesc }); - - string[] roleMemberDn = entry.GetAttribute("uniqueMember").StringValueArray; - foreach (string currentDn in roleMemberDn) - { - if (currentDn != "") - { - attributes.Add(new RoleAttribute() { Key = "user", Value = currentDn }); - } - } - roleUsers.Add(new RoleGetReturnParameters() { Role = entry.Dn, Attributes = attributes }); - } - } - } + using LdapConnection connection = Connect(); + // Authenticate as search user + TryBind(connection, SearchUser, SearchUserPwd); + + // Search for Ldap roles in given directory + int searchScope = LdapConnection.ScopeSub; // TODO: Correct search scope? + string searchFilter = $"(&(objectClass=groupOfUniqueNames)(cn=*))"; + LdapSearchResults searchResults = (LdapSearchResults)connection.Search(RoleSearchPath, searchScope, searchFilter, null, false); + + // Foreach found role + foreach (LdapEntry entry in searchResults) + { + List attributes = []; + string roleDesc = entry.GetAttribute("description").StringValue; + attributes.Add(new () { Key = "description", Value = roleDesc }); + + string[] roleMemberDn = entry.GetAttribute("uniqueMember").StringValueArray; + foreach (string currentDn in roleMemberDn) + { + if (currentDn != "") + { + attributes.Add(new () { Key = "user", Value = currentDn }); + } + } + roleUsers.Add(new RoleGetReturnParameters() { Role = entry.Dn, Attributes = attributes }); + } + } catch (Exception exception) { Log.WriteError($"Non-LDAP exception {Address}:{Port}", "Unexpected error while trying to get all roles", exception); @@ -572,22 +545,19 @@ public List GetAllGroups(string searchPattern) List allGroups = []; try { - // Connect to Ldap - using (LdapConnection connection = Connect()) - { - // Authenticate as search user - TryBind(connection, SearchUser, SearchUserPwd); - - // Search for Ldap groups in given directory - int searchScope = LdapConnection.ScopeSub; - LdapSearchResults searchResults = (LdapSearchResults)connection.Search(GroupSearchPath, searchScope, GetGroupSearchFilter(searchPattern), null, false); - - foreach (LdapEntry entry in searchResults) - { - allGroups.Add(entry.Dn); - } - } - } + using LdapConnection connection = Connect(); + // Authenticate as search user + TryBind(connection, SearchUser, SearchUserPwd); + + // Search for Ldap groups in given directory + int searchScope = LdapConnection.ScopeSub; + LdapSearchResults searchResults = (LdapSearchResults)connection.Search(GroupSearchPath, searchScope, GetGroupSearchFilter(searchPattern), null, false); + + foreach (LdapEntry entry in searchResults) + { + allGroups.Add(entry.Dn); + } + } catch (Exception exception) { Log.WriteError($"Non-LDAP exception {Address}:{Port}", "Unexpected error while trying to get all groups", exception); @@ -605,36 +575,33 @@ public List GetAllInternalGroups() try { - // Connect to Ldap - using (LdapConnection connection = Connect()) - { - // Authenticate as search user - TryBind(connection, SearchUser, SearchUserPwd); - - // Search for Ldap groups in given directory - int searchScope = LdapConnection.ScopeSub; - LdapSearchResults searchResults = (LdapSearchResults)connection.Search(GroupSearchPath, searchScope, GetGroupSearchFilter(""), null, false); - - foreach (LdapEntry entry in searchResults) - { - List members = []; - string[] groupMemberDn = entry.GetAttribute("uniqueMember").StringValueArray; - foreach (string currentDn in groupMemberDn) - { - if (currentDn != "") - { - members.Add(currentDn); - } - } - allGroups.Add(new GroupGetReturnParameters() - { - GroupDn = entry.Dn, - Members = members, - OwnerGroup = entry.GetAttributeSet().ContainsKey("businessCategory") && entry.GetAttribute("businessCategory").StringValue.Equals("ownergroup", StringComparison.CurrentCultureIgnoreCase) - }); - } - } - } + using LdapConnection connection = Connect(); + // Authenticate as search user + TryBind(connection, SearchUser, SearchUserPwd); + + // Search for Ldap groups in given directory + int searchScope = LdapConnection.ScopeSub; + LdapSearchResults searchResults = (LdapSearchResults)connection.Search(GroupSearchPath, searchScope, GetGroupSearchFilter(""), null, false); + + foreach (LdapEntry entry in searchResults) + { + List members = []; + string[] groupMemberDn = entry.GetAttribute("uniqueMember").StringValueArray; + foreach (string currentDn in groupMemberDn) + { + if (currentDn != "") + { + members.Add(currentDn); + } + } + allGroups.Add(new GroupGetReturnParameters() + { + GroupDn = entry.Dn, + Members = members, + OwnerGroup = entry.GetAttributeSet().ContainsKey("businessCategory") && entry.GetAttribute("businessCategory").StringValue.Equals("ownergroup", StringComparison.CurrentCultureIgnoreCase) + }); + } + } catch (Exception exception) { Log.WriteError($"Non-LDAP exception {Address}:{Port}", "Unexpected error while trying to get all internal groups", exception); @@ -654,26 +621,23 @@ public List GetGroupMembers(string groupDn) { try { - // Connect to Ldap - using (LdapConnection connection = Connect()) - { - // Authenticate as search user - TryBind(connection, SearchUser, SearchUserPwd); - LdapEntry entry = connection.Read(groupDn); - - if (entry != null) - { - string[] groupMemberDn = entry.GetAttribute("uniqueMember").StringValueArray; - foreach (string currentDn in groupMemberDn) - { - if (currentDn != "") - { - allMembers.Add(currentDn); - } - } - } - } - } + using LdapConnection connection = Connect(); + // Authenticate as search user + TryBind(connection, SearchUser, SearchUserPwd); + LdapEntry entry = connection.Read(groupDn); + + if (entry != null) + { + string[] groupMemberDn = entry.GetAttribute("uniqueMember").StringValueArray; + foreach (string currentDn in groupMemberDn) + { + if (currentDn != "") + { + allMembers.Add(currentDn); + } + } + } + } catch (Exception exception) { Log.WriteError($"Non-LDAP exception {Address}:{Port}", $"Unexpected error while trying to get all group members of group {groupDn}", exception); @@ -693,32 +657,29 @@ public List GetAllUsers(string searchPattern) try { - // Connect to Ldap - using (LdapConnection connection = Connect()) - { - // Authenticate as search user - TryBind(connection, SearchUser, SearchUserPwd); - - // Search for Ldap users in given directory - int searchScope = LdapConnection.ScopeSub; - - LdapSearchConstraints cons = connection.SearchConstraints; - cons.ReferralFollowing = true; - connection.Constraints = cons; - - LdapSearchResults searchResults = (LdapSearchResults)connection.Search(UserSearchPath, searchScope, GetUserSearchFilter(searchPattern), null, false); - - foreach (LdapEntry entry in searchResults) - { - allUsers.Add(new LdapUserGetReturnParameters() - { - UserDn = entry.Dn, - Email = entry.GetAttributeSet().ContainsKey("mail") ? entry.GetAttribute("mail").StringValue : null - // add first and last name of user - }); - } - } - } + using LdapConnection connection = Connect(); + // Authenticate as search user + TryBind(connection, SearchUser, SearchUserPwd); + + // Search for Ldap users in given directory + int searchScope = LdapConnection.ScopeSub; + + LdapSearchConstraints cons = connection.SearchConstraints; + cons.ReferralFollowing = true; + connection.Constraints = cons; + + LdapSearchResults searchResults = (LdapSearchResults)connection.Search(UserSearchPath, searchScope, GetUserSearchFilter(searchPattern), null, false); + + foreach (LdapEntry entry in searchResults) + { + allUsers.Add(new LdapUserGetReturnParameters() + { + UserDn = entry.Dn, + Email = entry.GetAttributeSet().ContainsKey("mail") ? entry.GetAttribute("mail").StringValue : null + // add first and last name of user + }); + } + } catch (Exception exception) { Log.WriteError($"Non-LDAP exception {Address}:{Port}", "Unexpected error while trying to get all users", exception); @@ -736,38 +697,35 @@ public bool AddUser(string userDn, string password, string email) bool userAdded = false; try { - // Connecting to Ldap - using (LdapConnection connection = Connect()) - { - // Authenticate as write user - TryBind(connection, WriteUser, WriteUserPwd); - - string userName = new FWO.Api.Data.DistName(userDn).UserName; - LdapAttributeSet attributeSet = new LdapAttributeSet - { - new LdapAttribute("objectclass", "inetOrgPerson"), - new LdapAttribute("sn", userName), - new LdapAttribute("cn", userName), - new LdapAttribute("uid", userName), - new LdapAttribute("userPassword", password), - new LdapAttribute("mail", email) - }; + using LdapConnection connection = Connect(); + // Authenticate as write user + TryBind(connection, WriteUser, WriteUserPwd); - LdapEntry newEntry = new LdapEntry(userDn, attributeSet); - - try - { - //Add the entry to the directory - connection.Add(newEntry); - userAdded = true; - Log.WriteDebug("Add user", $"User {userName} added in {Address}:{Port}"); - } - catch (Exception exception) - { - Log.WriteInfo("Add User", $"couldn't add user to LDAP {Address}:{Port}: {exception.ToString()}"); - } - } - } + string userName = new DistName(userDn).UserName; + LdapAttributeSet attributeSet = new () + { + new LdapAttribute("objectclass", "inetOrgPerson"), + new LdapAttribute("sn", userName), + new LdapAttribute("cn", userName), + new LdapAttribute("uid", userName), + new LdapAttribute("userPassword", password), + new LdapAttribute("mail", email) + }; + + LdapEntry newEntry = new (userDn, attributeSet); + + try + { + //Add the entry to the directory + connection.Add(newEntry); + userAdded = true; + Log.WriteDebug("Add user", $"User {userName} added in {Address}:{Port}"); + } + catch (Exception exception) + { + Log.WriteInfo("Add User", $"couldn't add user to LDAP {Address}:{Port}: {exception}"); + } + } catch (Exception exception) { Log.WriteError($"Non-LDAP exception {Address}:{Port}", "Unexpected error while trying to add user", exception); @@ -785,27 +743,24 @@ public bool UpdateUser(string userDn, string email) bool userUpdated = false; try { - // Connecting to Ldap - using (LdapConnection connection = Connect()) - { - // Authenticate as write user - TryBind(connection, WriteUser, WriteUserPwd); - LdapAttribute attribute = new LdapAttribute("mail", email); - LdapModification[] mods = { new LdapModification(LdapModification.Replace, attribute) }; - - try - { - //Add the entry to the directory - connection.Modify(userDn, mods); - userUpdated = true; - Log.WriteDebug("Update user", $"User {userDn} updated in {Address}:{Port}"); - } - catch (Exception exception) - { - Log.WriteInfo("Update User", $"couldn't update user in LDAP {Address}:{Port}: {exception.ToString()}"); - } - } - } + using LdapConnection connection = Connect(); + // Authenticate as write user + TryBind(connection, WriteUser, WriteUserPwd); + LdapAttribute attribute = new ("mail", email); + LdapModification[] mods = [new (LdapModification.Replace, attribute)]; + + try + { + //Add the entry to the directory + connection.Modify(userDn, mods); + userUpdated = true; + Log.WriteDebug("Update user", $"User {userDn} updated in {Address}:{Port}"); + } + catch (Exception exception) + { + Log.WriteInfo("Update User", $"couldn't update user in LDAP {Address}:{Port}: {exception}"); + } + } catch (Exception exception) { Log.WriteError($"Non-LDAP exception {Address}:{Port}", "Unexpected error while trying to update user", exception); @@ -823,25 +778,22 @@ public bool DeleteUser(string userDn) bool userDeleted = false; try { - // Connecting to Ldap - using (LdapConnection connection = Connect()) - { - // Authenticate as write user - TryBind(connection, WriteUser, WriteUserPwd); - - try - { - //Delete the entry in the directory - connection.Delete(userDn); - userDeleted = true; - Log.WriteDebug("Delete user", $"User {userDn} deleted in {Address}:{Port}"); - } - catch (Exception exception) - { - Log.WriteInfo("Delete User", $"couldn't delete user in LDAP {Address}:{Port}: {exception.ToString()}"); - } - } - } + using LdapConnection connection = Connect(); + // Authenticate as write user + TryBind(connection, WriteUser, WriteUserPwd); + + try + { + //Delete the entry in the directory + connection.Delete(userDn); + userDeleted = true; + Log.WriteDebug("Delete user", $"User {userDn} deleted in {Address}:{Port}"); + } + catch (Exception exception) + { + Log.WriteInfo("Delete User", $"couldn't delete user in LDAP {Address}:{Port}: {exception}"); + } + } catch (Exception exception) { Log.WriteError($"Non-LDAP exception {Address}:{Port}", "Unexpected error while trying to delete user", exception); @@ -860,41 +812,38 @@ public string AddGroup(string groupName, bool ownerGroup) string groupDn = ""; try { - // Connecting to Ldap - using (LdapConnection connection = Connect()) + using LdapConnection connection = Connect(); + // Authenticate as write user + TryBind(connection, WriteUser, WriteUserPwd); + + groupDn = $"cn={groupName},{GroupSearchPath}"; + LdapAttributeSet attributeSet = new (); + attributeSet.Add(new LdapAttribute("objectclass", "groupofuniquenames")); + attributeSet.Add(new LdapAttribute("uniqueMember", "")); + if (ownerGroup) { - // Authenticate as write user - TryBind(connection, WriteUser, WriteUserPwd); - - groupDn = $"cn={groupName},{GroupSearchPath}"; - LdapAttributeSet attributeSet = new LdapAttributeSet(); - attributeSet.Add(new LdapAttribute("objectclass", "groupofuniquenames")); - attributeSet.Add(new LdapAttribute("uniqueMember", "")); - if (ownerGroup) - { - attributeSet.Add(new LdapAttribute("businessCategory", "ownergroup")); - } - - LdapEntry newEntry = new LdapEntry(groupDn, attributeSet); - - try - { - //Add the entry to the directory - connection.Add(newEntry); - groupAdded = true; - Log.WriteDebug("Add group", $"Group {groupName} added in {Address}:{Port}"); - } - catch (Exception exception) - { - Log.WriteInfo("Add Group", $"couldn't add group to LDAP {Address}:{Port}: {exception.ToString()}"); - } + attributeSet.Add(new LdapAttribute("businessCategory", "ownergroup")); } - } + + LdapEntry newEntry = new (groupDn, attributeSet); + + try + { + //Add the entry to the directory + connection.Add(newEntry); + groupAdded = true; + Log.WriteDebug("Add group", $"Group {groupName} added in {Address}:{Port}"); + } + catch (Exception exception) + { + Log.WriteInfo("Add Group", $"couldn't add group to LDAP {Address}:{Port}: {exception}"); + } + } catch (Exception exception) { Log.WriteError($"Non-LDAP exception {Address}:{Port}", "Unexpected error while trying to add group", exception); } - return (groupAdded ? groupDn : ""); + return groupAdded ? groupDn : ""; } /// @@ -910,30 +859,27 @@ public string UpdateGroup(string oldName, string newName) try { - // Connecting to Ldap - using (LdapConnection connection = Connect()) - { - // Authenticate as write user - TryBind(connection, WriteUser, WriteUserPwd); - - try - { - //Add the entry to the directory - connection.Rename(oldGroupDn, newGroupRdn, true); - groupUpdated = true; - Log.WriteDebug("Update group", $"Group {oldName} renamed to {newName} in {Address}:{Port}"); - } - catch (Exception exception) - { - Log.WriteInfo("Update Group", $"couldn't update group in LDAP {Address}:{Port}: {exception.ToString()}"); - } - } - } + using LdapConnection connection = Connect(); + // Authenticate as write user + TryBind(connection, WriteUser, WriteUserPwd); + + try + { + //Add the entry to the directory + connection.Rename(oldGroupDn, newGroupRdn, true); + groupUpdated = true; + Log.WriteDebug("Update group", $"Group {oldName} renamed to {newName} in {Address}:{Port}"); + } + catch (Exception exception) + { + Log.WriteInfo("Update Group", $"couldn't update group in LDAP {Address}:{Port}: {exception}"); + } + } catch (Exception exception) { Log.WriteError($"Non-LDAP exception {Address}:{Port}", "Unexpected error while trying to update group", exception); } - return (groupUpdated ? $"{newGroupRdn},{GroupSearchPath}" : ""); + return groupUpdated ? $"{newGroupRdn},{GroupSearchPath}" : ""; } /// @@ -946,26 +892,23 @@ public bool DeleteGroup(string groupName) bool groupDeleted = false; try { - // Connecting to Ldap - using (LdapConnection connection = Connect()) - { - // Authenticate as write user - TryBind(connection, WriteUser, WriteUserPwd); - - try - { - //Delete the entry in the directory - string groupDn = $"cn={groupName},{GroupSearchPath}"; - connection.Delete(groupDn); - groupDeleted = true; - Log.WriteDebug("Delete group", $"Group {groupName} deleted in {Address}:{Port}"); - } - catch (Exception exception) - { - Log.WriteInfo("Delete Group", $"couldn't delete group in LDAP {Address}:{Port}: {exception.ToString()}"); - } - } - } + using LdapConnection connection = Connect(); + // Authenticate as write user + TryBind(connection, WriteUser, WriteUserPwd); + + try + { + //Delete the entry in the directory + string groupDn = $"cn={groupName},{GroupSearchPath}"; + connection.Delete(groupDn); + groupDeleted = true; + Log.WriteDebug("Delete group", $"Group {groupName} deleted in {Address}:{Port}"); + } + catch (Exception exception) + { + Log.WriteInfo("Delete Group", $"couldn't delete group in LDAP {Address}:{Port}: {exception}"); + } + } catch (Exception exception) { Log.WriteError($"Non-LDAP exception {Address}:{Port}", "Unexpected error while trying to delete group", exception); @@ -999,8 +942,7 @@ public bool RemoveUserFromEntry(string userDn, string entry) /// true if user removed from all entries public bool RemoveUserFromAllEntries(string userDn) { - List dnList = new List(); - dnList.Add(userDn); // group memberships do not need to be regarded here + List dnList = [userDn]; // group memberships do not need to be regarded here List roles = GetRoles(dnList); bool allRemoved = true; foreach (var role in roles) @@ -1015,34 +957,31 @@ public bool RemoveUserFromAllEntries(string userDn) return allRemoved; } - private bool ModifyUserInEntry(string userDn, string entry, int LdapModification) + private bool ModifyUserInEntry(string userDn, string entry, int ldapModification) { bool userModified = false; try { - // Connecting to Ldap - using (LdapConnection connection = Connect()) - { - // Authenticate as write user - TryBind(connection, WriteUser, WriteUserPwd); - - // Add a new value to the description attribute - LdapAttribute attribute = new LdapAttribute("uniquemember", userDn); - LdapModification[] mods = { new LdapModification(LdapModification, attribute) }; - - try - { - //Modify the entry in the directory - connection.Modify(entry, mods); - userModified = true; - Log.WriteDebug("Modify Entry", $"Entry {entry} modified in {Address}:{Port}"); - } - catch (Exception exception) - { - Log.WriteInfo("Modify Entry", $"maybe entry doesn't exist in this LDAP {Address}:{Port}: {exception.ToString()}"); - } - } - } + using LdapConnection connection = Connect(); + // Authenticate as write user + TryBind(connection, WriteUser, WriteUserPwd); + + // Add a new value to the description attribute + LdapAttribute attribute = new("uniquemember", userDn); + LdapModification[] mods = [new(ldapModification, attribute)]; + + try + { + //Modify the entry in the directory + connection.Modify(entry, mods); + userModified = true; + Log.WriteDebug("Modify Entry", $"Entry {entry} modified in {Address}:{Port}"); + } + catch (Exception exception) + { + Log.WriteInfo("Modify Entry", $"maybe entry doesn't exist in this LDAP {Address}:{Port}: {exception}"); + } + } catch (Exception exception) { Log.WriteError($"Non-LDAP exception {Address}:{Port}", "Unexpected error while trying to modify user", exception); @@ -1058,34 +997,31 @@ public bool AddTenant(string tenantName) { Log.WriteInfo("Add Tenant", $"Trying to add Tenant: \"{tenantName}\""); bool tenantAdded = false; - string tenantDn = ""; try { - // Connecting to Ldap - using (LdapConnection connection = Connect()) - { - // Authenticate as write user - TryBind(connection, WriteUser, WriteUserPwd); - - tenantDn = $"ou={tenantName},{UserSearchPath}"; - LdapAttributeSet attributeSet = new LdapAttributeSet(); - attributeSet.Add(new LdapAttribute("objectclass", "organizationalUnit")); + using LdapConnection connection = Connect(); + // Authenticate as write user + TryBind(connection, WriteUser, WriteUserPwd); - LdapEntry newEntry = new LdapEntry(tenantDn, attributeSet); - - try - { - //Add the entry to the directory - connection.Add(newEntry); - tenantAdded = true; - Log.WriteDebug("Add tenant", $"Tenant {tenantName} added in {Address}:{Port}"); - } - catch (Exception exception) - { - Log.WriteInfo("Add Tenant", $"couldn't add tenant to LDAP {Address}:{Port}: {exception.ToString()}"); - } - } - } + LdapAttributeSet attributeSet = new () + { + new LdapAttribute("objectclass", "organizationalUnit") + }; + + LdapEntry newEntry = new (TenantNameToDn(tenantName), attributeSet); + + try + { + //Add the entry to the directory + connection.Add(newEntry); + tenantAdded = true; + Log.WriteDebug("Add tenant", $"Tenant {tenantName} added in {Address}:{Port}"); + } + catch (Exception exception) + { + Log.WriteInfo("Add Tenant", $"couldn't add tenant to LDAP {Address}:{Port}: {exception}"); + } + } catch (Exception exception) { Log.WriteError($"Non-LDAP exception {Address}:{Port}", "Unexpected error while trying to add tenant", exception); @@ -1103,32 +1039,33 @@ public bool DeleteTenant(string tenantName) bool tenantDeleted = false; try { - // Connecting to Ldap - using (LdapConnection connection = Connect()) - { - // Authenticate as write user - TryBind(connection, WriteUser, WriteUserPwd); - - try - { - string tenantDn = "ou=" + tenantName + "," + UserSearchPath; - - //Delete the entry in the directory - connection.Delete(tenantDn); - tenantDeleted = true; - Log.WriteDebug("Delete Tenant", $"tenant {tenantDn} deleted in {Address}:{Port}"); - } - catch (Exception exception) - { - Log.WriteInfo("Delete Tenant", $"couldn't delete tenant in LDAP {Address}:{Port}: {exception.ToString()}"); - } - } - } + using LdapConnection connection = Connect(); + // Authenticate as write user + TryBind(connection, WriteUser, WriteUserPwd); + + try + { + string tenantDn = TenantNameToDn(tenantName); + //Delete the entry in the directory + connection.Delete(tenantDn); + tenantDeleted = true; + Log.WriteDebug("Delete Tenant", $"tenant {tenantDn} deleted in {Address}:{Port}"); + } + catch (Exception exception) + { + Log.WriteInfo("Delete Tenant", $"couldn't delete tenant in LDAP {Address}:{Port}: {exception}"); + } + } catch (Exception exception) { Log.WriteError($"Non-LDAP exception {Address}:{Port}", "Unexpected error while trying to delete tenant", exception); } return tenantDeleted; } + + private string TenantNameToDn(string tenantName) + { + return $"ou={tenantName},{UserSearchPath}"; + } } } From 379fe708a7b5cc62ad021739ceeeee2431b9f5e1 Mon Sep 17 00:00:00 2001 From: abarz722 Date: Fri, 3 May 2024 14:18:18 +0200 Subject: [PATCH 19/23] some docu + cleanup --- .../files/sql/idempotent/fworch-texts.sql | 34 +++++++++++++++++-- .../files/FWO.Api.Client/Data/StateMatrix.cs | 17 +++++----- .../Pages/Help/HelpModellingSidebar.cshtml | 4 ++- .../Pages/Help/HelpModellingWorkflow.cshtml | 17 ++++++++++ 4 files changed, 59 insertions(+), 13 deletions(-) create mode 100644 roles/ui/files/FWO.UI/Pages/Help/HelpModellingWorkflow.cshtml diff --git a/roles/database/files/sql/idempotent/fworch-texts.sql b/roles/database/files/sql/idempotent/fworch-texts.sql index 47eaf9131..5ba9eb08c 100644 --- a/roles/database/files/sql/idempotent/fworch-texts.sql +++ b/roles/database/files/sql/idempotent/fworch-texts.sql @@ -5747,11 +5747,11 @@ INSERT INTO txt VALUES ('H9011', 'English', 'An application is - from the perspe INSERT INTO txt VALUES ('H9021', 'German', 'Verbindungen sind die Hauptbestandteile des Kommunikationsprofils. Es wird zwischen verschiedenen Arten von Verbindungen unterschieden:'); INSERT INTO txt VALUES ('H9021', 'English', 'Connections are the main components of the communication profile. There are different types of connections:'); INSERT INTO txt VALUES ('H9022', 'German', 'Schnittstellen: Sie dienen in erster Linie der Modellierung von (aus Sicht der Applikation) externen Verbindungen oder der Bündelung interner Objekte. - Es müssen in der Applikation neben dem Dienst entweder Quelle oder Ziel definiert werden. Die Schnittstellen werden in den anderen Applikationen - zur Auswahl angeboten und können dort in der Definition von eigenen Verbindungen verwendet werden. + Es müssen in der Applikation neben dem Dienst entweder Quelle oder Ziel definiert werden. Die Schnittstellen können durch Setzen des entsprechendenn Häkchens veröffentlicht und dadurch in den anderen Applikationen + zur Auswahl angeboten werden. Sie können dann dort in der Definition von eigenen Verbindungen verwendet werden. '); INSERT INTO txt VALUES ('H9022', 'English', 'Interfaces: They serve primarily the modelling of (relative to the application) external connections or the bundling of internal objects. - Besides the service either source or destination have to be defined in the application. The interfaces are offered to other applications to use + Besides the service either source or destination have to be defined in the application. The interfaces can be published by setting the respective flag and are then offered to other applications to use them in the definition of own connections. '); INSERT INTO txt VALUES ('H9023', 'German', 'Standard: Zentrale Objekte zur Modellierung der Kommunikationsverbindungen. Dabei müssen Quelle, Dienst und Ziel aus den in der Bibliothek @@ -5810,3 +5810,31 @@ INSERT INTO txt VALUES ('H9043', 'German', 'Dienstgruppen: In Dienstgruppen k&o INSERT INTO txt VALUES ('H9043', 'English', 'Service Groups: Simple services can be bundled in Service Groups. A name has to be given to them, comments can be added. Again definition can be done by the modeller, but also Service Groups predefined by the administrator can be used. '); +INSERT INTO txt VALUES ('H9051', 'German', 'Beantragung neuer Schnittstellen: Wenn externe Schnittstellen von anderen Applikationen benötigt werden, können diese über die entsprechende Schaltfläche in der Bibliothek beantragt werden. +
    +
  • Es erscheint ein Dialog, in dem die externe Applikation ausgewählt und eine Begründung eingetragen werden müssen, sowie das Häkchen, ob die Schnittstelle als Quelle oder Ziel genutzt werden soll.
  • +
  • Beim Abschicken der Anforderung wird +
      +
    • bei der externen Applikation automatisch eine Dummy-Schnittstelle angelegt, die dann in der eigenen Schnittstellen-Auswahl erscheint und direkt zur Erstellung eigener Verbindungen genutzt werden kann. + Sie wird in der Liste der eigenen Verbindungen mit Einträgen "Schnittstelle angefordert" in Quelle/Ziel und Dienst als solche gekennzeichnet.
    • +
    • der oder die für die externe Applikation Verantwortlichen per Email über den Antrag informiert.
    • +
    • im Workflow-Modul ein Ticket mit dem Antrag erstellt. Je nach Konfiguration des Workflows kann hier der Auftrag abgelehnt, an andere Applikationen weitergeleitet, einzelnen Bearbeitern zugewiesen oder mit Kommentaren versehen werden.
    • +
    +
  • +
  • Wird die Schnittstelle auf der Gegenseite modelliert und veröffentlicht, wandelt sich auch die eigene nutzende Verbindung automatisch in eine "normale" Verbindung um, eine weiteres Eingreifen des Antragstellers ist nicht mehr notwendig.
  • +
+'); +INSERT INTO txt VALUES ('H9051', 'English', 'Request new interface: If external interfaces from other applications are needed, they can be requested via a button in the library. +
    +
  • A dialogue is displayed to select the external Application. A reason field has to be filled as well as the checkbox, if the interface should be used as source or destination.
  • +
  • If the request is submitted +
      +
    • a dummy interface is created automatically at the target application, which then appears in the own interface selection in the library and can be used for the definition of the own connection. + It is marked as such by the text "Interface requested" in Source/Destination and Service in the list of own the connections.
    • +
    • the responsible(s) of the external Application is informed about the request by email.
    • +
    • a ticket in the Workflow module is created. Depending on the configuration of the workflow, the request can be rejected, forwarded to other applications, assigned to aperson in charge or commented.
    • +
    +
  • +
  • When the requested interface is modelled and published on the other side, the own using connection is changed to a "regular" connection automatically, further action is not necessary.
  • +
+'); diff --git a/roles/lib/files/FWO.Api.Client/Data/StateMatrix.cs b/roles/lib/files/FWO.Api.Client/Data/StateMatrix.cs index 097279f46..6a6f5e518 100644 --- a/roles/lib/files/FWO.Api.Client/Data/StateMatrix.cs +++ b/roles/lib/files/FWO.Api.Client/Data/StateMatrix.cs @@ -1,5 +1,4 @@ using FWO.Api.Client.Queries; -using FWO.GlobalConstants; using FWO.Api.Data; using System.Text.Json.Serialization; using Newtonsoft.Json; @@ -20,10 +19,10 @@ public enum WorkflowPhases public class StateMatrix { [JsonProperty("matrix"), JsonPropertyName("matrix")] - public Dictionary> Matrix { get; set; } = new (); + public Dictionary> Matrix { get; set; } = []; [JsonProperty("derived_states"), JsonPropertyName("derived_states")] - public Dictionary DerivedStates { get; set; } = new (); + public Dictionary DerivedStates { get; set; } = []; [JsonProperty("lowest_input_state"), JsonPropertyName("lowest_input_state")] public int LowestInputState { get; set; } @@ -37,7 +36,7 @@ public class StateMatrix [JsonProperty("active"), JsonPropertyName("active")] public bool Active { get; set; } - public Dictionary PhaseActive = new Dictionary(); + public Dictionary PhaseActive = []; public bool IsLastActivePhase = true; public int MinImplTasksNeeded; @@ -77,7 +76,7 @@ public bool getNextActivePhase(ref WorkflowPhases phase) public List getAllowedTransitions(int stateIn) { - return Matrix.ContainsKey(stateIn) ? Matrix[stateIn] : new (); + return Matrix.ContainsKey(stateIn) ? Matrix[stateIn] : []; } public int getDerivedStateFromSubStates(List statesIn) @@ -86,7 +85,7 @@ public int getDerivedStateFromSubStates(List statesIn) { return 0; } - int stateOut = 0; + int stateOut; int backAssignedState = LowestInputState; int initState = 0; int inWorkState = LowestEndState; @@ -160,7 +159,7 @@ public int getDerivedStateFromSubStates(List statesIn) public class GlobalStateMatrix { [JsonProperty("config_value"), JsonPropertyName("config_value")] - public Dictionary GlobalMatrix { get; set; } = new (); + public Dictionary GlobalMatrix { get; set; } = []; public async Task Init(ApiConnection apiConnection, TaskType taskType = TaskType.master, bool reset = false) @@ -198,11 +197,11 @@ public class GlobalStateMatrixHelper public class StateMatrixDict { - public Dictionary Matrices { get; set; } = new Dictionary(); + public Dictionary Matrices { get; set; } = []; public async Task Init(WorkflowPhases phase, ApiConnection apiConnection) { - Matrices = new Dictionary(); + Matrices = []; foreach(TaskType taskType in Enum.GetValues(typeof(TaskType))) { Matrices.Add(taskType.ToString(), new StateMatrix()); diff --git a/roles/ui/files/FWO.UI/Pages/Help/HelpModellingSidebar.cshtml b/roles/ui/files/FWO.UI/Pages/Help/HelpModellingSidebar.cshtml index 679a4c9b6..c710dd21b 100644 --- a/roles/ui/files/FWO.UI/Pages/Help/HelpModellingSidebar.cshtml +++ b/roles/ui/files/FWO.UI/Pages/Help/HelpModellingSidebar.cshtml @@ -21,7 +21,9 @@ @(userConfig.GetText("services")) - + + @(userConfig.GetText("workflow")) + diff --git a/roles/ui/files/FWO.UI/Pages/Help/HelpModellingWorkflow.cshtml b/roles/ui/files/FWO.UI/Pages/Help/HelpModellingWorkflow.cshtml new file mode 100644 index 000000000..ae2b73ec1 --- /dev/null +++ b/roles/ui/files/FWO.UI/Pages/Help/HelpModellingWorkflow.cshtml @@ -0,0 +1,17 @@ +@page "/help/modelling/workflow" +@model FWO.Ui.Pages.Help.MainModel +@{ + Layout = "HelpLayout"; +} +@section sidebar{ + @{ + await Html.RenderPartialAsync("HelpModellingSidebar.cshtml"); + } +} +@using FWO.Config.Api +@inject UserConfig userConfig + +
+

@userConfig.GetText("workflow")

+ @(Html.Raw(userConfig.GetText("H9051"))) +
From 498472bb020620055e076a6e24f1df1b526c5dba Mon Sep 17 00:00:00 2001 From: abarz722 Date: Fri, 3 May 2024 14:20:06 +0200 Subject: [PATCH 20/23] refine actions --- .../modelling/updateConnectionOwner.graphql | 12 +++++ .../modelling/updateConnectionPublish.graphql | 14 ++++++ .../Queries/ModellingQueries.cs | 4 ++ .../ui/files/FWO.UI/Services/ActionHandler.cs | 50 ++++++++++++++----- .../Services/ModellingConnectionHandler.cs | 2 +- 5 files changed, 69 insertions(+), 13 deletions(-) create mode 100644 roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateConnectionOwner.graphql create mode 100644 roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateConnectionPublish.graphql diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateConnectionOwner.graphql b/roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateConnectionOwner.graphql new file mode 100644 index 000000000..fdf9b53b6 --- /dev/null +++ b/roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateConnectionOwner.graphql @@ -0,0 +1,12 @@ +mutation updateConnectionOwner( + $id: Int! + $appId: Int + ) { + update_modelling_connection_by_pk( + pk_columns: { id: $id } + _set: { + app_id: $appId + }) { + UpdatedId: id + } +} diff --git a/roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateConnectionPublish.graphql b/roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateConnectionPublish.graphql new file mode 100644 index 000000000..9f5589d8d --- /dev/null +++ b/roles/lib/files/FWO.Api.Client/APIcalls/modelling/updateConnectionPublish.graphql @@ -0,0 +1,14 @@ +mutation updateConnectionPublish( + $id: Int! + $isPublished: Boolean + $isRequested: Boolean + ) { + update_modelling_connection_by_pk( + pk_columns: { id: $id } + _set: { + is_requested: $isRequested + is_published: $isPublished + }) { + UpdatedId: id + } +} diff --git a/roles/lib/files/FWO.Api.Client/Queries/ModellingQueries.cs b/roles/lib/files/FWO.Api.Client/Queries/ModellingQueries.cs index 1b6458b46..1cced1946 100644 --- a/roles/lib/files/FWO.Api.Client/Queries/ModellingQueries.cs +++ b/roles/lib/files/FWO.Api.Client/Queries/ModellingQueries.cs @@ -34,6 +34,8 @@ public class ModellingQueries : Queries public static readonly string getCommonServices; public static readonly string newConnection; public static readonly string updateConnection; + public static readonly string updateConnectionOwner; + public static readonly string updateConnectionPublish; public static readonly string deleteConnection; public static readonly string addAppServerToConnection; public static readonly string removeAppServerFromConnection; @@ -122,6 +124,8 @@ static ModellingQueries() getCommonServices = connectionDetailsFragment + File.ReadAllText(QueryPath + "modelling/getCommonServices.graphql"); newConnection = File.ReadAllText(QueryPath + "modelling/newConnection.graphql"); updateConnection = File.ReadAllText(QueryPath + "modelling/updateConnection.graphql"); + updateConnectionOwner = File.ReadAllText(QueryPath + "modelling/updateConnectionOwner.graphql"); + updateConnectionPublish = File.ReadAllText(QueryPath + "modelling/updateConnectionPublish.graphql"); deleteConnection = File.ReadAllText(QueryPath + "modelling/deleteConnection.graphql"); addAppServerToConnection = File.ReadAllText(QueryPath + "modelling/addAppServerToConnection.graphql"); removeAppServerFromConnection = File.ReadAllText(QueryPath + "modelling/removeAppServerFromConnection.graphql"); diff --git a/roles/ui/files/FWO.UI/Services/ActionHandler.cs b/roles/ui/files/FWO.UI/Services/ActionHandler.cs index 7ad73174b..8389a7276 100644 --- a/roles/ui/files/FWO.UI/Services/ActionHandler.cs +++ b/roles/ui/files/FWO.UI/Services/ActionHandler.cs @@ -119,7 +119,7 @@ public async Task PerformAction(RequestStateAction action, RequestStatefulObject await UpdateConnectionOwner(owner, ticketId); break; case nameof(StateActionTypes.UpdateConnectionRelease): - await UpdateConnectionRelease(action, owner); + await UpdateConnectionPublish(owner, ticketId); break; case nameof(StateActionTypes.DisplayConnection): await DisplayConnection(statefulObject, scope); @@ -189,16 +189,9 @@ public async Task UpdateConnectionOwner(FwoOwner? owner, long? ticketId) var Variables = new { id = conn.Id, - name = conn.Name, - appId = owner.Id, - reason = conn.Reason, - isInterface = conn.IsInterface, - usedInterfaceId = conn.UsedInterfaceId, - isRequested = conn.IsRequested, - isPublished = conn.IsPublished, - commonSvc = conn.IsCommonService + appId = owner.Id }; - await apiConnection.SendQueryAsync(ModellingQueries.updateConnection, Variables); + await apiConnection.SendQueryAsync(ModellingQueries.updateConnectionOwner, Variables); await ModellingHandlerBase.LogChange(ModellingTypes.ChangeType.Update, ModellingTypes.ModObjectType.Connection, conn.Id, $"Updated {(conn.IsInterface? "Interface" : "Connection")}: {conn.Name}", apiConnection, requestHandler.userConfig, owner.Id, DefaultInit.DoNothing); } @@ -212,9 +205,42 @@ await ModellingHandlerBase.LogChange(ModellingTypes.ChangeType.Update, Modelling } } - public async Task UpdateConnectionRelease(RequestStateAction action, FwoOwner? owner) + public async Task UpdateConnectionPublish(FwoOwner? owner, long? ticketId) { - Log.WriteDebug("UpdateConnectionRelease", "Perform Action"); + Log.WriteDebug("UpdateConnectionPublish", "Perform Action"); + try + { + if(owner != null && ticketId != null) // todo: role check + { + apiConnection.SetRole(Roles.Modeller); + List Connections = await apiConnection.SendQueryAsync>(ModellingQueries.getConnectionsByTicketId, new { ticketId }); + foreach(var conn in Connections) + { + if(conn.IsRequested && !conn.IsPublished) + { + ConnHandler = new (apiConnection, requestHandler.userConfig, owner, new(), conn, true, false, DefaultInit.DoNothing, DefaultInit.DoNothing, false); + await ConnHandler.PartialInit(); + if(ConnHandler.CheckConn()) + { + var Variables = new + { + id = conn.Id, + isRequested = false, + isPublished = true + }; + await apiConnection.SendQueryAsync(ModellingQueries.updateConnectionPublish, Variables); + await ModellingHandlerBase.LogChange(ModellingTypes.ChangeType.Update, ModellingTypes.ModObjectType.Connection, conn.Id, + $"Updated {(conn.IsInterface? "Interface" : "Connection")}: {conn.Name}", apiConnection, requestHandler.userConfig, owner.Id, DefaultInit.DoNothing); + } + } + } + apiConnection.SwitchBack(); + } + } + catch(Exception exc) + { + Log.WriteError("Update Connection Publish", $"Could not publish connection: ", exc); + } } public async Task DisplayConnection(RequestStatefulObject statefulObject, RequestObjectScopes scope) diff --git a/roles/ui/files/FWO.UI/Services/ModellingConnectionHandler.cs b/roles/ui/files/FWO.UI/Services/ModellingConnectionHandler.cs index ad6244be2..a08702eca 100644 --- a/roles/ui/files/FWO.UI/Services/ModellingConnectionHandler.cs +++ b/roles/ui/files/FWO.UI/Services/ModellingConnectionHandler.cs @@ -923,7 +923,7 @@ public async Task Save() return false; } - private bool CheckConn() + public bool CheckConn() { if(ActConn.Name == null || ActConn.Name == "" || ActConn.Reason == null || ActConn.Reason == "") { From 1c14104095cf0b2c1afc81ae007ff25d9ada9e35 Mon Sep 17 00:00:00 2001 From: abarz722 Date: Fri, 3 May 2024 14:20:52 +0200 Subject: [PATCH 21/23] workflow fixes --- .../Request/DisplayImplementationTask.razor | 8 +++----- .../Pages/Request/DisplayRequestTask.razor | 18 +++++++++--------- .../FWO.UI/Pages/Request/DisplayTicket.razor | 8 +++----- 3 files changed, 15 insertions(+), 19 deletions(-) diff --git a/roles/ui/files/FWO.UI/Pages/Request/DisplayImplementationTask.razor b/roles/ui/files/FWO.UI/Pages/Request/DisplayImplementationTask.razor index 69c0631c5..abd72225c 100644 --- a/roles/ui/files/FWO.UI/Pages/Request/DisplayImplementationTask.razor +++ b/roles/ui/files/FWO.UI/Pages/Request/DisplayImplementationTask.razor @@ -202,14 +202,12 @@
-
+
@foreach(var action in offeredActions) { @if(ReqHandler.ImplementImplTaskMode || ReqHandler.ReviewImplTaskMode || RequestStateAction.IsReadonlyType(action.ActionType)) { -
- -
+ } }
@@ -381,7 +379,7 @@ private async Task PerformAction(RequestStateAction action) { - await ReqHandler.ActionHandler.PerformAction(action, ReqHandler.ActImplTask, RequestObjectScopes.ImplementationTask); + await ReqHandler.ActionHandler.PerformAction(action, ReqHandler.ActImplTask, RequestObjectScopes.ImplementationTask, actOwner, ReqHandler.ActReqTask.TicketId); } private void RequestAssignOwner() diff --git a/roles/ui/files/FWO.UI/Pages/Request/DisplayRequestTask.razor b/roles/ui/files/FWO.UI/Pages/Request/DisplayRequestTask.razor index be27d0eb0..fc1eb18d5 100644 --- a/roles/ui/files/FWO.UI/Pages/Request/DisplayRequestTask.razor +++ b/roles/ui/files/FWO.UI/Pages/Request/DisplayRequestTask.razor @@ -255,16 +255,16 @@
} -
- @foreach(var action in offeredActions) - { - @if(ReqHandler.EditReqTaskMode || ReqHandler.PlanReqTaskMode || ReqHandler.ApproveReqTaskMode || RequestStateAction.IsReadonlyType(action.ActionType)) +
+
+ @foreach(var action in offeredActions) { -
- -
+ @if(ReqHandler.EditReqTaskMode || ReqHandler.PlanReqTaskMode || ReqHandler.ApproveReqTaskMode || RequestStateAction.IsReadonlyType(action.ActionType)) + { + + } } - } +
@if(Phase == WorkflowPhases.planning || ReqHandler.ActReqTask.ImplementationTasks.Count > 0) @@ -492,7 +492,7 @@ private async Task PerformAction(RequestStateAction action) { - await ReqHandler.ActionHandler.PerformAction(action, ReqHandler.ActReqTask, RequestObjectScopes.RequestTask); + await ReqHandler.ActionHandler.PerformAction(action, ReqHandler.ActReqTask, RequestObjectScopes.RequestTask, actOwner, ReqHandler.ActReqTask.TicketId); await Reset(); } diff --git a/roles/ui/files/FWO.UI/Pages/Request/DisplayTicket.razor b/roles/ui/files/FWO.UI/Pages/Request/DisplayTicket.razor index 74da0f632..34e6364e3 100644 --- a/roles/ui/files/FWO.UI/Pages/Request/DisplayTicket.razor +++ b/roles/ui/files/FWO.UI/Pages/Request/DisplayTicket.razor @@ -90,14 +90,12 @@
-
+
@foreach(var action in offeredActions) { @if(!ReqHandler.ReadOnlyMode || RequestStateAction.IsReadonlyType(action.ActionType)) { -
- -
+ } }
@@ -262,7 +260,7 @@ private async Task PerformAction(RequestStateAction action) { - await ReqHandler.ActionHandler.PerformAction(action, ReqHandler.ActTicket, RequestObjectScopes.Ticket); + await ReqHandler.ActionHandler.PerformAction(action, ReqHandler.ActTicket, RequestObjectScopes.Ticket, null, ReqHandler.ActTicket.Id); await ResetParent(); } From 629b5e062b1c5f126805076a65818aa6776a10c8 Mon Sep 17 00:00:00 2001 From: abarz722 Date: Fri, 3 May 2024 15:43:08 +0200 Subject: [PATCH 22/23] fix refresh error --- .../ui/files/FWO.UI/Services/ModellingServiceGroupHandler.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/roles/ui/files/FWO.UI/Services/ModellingServiceGroupHandler.cs b/roles/ui/files/FWO.UI/Services/ModellingServiceGroupHandler.cs index 19c3a67d5..4f3053184 100644 --- a/roles/ui/files/FWO.UI/Services/ModellingServiceGroupHandler.cs +++ b/roles/ui/files/FWO.UI/Services/ModellingServiceGroupHandler.cs @@ -49,7 +49,10 @@ public async Task RefreshActServiceGroup() { try { - ActServiceGroup = await apiConnection.SendQueryAsync(ModellingQueries.getServiceGroupById, new { id = ActServiceGroup.Id }); + if(ActServiceGroup.Id > 0) + { + ActServiceGroup = await apiConnection.SendQueryAsync(ModellingQueries.getServiceGroupById, new { id = ActServiceGroup.Id }); + } await RefreshParent(); } catch (Exception exception) From 2dac48d4b1c1f3b1c49b71e6cc06c6b17865efe9 Mon Sep 17 00:00:00 2001 From: abarz722 Date: Mon, 6 May 2024 14:24:27 +0200 Subject: [PATCH 23/23] no translation of obj type --- roles/ui/files/FWO.UI/Shared/ObjectGroup.razor | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/roles/ui/files/FWO.UI/Shared/ObjectGroup.razor b/roles/ui/files/FWO.UI/Shared/ObjectGroup.razor index cfeb0a370..906607f9e 100644 --- a/roles/ui/files/FWO.UI/Shared/ObjectGroup.razor +++ b/roles/ui/files/FWO.UI/Shared/ObjectGroup.razor @@ -52,7 +52,7 @@ - + @@ -120,7 +120,7 @@ - + @if (context.Type.Name != ObjectType.Group) { @@ -196,7 +196,7 @@ - + @if (context.Type != null && context.Type.Name == ObjectType.Group && context.UserGroups != null && context.UserGroupFlats != null)