From 3df7399f1979c54cf593f98aaffe9a41907f7bd0 Mon Sep 17 00:00:00 2001 From: Phil Schneider Date: Thu, 12 Sep 2024 16:35:13 +0200 Subject: [PATCH] feat(provisioning): change to the new div provisioning api Refs: #79 --- charts/dim/templates/cronjob-processes.yaml | 46 +- charts/dim/templates/deployment.yaml | 28 +- charts/dim/templates/secret.yaml | 6 +- charts/dim/values.yaml | 23 +- environments/helm-values/values-int.yaml | 17 +- src/Dim.sln | 14 + .../Api/Cf/AddSpaceRoleToUserRequest.cs | 50 - src/clients/Dim.Clients/Api/Cf/CfClient.cs | 287 ------ .../Dim.Clients/Api/Cf/CreateCfeRequest.cs | 53 - .../CfClientServiceExtensions.cs | 44 - .../Api/Cf/DependencyInjection/CfSettings.cs | 30 - src/clients/Dim.Clients/Api/Cf/ICfClient.cs | 34 - .../Dim.Clients/Api/Cf/ServicePlanResponse.cs | 140 --- .../Dim.Clients/Api/Cf/SpaceResponse.cs | 31 - .../Api/Dim/ApplicationResponse.cs | 31 - .../Api/Dim/CreateApplicationRequest.cs | 37 - .../Api/Dim/CreateCompanyIdentityRequest.cs | 65 -- src/clients/Dim.Clients/Api/Dim/DimClient.cs | 135 +-- src/clients/Dim.Clients/Api/Dim/IDimClient.cs | 7 +- .../CompanyData.cs} | 11 +- .../Dim/{ => Models}/StatusListResponse.cs | 2 +- .../DirectoryClientServiceExtensions.cs | 44 - .../DependencyInjection/DirectorySettings.cs | 30 - .../Api/Directories/DirectoryClient.cs | 76 -- .../Api/Directories/DirectoryRequest.cs | 35 - .../Api/Directories/IDirectoryClient.cs | 26 - .../ProvisioningClientServiceExtensions.cs | 15 +- .../ProvisioningSettings.cs | 2 +- .../IProvisioningClient.cs | 7 +- .../Api/Div/Models/CreateOperationRequest.cs | 73 ++ .../Api/Div/Models/OperationResponse.cs | 58 ++ .../Dim.Clients/Api/Div/ProvisioningClient.cs | 132 +++ .../Entitlements/CreateSubAccountRequest.cs | 39 - .../EntitlementClientServiceExtensions.cs | 44 - .../EntitlementSettings.cs | 29 - .../Api/Entitlements/EntitlementClient.cs | 53 - .../Api/Entitlements/IEntitlementClient.cs | 28 - .../Api/Provisioning/CreateCfeRequest.cs | 36 - .../Api/Provisioning/ProvisioningClient.cs | 62 -- .../Services/CreateServiceInstanceRequest.cs | 71 -- .../ServiceClientServiceExtensions.cs | 36 - .../Api/Services/IServiceClient.cs | 30 - .../Dim.Clients/Api/Services/ServiceClient.cs | 146 --- .../SubAccounts/CreateSubAccountRequest.cs | 51 - .../SubAccountClientServiceExtensions.cs | 44 - .../DependencyInjection/SubAccountSettings.cs | 29 - .../Api/SubAccounts/ISubAccountClient.cs | 30 - .../Api/SubAccounts/SubAccountClient.cs | 114 --- .../Api/SubAccounts/UsedForProduction.cs | 28 - .../SubscriptionClientServiceExtensions.cs | 36 - .../Api/Subscriptions/ISubscriptionClient.cs | 28 - .../Api/Subscriptions/SubscriptionClient.cs | 55 -- src/clients/Dim.Clients/Dim.Clients.csproj | 1 + src/clients/Dim.Clients/Token/AuthResponse.cs | 10 - .../Token/BasicAuthTokenService.cs | 55 +- .../Token/GetBasicTokenSettings.cs | 10 - .../Token/IBasicAuthTokenService.cs | 1 - src/database/Dim.DbAccess/DimRepositories.cs | 48 +- .../Extensions/WalletDataExtensions.cs | 38 + src/database/Dim.DbAccess/IDimRepositories.cs | 10 - .../Dim.DbAccess/Models/WalletData.cs | 9 + .../Repositories/ITenantRepository.cs | 23 +- .../Repositories/ProcessStepRepository.cs | 32 +- .../Repositories/TenantRepository.cs | 190 ++-- .../Dim.Entities/Entities/ProcessStep.cs | 26 +- src/database/Dim.Entities/Entities/Tenant.cs | 26 +- .../Dim.Entities/Enums/ProcessStepTypeId.cs | 24 +- ...40912045058_AddDivProvisioning.Designer.cs | 515 ++++++++++ .../20240912045058_AddDivProvisioning.cs | 349 +++++++ .../Migrations/DimDbContextModelSnapshot.cs | 118 +-- src/database/Dim.Migrations/Program.cs | 2 +- .../DimProcessCollectionExtensions.cs | 2 +- .../DimProcessTypeExecutor.cs | 57 +- .../TechnicalUserProcessTypeExecutor.cs | 4 +- .../Callback/CallbackService.cs | 9 +- .../Callback/ICallbackService.cs | 3 +- .../DimHandlerExtensions.cs | 16 +- .../DependencyInjection/DimHandlerSettings.cs | 20 +- .../DimProcess.Library/DimProcessHandler.cs | 501 +++------- .../DimProcess.Library/IDimProcessHandler.cs | 20 +- .../TechnicalUserProcessHandler.cs | 100 +- .../Processes.Library}/VerifyProcessData.cs | 4 +- .../IProcessExecutor.cs | 2 +- .../IProcessTypeExecutor.cs | 2 +- .../ProcessExecutionService.cs | 2 +- .../ProcessExecutionServiceExtensions.cs | 2 +- .../ProcessExecutionServiceSettings.cs | 2 +- .../ProcessExecutor.cs | 2 +- src/processes/Processes.Worker/Program.cs | 2 +- .../Processes.Worker/appsettings.json | 25 +- .../Dim.Web/BusinessLogic/DimBusinessLogic.cs | 64 +- src/web/Dim.Web/BusinessLogic/DimSettings.cs | 8 + .../ErrorHandling/DimErrorMessageContainer.cs | 6 +- .../Extensions/ServiceCollectionExtensions.cs | 3 +- src/web/Dim.Web/Program.cs | 3 +- src/web/Dim.Web/appsettings.json | 18 +- .../Dim.Clients.Tests.csproj | 48 + .../Dim.Clients.Tests/DimClientTests.cs | 192 ++++ .../Extensions/AutoFixtureExtensions.cs | 27 + .../ProvisioningClientTests.cs | 139 +++ .../Dim.DbAccess.Tests.csproj | 63 ++ .../DimRepositoriesTests.cs | 139 +++ .../Extensions/WalletDataExtensionsTests.cs | 81 ++ .../ProcessStepRepositoryTests.cs | 358 +++++++ .../Seeder/BatchInsertSeeder.cs | 92 ++ .../Seeder/Data/process_steps.json | 50 + .../Seeder/Data/processes.json | 26 + .../Seeder/Data/technical_users.json | 16 + .../Seeder/Data/tenants.json | 23 + .../Dim.DbAccess.Tests/Setup/TestDbFixture.cs | 104 ++ .../TenantRepositoryTests.cs | 472 +++++++++ .../DimProcessTypeExecutorTests.cs | 108 +-- .../TechnicalUserProcessTypeExecutorTests.cs | 257 +++++ .../CallbackServiceTests.cs | 6 +- .../DimProcessHandlerTests.cs | 904 ++++-------------- .../TechnicalUserProcessHandlerTests.cs | 106 +- .../ProcessExecutionServiceTests.cs | 1 - .../ProcessExecutorTests.cs | 1 - .../Dim.Web.Tests/DimBusinessLogicTests.cs | 315 +++++- 119 files changed, 4362 insertions(+), 4108 deletions(-) delete mode 100644 src/clients/Dim.Clients/Api/Cf/AddSpaceRoleToUserRequest.cs delete mode 100644 src/clients/Dim.Clients/Api/Cf/CfClient.cs delete mode 100644 src/clients/Dim.Clients/Api/Cf/CreateCfeRequest.cs delete mode 100644 src/clients/Dim.Clients/Api/Cf/DependencyInjection/CfClientServiceExtensions.cs delete mode 100644 src/clients/Dim.Clients/Api/Cf/DependencyInjection/CfSettings.cs delete mode 100644 src/clients/Dim.Clients/Api/Cf/ICfClient.cs delete mode 100644 src/clients/Dim.Clients/Api/Cf/ServicePlanResponse.cs delete mode 100644 src/clients/Dim.Clients/Api/Cf/SpaceResponse.cs delete mode 100644 src/clients/Dim.Clients/Api/Dim/ApplicationResponse.cs delete mode 100644 src/clients/Dim.Clients/Api/Dim/CreateApplicationRequest.cs delete mode 100644 src/clients/Dim.Clients/Api/Dim/CreateCompanyIdentityRequest.cs rename src/clients/Dim.Clients/Api/Dim/{CompanyIdentityPatch.cs => Models/CompanyData.cs} (77%) rename src/clients/Dim.Clients/Api/Dim/{ => Models}/StatusListResponse.cs (98%) delete mode 100644 src/clients/Dim.Clients/Api/Directories/DependencyInjection/DirectoryClientServiceExtensions.cs delete mode 100644 src/clients/Dim.Clients/Api/Directories/DependencyInjection/DirectorySettings.cs delete mode 100644 src/clients/Dim.Clients/Api/Directories/DirectoryClient.cs delete mode 100644 src/clients/Dim.Clients/Api/Directories/DirectoryRequest.cs delete mode 100644 src/clients/Dim.Clients/Api/Directories/IDirectoryClient.cs rename src/clients/Dim.Clients/Api/{Provisioning => Div}/DependencyInjection/ProvisioningClientServiceExtensions.cs (71%) rename src/clients/Dim.Clients/Api/{Provisioning => Div}/DependencyInjection/ProvisioningSettings.cs (95%) rename src/clients/Dim.Clients/Api/{Provisioning => Div}/IProvisioningClient.cs (73%) create mode 100644 src/clients/Dim.Clients/Api/Div/Models/CreateOperationRequest.cs create mode 100644 src/clients/Dim.Clients/Api/Div/Models/OperationResponse.cs create mode 100644 src/clients/Dim.Clients/Api/Div/ProvisioningClient.cs delete mode 100644 src/clients/Dim.Clients/Api/Entitlements/CreateSubAccountRequest.cs delete mode 100644 src/clients/Dim.Clients/Api/Entitlements/DependencyInjection/EntitlementClientServiceExtensions.cs delete mode 100644 src/clients/Dim.Clients/Api/Entitlements/DependencyInjection/EntitlementSettings.cs delete mode 100644 src/clients/Dim.Clients/Api/Entitlements/EntitlementClient.cs delete mode 100644 src/clients/Dim.Clients/Api/Entitlements/IEntitlementClient.cs delete mode 100644 src/clients/Dim.Clients/Api/Provisioning/CreateCfeRequest.cs delete mode 100644 src/clients/Dim.Clients/Api/Provisioning/ProvisioningClient.cs delete mode 100644 src/clients/Dim.Clients/Api/Services/CreateServiceInstanceRequest.cs delete mode 100644 src/clients/Dim.Clients/Api/Services/DependencyInjection/ServiceClientServiceExtensions.cs delete mode 100644 src/clients/Dim.Clients/Api/Services/IServiceClient.cs delete mode 100644 src/clients/Dim.Clients/Api/Services/ServiceClient.cs delete mode 100644 src/clients/Dim.Clients/Api/SubAccounts/CreateSubAccountRequest.cs delete mode 100644 src/clients/Dim.Clients/Api/SubAccounts/DependencyInjection/SubAccountClientServiceExtensions.cs delete mode 100644 src/clients/Dim.Clients/Api/SubAccounts/DependencyInjection/SubAccountSettings.cs delete mode 100644 src/clients/Dim.Clients/Api/SubAccounts/ISubAccountClient.cs delete mode 100644 src/clients/Dim.Clients/Api/SubAccounts/SubAccountClient.cs delete mode 100644 src/clients/Dim.Clients/Api/SubAccounts/UsedForProduction.cs delete mode 100644 src/clients/Dim.Clients/Api/Subscriptions/DependencyInjection/SubscriptionClientServiceExtensions.cs delete mode 100644 src/clients/Dim.Clients/Api/Subscriptions/ISubscriptionClient.cs delete mode 100644 src/clients/Dim.Clients/Api/Subscriptions/SubscriptionClient.cs create mode 100644 src/database/Dim.DbAccess/Extensions/WalletDataExtensions.cs create mode 100644 src/database/Dim.DbAccess/Models/WalletData.cs create mode 100644 src/database/Dim.Migrations/Migrations/20240912045058_AddDivProvisioning.Designer.cs create mode 100644 src/database/Dim.Migrations/Migrations/20240912045058_AddDivProvisioning.cs rename src/{database/Dim.Entities/Entities => processes/Processes.Library}/VerifyProcessData.cs (94%) create mode 100644 tests/clients/Dim.Clients.Tests/Dim.Clients.Tests.csproj create mode 100644 tests/clients/Dim.Clients.Tests/DimClientTests.cs create mode 100644 tests/clients/Dim.Clients.Tests/Extensions/AutoFixtureExtensions.cs create mode 100644 tests/clients/Dim.Clients.Tests/ProvisioningClientTests.cs create mode 100644 tests/database/Dim.DbAccess.Tests/Dim.DbAccess.Tests.csproj create mode 100644 tests/database/Dim.DbAccess.Tests/DimRepositoriesTests.cs create mode 100644 tests/database/Dim.DbAccess.Tests/Extensions/WalletDataExtensionsTests.cs create mode 100644 tests/database/Dim.DbAccess.Tests/ProcessStepRepositoryTests.cs create mode 100644 tests/database/Dim.DbAccess.Tests/Seeder/BatchInsertSeeder.cs create mode 100644 tests/database/Dim.DbAccess.Tests/Seeder/Data/process_steps.json create mode 100644 tests/database/Dim.DbAccess.Tests/Seeder/Data/processes.json create mode 100644 tests/database/Dim.DbAccess.Tests/Seeder/Data/technical_users.json create mode 100644 tests/database/Dim.DbAccess.Tests/Seeder/Data/tenants.json create mode 100644 tests/database/Dim.DbAccess.Tests/Setup/TestDbFixture.cs create mode 100644 tests/database/Dim.DbAccess.Tests/TenantRepositoryTests.cs create mode 100644 tests/processes/DimProcess.Executor.Tests/TechnicalUserProcessTypeExecutorTests.cs diff --git a/charts/dim/templates/cronjob-processes.yaml b/charts/dim/templates/cronjob-processes.yaml index 64f2588..80efc84 100644 --- a/charts/dim/templates/cronjob-processes.yaml +++ b/charts/dim/templates/cronjob-processes.yaml @@ -67,36 +67,34 @@ spec: - name: "CONNECTIONSTRINGS__DIMDB" value: "Server={{ .Values.externalDatabase.host }};Database={{ .Values.externalDatabase.database }};Port={{ .Values.externalDatabase.port }};User Id={{ .Values.externalDatabase.username }};Password=$(DIM_PASSWORD);Ssl Mode={{ .Values.dbConnection.sslMode }};" {{- end }} - - name: "DIM__ADMINMAIL" - value: "{{ .Values.processesworker.dim.adminMail }}" - - name: "DIM__ROOTDIRECTORYID" - value: "{{ .Values.dim.rootDirectoryId }}" - - name: "DIM__CLIENTIDCISCENTRAL" - value: "{{ .Values.processesworker.dim.clientIdCisCentral }}" - - name: "DIM__CLIENTSECRETCISCENTRAL" + - name: "DIM__APPLICATIONNAME" + value: "{{ .Values.processesworker.dim.applicationName }}" + - name: "PROVISIONING__CLIENTID" + value: "{{ .Values.processesworker.provisioning.clientId }}" + - name: "PROVISIONING__CLIENTSECRET" valueFrom: secretKeyRef: name: "{{ template "dim.secretName" . }}" - key: "client-secret-cis-central" - - name: "DIM__AUTHURL" - value: "{{ .Values.processesworker.dim.authUrl }}" - - name: "SUBACCOUNT__BASEURL" - value: "{{ .Values.processesworker.subaccount.baseUrl }}" - - name: "ENTITLEMENT__BASEURL" - value: "{{ .Values.processesworker.entitlement.baseUrl }}" - - name: "CF__CLIENTID" - value: "{{ .Values.processesworker.cf.clientId }}" - - name: "CF__CLIENTSECRET" + key: "client-secret-provisioning" + - name: "PROVISIONING__TOKENADDRESS" + value: "{{ .Values.processesworker.provisioning.tokenAddress }}" + - name: "PROVISIONING__BASEURL" + value: "{{ .Values.processesworker.provisioning.baseUrl }}" + - name: "PROVISIONING__GRANTTYPE" + value: "{{ .Values.processesworker.provisioning.grantType }}" + - name: "PROVISIONING__ENCRYPTIONCONFIGINDEX" + value: "{{ .Values.processesworker.provisioning.encryptionConfigIndex }}" + - name: "PROVISIONING__ENCRYPTIONCONFIGS__0__INDEX" + value: "{{ .Values.processesworker.provisioning.encryptionConfigs.index0.index }}" + - name: "PROVISIONING__ENCRYPTIONCONFIGS__0__ENCRYPTIONKEY" valueFrom: secretKeyRef: name: "{{ template "dim.secretName" . }}" - key: "client-secret-cf" - - name: "CF__TOKENADDRESS" - value: "{{ .Values.processesworker.cf.tokenAddress }}" - - name: "CF__BASEURL" - value: "{{ .Values.processesworker.cf.baseUrl }}" - - name: "CF__GRANTTYPE" - value: "{{ .Values.processesworker.cf.grantType }}" + key: "provisioning-encryption-key0" + - name: "PROVISIONING__ENCRYPTIONCONFIGS__0__CIPHERMODE" + value: "{{ .Values.processesworker.provisioning.encryptionConfigs.index0.cipherMode }}" + - name: "PROVISIONING__ENCRYPTIONCONFIGS__0__PADDINGMODE" + value: "{{ .Values.processesworker.provisioning.encryptionConfigs.index0.paddingMode }}" - name: "CALLBACK__USERNAME" value: "empty" - name: "CALLBACK__PASSWORD" diff --git a/charts/dim/templates/deployment.yaml b/charts/dim/templates/deployment.yaml index de8d07e..cc54afe 100644 --- a/charts/dim/templates/deployment.yaml +++ b/charts/dim/templates/deployment.yaml @@ -80,10 +80,21 @@ spec: value: "{{ .Values.dim.healthChecks.liveness.path}}" - name: "SWAGGERENABLED" value: "{{ .Values.dim.swaggerEnabled }}" - - name: "DIM__ROOTDIRECTORYID" - value: "{{ .Values.dim.rootDirectoryId }}" - name: "DIM__OPERATORID" value: "{{ .Values.dim.operatorId }}" + - name: "DIM__ENCRYPTIONCONFIGINDEX" + value: "{{ .Values.processesworker.provisioning.encryptionConfigIndex }}" + - name: "DIM__ENCRYPTIONCONFIGS__0__INDEX" + value: "{{ .Values.processesworker.provisioning.encryptionConfigs.index0.index }}" + - name: "DIM__ENCRYPTIONCONFIGS__0__ENCRYPTIONKEY" + valueFrom: + secretKeyRef: + name: "{{ template "dim.secretName" . }}" + key: "provisioning-encryption-key0" + - name: "DIM__ENCRYPTIONCONFIGS__0__CIPHERMODE" + value: "{{ .Values.processesworker.provisioning.encryptionConfigs.index0.cipherMode }}" + - name: "DIM__ENCRYPTIONCONFIGS__0__PADDINGMODE" + value: "{{ .Values.processesworker.provisioning.encryptionConfigs.index0.paddingMode }}" - name: "JWTBEAREROPTIONS__METADATAADDRESS" value: "{{ .Values.idp.address }}{{ .Values.idp.jwtBearerOptions.metadataPath }}" - name: "JWTBEAREROPTIONS__REQUIREHTTPSMETADATA" @@ -94,19 +105,6 @@ spec: value: "{{ .Values.idp.address }}{{ .Values.idp.jwtBearerOptions.tokenValidationParameters.validIssuerPath }}" - name: "JWTBEAREROPTIONS__REFRESHINTERVAL" value: "{{ .Values.idp.jwtBearerOptions.refreshInterval }}" - - name: "CF__CLIENTID" - value: "{{ .Values.processesworker.cf.clientId }}" - - name: "CF__CLIENTSECRET" - valueFrom: - secretKeyRef: - name: "{{ template "dim.secretName" . }}" - key: "client-secret-cf" - - name: "CF__TOKENADDRESS" - value: "{{ .Values.processesworker.cf.tokenAddress }}" - - name: "CF__BASEURL" - value: "{{ .Values.processesworker.cf.baseUrl }}" - - name: "CF__GRANTTYPE" - value: "{{ .Values.processesworker.cf.grantType }}" ports: - name: http containerPort: {{ .Values.portContainer }} diff --git a/charts/dim/templates/secret.yaml b/charts/dim/templates/secret.yaml index 3fdece7..472091e 100644 --- a/charts/dim/templates/secret.yaml +++ b/charts/dim/templates/secret.yaml @@ -36,15 +36,17 @@ data: # use data map instead of stringData to prevent base64 encoding of already base64-encoded existing value from secret # use index function for secret keys with hyphen otherwise '$secret.data.secretKey' works too client-secret-cis-central: {{ coalesce ( .Values.processesworker.dim.clientSecretCisCentral | b64enc ) ( index $secret.data "client-secret-cis-central" ) | default ( randAlphaNum 32 ) | quote }} - client-secret-cf: {{ coalesce ( .Values.processesworker.cf.clientSecret | b64enc ) ( index $secret.data "client-secret-cf" ) | default ( randAlphaNum 32 ) | quote }} + client-secret-provisioning: {{ coalesce ( .Values.processesworker.provisioning.clientSecret | b64enc ) ( index $secret.data "client-secret-provisioning" ) | default ( randAlphaNum 32 ) | quote }} client-secret-callback: {{ coalesce ( .Values.processesworker.callback.clientSecret | b64enc ) ( index $secret.data "client-secret-callback" ) | default ( randAlphaNum 32 ) | quote }} + provisioning-encryption-key0: {{ coalesce ( .Values.processesworker.provisioning.encryptionConfigs.index0.encryptionKey | b64enc ) ( index $secret.data "provisioning-encryption-key0" ) | default ( randAlphaNum 32 ) | quote }} technicalusercreation-encryption-key0: {{ coalesce ( .Values.processesworker.technicalUserCreation.encryptionConfigs.index0.encryptionKey | b64enc ) ( index $secret.data "technicalusercreation-encryption-key0" ) | default ( randAlphaNum 32 ) | quote }} {{ else -}} stringData: # if secret doesn't exist, use provided value from values file or generate a random one client-secret-cis-central: {{ .Values.processesworker.dim.clientSecretCisCentral | default ( randAlphaNum 32 ) | quote }} - client-secret-cf: {{ .Values.processesworker.cf.clientSecret | default ( randAlphaNum 32 ) | quote }} + client-secret-provisioning: {{ .Values.processesworker.provisioning.clientSecret | default ( randAlphaNum 32 ) | quote }} client-secret-callback: {{ .Values.processesworker.callback.clientSecret | default ( randAlphaNum 32 ) | quote }} + provisioning-encryption-key0: {{ .Values.processesworker.provisioning.encryptionConfigs.index0.encryptionKey | default ( randAlphaNum 32 ) | quote }} technicalusercreation-encryption-key0: {{ .Values.processesworker.technicalUserCreation.encryptionConfigs.index0.encryptionKey | default ( randAlphaNum 32 ) | quote }} {{ end }} {{- end -}} diff --git a/charts/dim/values.yaml b/charts/dim/values.yaml index 60a68c1..195089f 100644 --- a/charts/dim/values.yaml +++ b/charts/dim/values.yaml @@ -42,7 +42,6 @@ dim: readyness: path: "/ready" swaggerEnabled: false - rootDirectoryId: "00000000-0000-0000-0000-000000000000" operatorId: "00000000-0000-0000-0000-000000000000" migrations: @@ -80,23 +79,23 @@ processesworker: cpu: 45m memory: 300M dim: - adminMail: "mail@example.org" - clientIdCisCentral: "" - clientSecretCisCentral: "" - authUrl: "" - subaccount: - # -- Url to the subaccount service api - baseUrl: "" - entitlement: - # -- Url to the entitlement service api - baseUrl: "" - cf: + applicationName: "" + provisioning: clientId: "" clientSecret: "" tokenAddress: "" # -- Url to the cf service api baseUrl: "" grantType: "client_credentials" + encryptionConfigIndex: 0 + encryptionConfigs: + index0: + index: 0 + cipherMode: "CBC" + paddingMode: "PKCS7" + # -- EncryptionKey to encrypt the provisioning client-secret. Secret-key 'provisioning-encryption-key0'. + # Expected format is 256 bit (64 digits) hex. + encryptionKey: "" callback: scope: "openid" grantType: "client_credentials" diff --git a/environments/helm-values/values-int.yaml b/environments/helm-values/values-int.yaml index 1bf1a6b..9d9846a 100644 --- a/environments/helm-values/values-int.yaml +++ b/environments/helm-values/values-int.yaml @@ -41,7 +41,6 @@ ingress: dim: swaggerEnabled: true - rootDirectoryId: "d6cd4e2e-1053-4ba6-888e-1cd56509958a" operatorId: "d6cd4e2e-1053-4ba6-888e-1cd56509958a" migrations: @@ -52,23 +51,17 @@ processesworker: logging: default: "Debug" dim: - adminMail: "" - clientIdCisCentral: "" - clientSecretCisCentral: "" - authUrl: "https://catena-x-int-div.authentication.eu10.hana.ondemand.com" - subaccount: - # -- Url to the subaccount service api - baseUrl: "https://accounts-service.cfapps.eu10.hana.ondemand.com" - entitlement: - # -- Url to the entitlement service api - baseUrl: "https://entitlements-service.cfapps.eu10.hana.ondemand.com" - cf: + applicationName: "catena-x-portal" + provisioning: clientId: "" clientSecret: "" tokenAddress: "https://login.cf.eu10.hana.ondemand.com/oauth/token" # -- Url to the cf service api baseUrl: "https://api.cf.eu10.hana.ondemand.com" grantType: "client_credentials" + encryptionConfigs: + index0: + encryptionKey: "" callback: scope: "openid" grantType: "client_credentials" diff --git a/src/Dim.sln b/src/Dim.sln index ee2abba..f52c3a4 100644 --- a/src/Dim.sln +++ b/src/Dim.sln @@ -42,6 +42,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DimProcess.Library.Tests", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dim.Web.Tests", "..\tests\web\Dim.Web.Tests\Dim.Web.Tests.csproj", "{CE87E424-36CF-4597-9E08-2D687E67F259}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dim.DbAccess.Tests", "..\tests\database\Dim.DbAccess.Tests\Dim.DbAccess.Tests.csproj", "{5A8971D7-D0FB-4886-8982-48DC312F6262}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dim.Clients.Tests", "..\tests\clients\Dim.Clients.Tests\Dim.Clients.Tests.csproj", "{256D56CC-5A19-45D5-8E98-06419ADBC275}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -112,6 +116,14 @@ Global {CE87E424-36CF-4597-9E08-2D687E67F259}.Debug|Any CPU.Build.0 = Debug|Any CPU {CE87E424-36CF-4597-9E08-2D687E67F259}.Release|Any CPU.ActiveCfg = Release|Any CPU {CE87E424-36CF-4597-9E08-2D687E67F259}.Release|Any CPU.Build.0 = Release|Any CPU + {5A8971D7-D0FB-4886-8982-48DC312F6262}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5A8971D7-D0FB-4886-8982-48DC312F6262}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5A8971D7-D0FB-4886-8982-48DC312F6262}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5A8971D7-D0FB-4886-8982-48DC312F6262}.Release|Any CPU.Build.0 = Release|Any CPU + {256D56CC-5A19-45D5-8E98-06419ADBC275}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {256D56CC-5A19-45D5-8E98-06419ADBC275}.Debug|Any CPU.Build.0 = Debug|Any CPU + {256D56CC-5A19-45D5-8E98-06419ADBC275}.Release|Any CPU.ActiveCfg = Release|Any CPU + {256D56CC-5A19-45D5-8E98-06419ADBC275}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {8356C7AF-6F88-4A62-B3E9-5656634A6FEA} = {B84A3CAB-AC86-4B2D-A490-79E1002350FF} @@ -130,5 +142,7 @@ Global {A44447B0-794D-451A-A571-E3B761174B48} = {CB1B7D43-9AFC-47EF-8915-A547F7F553AB} {85D316A0-17BE-4983-AB06-5C72365ABD9B} = {CB1B7D43-9AFC-47EF-8915-A547F7F553AB} {CE87E424-36CF-4597-9E08-2D687E67F259} = {CB1B7D43-9AFC-47EF-8915-A547F7F553AB} + {5A8971D7-D0FB-4886-8982-48DC312F6262} = {CB1B7D43-9AFC-47EF-8915-A547F7F553AB} + {256D56CC-5A19-45D5-8E98-06419ADBC275} = {CB1B7D43-9AFC-47EF-8915-A547F7F553AB} EndGlobalSection EndGlobal diff --git a/src/clients/Dim.Clients/Api/Cf/AddSpaceRoleToUserRequest.cs b/src/clients/Dim.Clients/Api/Cf/AddSpaceRoleToUserRequest.cs deleted file mode 100644 index b165b63..0000000 --- a/src/clients/Dim.Clients/Api/Cf/AddSpaceRoleToUserRequest.cs +++ /dev/null @@ -1,50 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using System.Text.Json.Serialization; - -namespace Dim.Clients.Api.Cf; - -public record AddSpaceRoleToUserRequest( - [property: JsonPropertyName("type")] string Type, - [property: JsonPropertyName("relationships")] SpaceRoleRelationship Relationship -); - -public record SpaceRoleRelationship( - [property: JsonPropertyName("user")] RelationshipUser User, - [property: JsonPropertyName("space")] SpaceRoleSpace Space -); - -public record RelationshipUser( - [property: JsonPropertyName("data")] UserData Data -); - -public record UserData( - [property: JsonPropertyName("username")] string Username, - [property: JsonPropertyName("origin")] string Origin -); - -public record SpaceRoleSpace( - [property: JsonPropertyName("data")] SpaceRoleData Data -); - -public record SpaceRoleData( - [property: JsonPropertyName("guid")] Guid Id -); diff --git a/src/clients/Dim.Clients/Api/Cf/CfClient.cs b/src/clients/Dim.Clients/Api/Cf/CfClient.cs deleted file mode 100644 index 956bf51..0000000 --- a/src/clients/Dim.Clients/Api/Cf/CfClient.cs +++ /dev/null @@ -1,287 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using Dim.Clients.Api.Cf.DependencyInjection; -using Dim.Clients.Extensions; -using Dim.Clients.Token; -using Microsoft.Extensions.Options; -using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; -using Org.Eclipse.TractusX.Portal.Backend.Framework.HttpClientExtensions; -using System.Net.Http.Json; -using System.Text.Json; -using System.Web; - -namespace Dim.Clients.Api.Cf; - -public class CfClient : ICfClient -{ - private readonly CfSettings _settings; - private readonly IBasicAuthTokenService _basicAuthTokenService; - - public CfClient(IBasicAuthTokenService basicAuthTokenService, IOptions settings) - { - _basicAuthTokenService = basicAuthTokenService; - _settings = settings.Value; - } - - public async Task CreateCloudFoundrySpace(string tenantName, CancellationToken cancellationToken) - { - var client = await _basicAuthTokenService.GetBasicAuthorizedLegacyClient(_settings, cancellationToken).ConfigureAwait(false); - var cfEnvironmentId = await GetEnvironmentId(tenantName, cancellationToken, client).ConfigureAwait(false); - var data = new CreateSpaceRequest( - $"{tenantName}-space", - new SpaceRelationship(new SpaceOrganization(new SpaceRelationshipData(cfEnvironmentId))) - ); - - var result = await client.PostAsJsonAsync("/v3/spaces", data, JsonSerializerExtensions.Options, cancellationToken) - .CatchingIntoServiceExceptionFor("create-cfe", HttpAsyncResponseMessageExtension.RecoverOptions.ALLWAYS).ConfigureAwait(false); - try - { - var response = await result.Content - .ReadFromJsonAsync(JsonSerializerExtensions.Options, cancellationToken) - .ConfigureAwait(false); - - if (response == null) - { - throw new ServiceException("Response must not be null"); - } - - return response.Id; - } - catch (JsonException je) - { - throw new ServiceException(je.Message); - } - } - - private static async Task GetEnvironmentId(string tenantName, CancellationToken cancellationToken, HttpClient client) - { - var environmentsResponse = await client.GetAsync($"/v3/organizations?names={HttpUtility.UrlEncode(tenantName)}", cancellationToken) - .CatchingIntoServiceExceptionFor("get-organizations", HttpAsyncResponseMessageExtension.RecoverOptions.INFRASTRUCTURE); - var environments = await environmentsResponse.Content - .ReadFromJsonAsync(JsonSerializerExtensions.Options, cancellationToken) - .ConfigureAwait(false); - - var tenantEnvironment = environments?.Resources.Where(x => x.Name == tenantName); - if (tenantEnvironment == null || tenantEnvironment.Count() != 1) - { - throw new ServiceException($"There should only be one cf environment for tenant {tenantName}", true); - } - - return tenantEnvironment.Single().EnvironmentId; - } - - public async Task AddSpaceRoleToUser(string type, string user, Guid spaceId, CancellationToken cancellationToken) - { - var client = await _basicAuthTokenService.GetBasicAuthorizedLegacyClient(_settings, cancellationToken).ConfigureAwait(false); - var data = new AddSpaceRoleToUserRequest( - type, - new SpaceRoleRelationship( - new RelationshipUser(new UserData(user, "sap.ids")), - new SpaceRoleSpace(new SpaceRoleData(spaceId)) - ) - ); - - await client.PostAsJsonAsync("/v3/roles", data, JsonSerializerExtensions.Options, cancellationToken) - .CatchingIntoServiceExceptionFor("add-space-roles", HttpAsyncResponseMessageExtension.RecoverOptions.INFRASTRUCTURE).ConfigureAwait(false); - } - - public async Task GetServicePlan(string servicePlanName, string servicePlanType, CancellationToken cancellationToken) - { - var client = await _basicAuthTokenService.GetBasicAuthorizedLegacyClient(_settings, cancellationToken).ConfigureAwait(false); - var result = await client.GetAsync("/v3/service_plans", cancellationToken) - .CatchingIntoServiceExceptionFor("get-service-plan", HttpAsyncResponseMessageExtension.RecoverOptions.ALLWAYS).ConfigureAwait(false); - try - { - var response = await result.Content - .ReadFromJsonAsync(JsonSerializerExtensions.Options, cancellationToken) - .ConfigureAwait(false); - - if (response == null) - { - throw new ServiceException("response should never be null here"); - } - - var servicePlans = response.Resources.Where(x => x.Name == servicePlanType && - x.BrokerCatalog?.BrokerCatalogMetadata?.AutoSubscription?.AppName == servicePlanName); - if (servicePlans.Count() != 1) - { - throw new ServiceException($"There must be exactly one service plan with name {servicePlanName} and type {servicePlanType}", isRecoverable: !servicePlans.Any()); - } - - return servicePlans.Single().Id; - } - catch (JsonException je) - { - throw new ServiceException(je.Message); - } - } - - public async Task GetSpace(string tenantName, CancellationToken cancellationToken) - { - var spaceName = $"{tenantName}-space"; - var client = await _basicAuthTokenService.GetBasicAuthorizedLegacyClient(_settings, cancellationToken).ConfigureAwait(false); - var result = await client.GetAsync($"/v3/spaces?names={HttpUtility.UrlEncode(spaceName)}", cancellationToken) - .CatchingIntoServiceExceptionFor("get-space", HttpAsyncResponseMessageExtension.RecoverOptions.ALLWAYS).ConfigureAwait(false); - try - { - var response = await result.Content - .ReadFromJsonAsync(JsonSerializerExtensions.Options, cancellationToken) - .ConfigureAwait(false); - - if (response == null) - { - throw new ServiceException("response should never be null here"); - } - - var spaces = response.Resources.Where(x => x.Name == spaceName); - if (spaces.Count() != 1) - { - throw new ServiceException($"There must be exactly one space with name {spaceName}", isRecoverable: !spaces.Any()); - } - - return spaces.Single().Id; - } - catch (JsonException je) - { - throw new ServiceException(je.Message); - } - } - - public async Task CreateDimServiceInstance(string tenantName, Guid spaceId, Guid servicePlanId, CancellationToken cancellationToken) - { - var client = await _basicAuthTokenService.GetBasicAuthorizedLegacyClient(_settings, cancellationToken).ConfigureAwait(false); - var data = new CreateDimServiceInstance( - "managed", - $"{tenantName}-dim-instance", - new DimRelationships( - new DimSpace(new DimData(spaceId)), - new DimServicePlan(new DimData(servicePlanId))) - ); - - await client.PostAsJsonAsync("/v3/service_instances", data, JsonSerializerExtensions.Options, cancellationToken) - .CatchingIntoServiceExceptionFor("create-dim-si", HttpAsyncResponseMessageExtension.RecoverOptions.ALLWAYS).ConfigureAwait(false); - } - - private async Task GetServiceInstances(string tenantName, Guid? spaceId, CancellationToken cancellationToken) - { - var name = $"{tenantName}-dim-instance"; - var client = await _basicAuthTokenService.GetBasicAuthorizedLegacyClient(_settings, cancellationToken).ConfigureAwait(false); - var result = await client.GetAsync($"/v3/service_instances?names={HttpUtility.UrlEncode(name)}", cancellationToken) - .CatchingIntoServiceExceptionFor("get-si", HttpAsyncResponseMessageExtension.RecoverOptions.INFRASTRUCTURE).ConfigureAwait(false); - try - { - var response = await result.Content - .ReadFromJsonAsync(JsonSerializerExtensions.Options, cancellationToken) - .ConfigureAwait(false); - if (response == null) - { - throw new ServiceException("Response must not be null"); - } - - var resources = response.Resources.Where(x => x.Name == name && x.Type == "managed" && (spaceId == null || x.Relationships.Space.Data.Id == spaceId.Value) && x.LastOperation.State == "succeeded"); - if (resources.Count() != 1) - { - throw new ServiceException("There must be exactly one service instance", isRecoverable: !resources.Any()); - } - - return resources.Single().Id; - } - catch (JsonException je) - { - throw new ServiceException(je.Message); - } - } - - public async Task CreateServiceInstanceBindings(string tenantName, string? keyName, Guid spaceId, CancellationToken cancellationToken) - { - var serviceInstanceId = await GetServiceInstances(tenantName, spaceId, cancellationToken).ConfigureAwait(false); - var client = await _basicAuthTokenService.GetBasicAuthorizedLegacyClient(_settings, cancellationToken).ConfigureAwait(false); - var data = new CreateServiceCredentialBindingRequest( - "key", - $"{keyName ?? tenantName}-dim-key01", - new ServiceCredentialRelationships( - new DimServiceInstance(new DimData(serviceInstanceId))) - ); - await client.PostAsJsonAsync("/v3/service_credential_bindings", data, JsonSerializerOptions.Default, cancellationToken) - .CatchingIntoServiceExceptionFor("create-si-bindings", HttpAsyncResponseMessageExtension.RecoverOptions.ALLWAYS); - } - - public async Task GetServiceBinding(string tenantName, Guid spaceId, string bindingName, CancellationToken cancellationToken) - { - var client = await _basicAuthTokenService.GetBasicAuthorizedLegacyClient(_settings, cancellationToken).ConfigureAwait(false); - var serviceInstanceId = await GetServiceInstances(tenantName, spaceId, cancellationToken).ConfigureAwait(false); - var result = await client.GetAsync($"/v3/service_credential_bindings?names={HttpUtility.UrlEncode(bindingName)}", cancellationToken) - .CatchingIntoServiceExceptionFor("get-credential-binding", HttpAsyncResponseMessageExtension.RecoverOptions.INFRASTRUCTURE).ConfigureAwait(false); - try - { - var response = await result.Content - .ReadFromJsonAsync(JsonSerializerExtensions.Options, cancellationToken) - .ConfigureAwait(false); - if (response == null) - { - throw new ServiceException("Response must not be null"); - } - - var resources = response.Resources.Where(x => x.Relationships.ServiceInstance.Data.Id == serviceInstanceId); - if (resources.Count() != 1) - { - throw new ServiceException("There must be exactly one service credential binding", isRecoverable: !resources.Any()); - } - - return resources.Single().Id; - } - catch (JsonException je) - { - throw new ServiceException(je.Message); - } - } - - public async Task GetServiceBindingDetails(Guid id, CancellationToken cancellationToken) - { - var client = await _basicAuthTokenService.GetBasicAuthorizedLegacyClient(_settings, cancellationToken).ConfigureAwait(false); - var result = await client.GetAsync($"/v3/service_credential_bindings/{id}/details", cancellationToken) - .CatchingIntoServiceExceptionFor("get-credential-binding-name", HttpAsyncResponseMessageExtension.RecoverOptions.INFRASTRUCTURE).ConfigureAwait(false); - try - { - var response = await result.Content - .ReadFromJsonAsync(JsonSerializerExtensions.Options, cancellationToken) - .ConfigureAwait(false); - - if (response == null) - { - throw new ServiceException("There must be exactly one service instance", isRecoverable: true); - } - - return response; - } - catch (JsonException je) - { - throw new ServiceException(je.Message); - } - } - - public async Task DeleteServiceInstanceBindings(Guid serviceBindingId, CancellationToken cancellationToken) - { - var client = await _basicAuthTokenService.GetBasicAuthorizedLegacyClient(_settings, cancellationToken).ConfigureAwait(false); - await client.DeleteAsync($"/v3/service_credential_bindings/{serviceBindingId}", cancellationToken) - .CatchingIntoServiceExceptionFor("delete-si-bindings", HttpAsyncResponseMessageExtension.RecoverOptions.ALLWAYS); - } -} diff --git a/src/clients/Dim.Clients/Api/Cf/CreateCfeRequest.cs b/src/clients/Dim.Clients/Api/Cf/CreateCfeRequest.cs deleted file mode 100644 index d832e4e..0000000 --- a/src/clients/Dim.Clients/Api/Cf/CreateCfeRequest.cs +++ /dev/null @@ -1,53 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using System.Text.Json.Serialization; - -namespace Dim.Clients.Api.Cf; - -public record CreateSpaceRequest( - [property: JsonPropertyName("name")] string Name, - [property: JsonPropertyName("relationships")] SpaceRelationship Relationship -); - -public record SpaceRelationship( - [property: JsonPropertyName("organization")] SpaceOrganization Organization -); - -public record SpaceOrganization( - [property: JsonPropertyName("data")] SpaceRelationshipData Data -); - -public record SpaceRelationshipData( - [property: JsonPropertyName("guid")] Guid Id -); - -public record CreateSpaceResponse( - [property: JsonPropertyName("guid")] Guid Id -); - -public record GetEnvironmentsResponse( - [property: JsonPropertyName("resources")] IEnumerable Resources -); - -public record EnvironmentResource( - [property: JsonPropertyName("guid")] Guid EnvironmentId, - [property: JsonPropertyName("name")] string Name -); diff --git a/src/clients/Dim.Clients/Api/Cf/DependencyInjection/CfClientServiceExtensions.cs b/src/clients/Dim.Clients/Api/Cf/DependencyInjection/CfClientServiceExtensions.cs deleted file mode 100644 index dba6419..0000000 --- a/src/clients/Dim.Clients/Api/Cf/DependencyInjection/CfClientServiceExtensions.cs +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using Dim.Clients.Extensions; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; - -namespace Dim.Clients.Api.Cf.DependencyInjection; - -public static class CfClientServiceExtensions -{ - public static IServiceCollection AddCfClient(this IServiceCollection services, IConfigurationSection section) - { - services.AddOptions() - .Bind(section) - .ValidateOnStart(); - - var sp = services.BuildServiceProvider(); - var settings = sp.GetRequiredService>(); - services - .AddCustomHttpClientWithAuthentication(settings.Value.BaseUrl, settings.Value.TokenAddress) - .AddTransient(); - - return services; - } -} diff --git a/src/clients/Dim.Clients/Api/Cf/DependencyInjection/CfSettings.cs b/src/clients/Dim.Clients/Api/Cf/DependencyInjection/CfSettings.cs deleted file mode 100644 index ebe728d..0000000 --- a/src/clients/Dim.Clients/Api/Cf/DependencyInjection/CfSettings.cs +++ /dev/null @@ -1,30 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using Dim.Clients.Token; -using System.ComponentModel.DataAnnotations; - -namespace Dim.Clients.Api.Cf.DependencyInjection; - -public class CfSettings : BasicAuthSettings -{ - [Required] - public string BaseUrl { get; set; } = null!; -} diff --git a/src/clients/Dim.Clients/Api/Cf/ICfClient.cs b/src/clients/Dim.Clients/Api/Cf/ICfClient.cs deleted file mode 100644 index 1e34fc6..0000000 --- a/src/clients/Dim.Clients/Api/Cf/ICfClient.cs +++ /dev/null @@ -1,34 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -namespace Dim.Clients.Api.Cf; - -public interface ICfClient -{ - Task CreateCloudFoundrySpace(string tenantName, CancellationToken cancellationToken); - Task AddSpaceRoleToUser(string type, string user, Guid spaceId, CancellationToken cancellationToken); - Task GetServicePlan(string servicePlanName, string servicePlanType, CancellationToken cancellationToken); - Task GetSpace(string tenantName, CancellationToken cancellationToken); - Task CreateDimServiceInstance(string tenantName, Guid spaceId, Guid servicePlanId, CancellationToken cancellationToken); - Task CreateServiceInstanceBindings(string tenantName, string? keyName, Guid spaceId, CancellationToken cancellationToken); - Task GetServiceBinding(string tenantName, Guid spaceId, string bindingName, CancellationToken cancellationToken); - Task GetServiceBindingDetails(Guid id, CancellationToken cancellationToken); - Task DeleteServiceInstanceBindings(Guid serviceBindingId, CancellationToken cancellationToken); -} diff --git a/src/clients/Dim.Clients/Api/Cf/ServicePlanResponse.cs b/src/clients/Dim.Clients/Api/Cf/ServicePlanResponse.cs deleted file mode 100644 index 956834b..0000000 --- a/src/clients/Dim.Clients/Api/Cf/ServicePlanResponse.cs +++ /dev/null @@ -1,140 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using System.Text.Json.Serialization; - -namespace Dim.Clients.Api.Cf; - -public record ServicePlanResponse( - [property: JsonPropertyName("resources")] IEnumerable Resources -); - -public record ServicePlanResources( - [property: JsonPropertyName("guid")] Guid Id, - [property: JsonPropertyName("name")] string Name, - [property: JsonPropertyName("broker_catalog")] BrokerCatalog? BrokerCatalog -); - -public record BrokerCatalog( - [property: JsonPropertyName("id")] string Id, - [property: JsonPropertyName("metadata")] BrokerCatalogMetadata? BrokerCatalogMetadata -); - -public record BrokerCatalogMetadata( - [property: JsonPropertyName("auto_subscription")] AutoSupscription? AutoSubscription -); - -public record AutoSupscription( - [property: JsonPropertyName("app_name")] string? AppName -); - -public record ServiceInstanceResponse( - [property: JsonPropertyName("resources")] IEnumerable Resources -); - -public record ServiceInstanceResource( - [property: JsonPropertyName("guid")] Guid Id, - [property: JsonPropertyName("name")] string Name, - [property: JsonPropertyName("type")] string Type, - [property: JsonPropertyName("relationships")] ServiceInstanceRelationship Relationships, - [property: JsonPropertyName("last_operation")] LastOperation LastOperation -); - -public record ServiceInstanceRelationship( - [property: JsonPropertyName("space")] ServiceInstanceRelationshipSpace Space -); - -public record LastOperation( - [property: JsonPropertyName("state")] string State -); - -public record ServiceInstanceRelationshipSpace( - [property: JsonPropertyName("data")] DimData Data -); - -public record CreateServiceCredentialBindingRequest( - [property: JsonPropertyName("type")] string Type, - [property: JsonPropertyName("name")] string Name, - [property: JsonPropertyName("relationships")] ServiceCredentialRelationships Relationships -); - -public record ServiceCredentialRelationships( - [property: JsonPropertyName("service_instance")] DimServiceInstance ServiceInstance -); - -public record DimServiceInstance( - [property: JsonPropertyName("data")] DimData Data -); - -public record CreateDimServiceInstance( - [property: JsonPropertyName("type")] string Type, - [property: JsonPropertyName("name")] string Name, - [property: JsonPropertyName("relationships")] DimRelationships Relationships -); - -public record DimRelationships( - [property: JsonPropertyName("space")] DimSpace Space, - [property: JsonPropertyName("service_plan")] DimServicePlan ServicePlan -); - -public record DimServicePlan( - [property: JsonPropertyName("data")] DimData Data -); - -public record DimSpace( - [property: JsonPropertyName("data")] DimData Data -); - -public record DimData( - [property: JsonPropertyName("guid")] Guid Id -); - -public record ServiceCredentialBindingResponse( - [property: JsonPropertyName("resources")] IEnumerable Resources -); - -public record ServiceCredentialBindingRelationships( - [property: JsonPropertyName("service_instance")] ScbServiceInstnace ServiceInstance -); - -public record ScbServiceInstnace( - [property: JsonPropertyName("data")] DimData Data -); - -public record ServiceCredentialBindingResource( - [property: JsonPropertyName("guid")] Guid Id, - [property: JsonPropertyName("relationships")] ServiceCredentialBindingRelationships Relationships -); - -public record ServiceCredentialBindingDetailResponse( - [property: JsonPropertyName("credentials")] Credentials Credentials -); - -public record Credentials( - [property: JsonPropertyName("url")] string Url, - [property: JsonPropertyName("uaa")] Uaa Uaa -); - -public record Uaa( - [property: JsonPropertyName("clientid")] string ClientId, - [property: JsonPropertyName("clientsecret")] string ClientSecret, - [property: JsonPropertyName("url")] string Url, - [property: JsonPropertyName("apiurl")] string ApiUrl -); diff --git a/src/clients/Dim.Clients/Api/Cf/SpaceResponse.cs b/src/clients/Dim.Clients/Api/Cf/SpaceResponse.cs deleted file mode 100644 index 21f9234..0000000 --- a/src/clients/Dim.Clients/Api/Cf/SpaceResponse.cs +++ /dev/null @@ -1,31 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using System.Text.Json.Serialization; - -namespace Dim.Clients.Api.Cf; - -public record SpaceResponse( - [property: JsonPropertyName("resources")] IEnumerable Resources -); - -public record Space( - [property: JsonPropertyName("guid")] Guid Id, - [property: JsonPropertyName("name")] string Name -); diff --git a/src/clients/Dim.Clients/Api/Dim/ApplicationResponse.cs b/src/clients/Dim.Clients/Api/Dim/ApplicationResponse.cs deleted file mode 100644 index 9db87fc..0000000 --- a/src/clients/Dim.Clients/Api/Dim/ApplicationResponse.cs +++ /dev/null @@ -1,31 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using System.Text.Json.Serialization; - -namespace Dim.Clients.Api.Dim; - -public record ApplicationResponse( - [property: JsonPropertyName("id")] string Id, - [property: JsonPropertyName("application")] string Application, - [property: JsonPropertyName("applicationKey")] string ApplicaitonKey, - [property: JsonPropertyName("description")] string Description, - [property: JsonPropertyName("colorAccent")] int ColorAccent -); diff --git a/src/clients/Dim.Clients/Api/Dim/CreateApplicationRequest.cs b/src/clients/Dim.Clients/Api/Dim/CreateApplicationRequest.cs deleted file mode 100644 index 5ffbecc..0000000 --- a/src/clients/Dim.Clients/Api/Dim/CreateApplicationRequest.cs +++ /dev/null @@ -1,37 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using System.Text.Json.Serialization; - -namespace Dim.Clients.Api.Dim; - -public record CreateApplicationRequest( - [property: JsonPropertyName("payload")] ApplicationPayload Payload -); - -public record ApplicationPayload( - [property: JsonPropertyName("application")] string Application, - [property: JsonPropertyName("description")] string Description, - [property: JsonPropertyName("colorAccent")] int ColorAccent -); - -public record CreateApplicationResponse( - [property: JsonPropertyName("id")] string Id -); diff --git a/src/clients/Dim.Clients/Api/Dim/CreateCompanyIdentityRequest.cs b/src/clients/Dim.Clients/Api/Dim/CreateCompanyIdentityRequest.cs deleted file mode 100644 index e135e0d..0000000 --- a/src/clients/Dim.Clients/Api/Dim/CreateCompanyIdentityRequest.cs +++ /dev/null @@ -1,65 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using System.Text.Json.Serialization; - -namespace Dim.Clients.Api.Dim; - -public record CreateCompanyIdentityRequest( - [property: JsonPropertyName("payload")] Payload Payload -); - -public record Payload( - [property: JsonPropertyName("hostingURL")] string HostingUrl, - [property: JsonPropertyName("network")] Network Network, - [property: JsonPropertyName("services")] IEnumerable Services, - [property: JsonPropertyName("keys")] IEnumerable Keys, - [property: JsonPropertyName("name")] string Name -); - -public record Service( - [property: JsonPropertyName("id")] string Id, - [property: JsonPropertyName("type")] string Type, - [property: JsonPropertyName("serviceEndpoint")] string ServiceEndpoint -); - -public record Network( - [property: JsonPropertyName("didMethod")] string DidMethod, - [property: JsonPropertyName("type")] string Type -); - -public record Key( - [property: JsonPropertyName("type")] string Type -); - -public record CreateCompanyIdentityResponse( - [property: JsonPropertyName("did")] string Did, - [property: JsonPropertyName("companyId")] Guid CompanyId, - [property: JsonPropertyName("downloadURL")] string DownloadUrl -); -// -// public record UpdateCompanyIdentityRequest( -// [property: JsonPropertyName("didDocUpdates")] DidDocUpdates DidDocUpdates -// ); -// -// public record DidDocUpdates( -// [property: JsonPropertyName("removeServices")] IEnumerable RemoveServices, -// [property: JsonPropertyName("addServices")] IEnumerable AddServices -// ); diff --git a/src/clients/Dim.Clients/Api/Dim/DimClient.cs b/src/clients/Dim.Clients/Api/Dim/DimClient.cs index 5ec830e..5ba1249 100644 --- a/src/clients/Dim.Clients/Api/Dim/DimClient.cs +++ b/src/clients/Dim.Clients/Api/Dim/DimClient.cs @@ -18,53 +18,36 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ +using Dim.Clients.Api.Dim.Models; using Dim.Clients.Extensions; using Dim.Clients.Token; using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; using Org.Eclipse.TractusX.Portal.Backend.Framework.HttpClientExtensions; using System.Net.Http.Json; using System.Text.Json; +using System.Web; namespace Dim.Clients.Api.Dim; -public class DimClient(IBasicAuthTokenService basicAuthTokenService, IHttpClientFactory clientFactory) +public class DimClient(IBasicAuthTokenService basicAuthTokenService) : IDimClient { - public async Task CreateCompanyIdentity(BasicAuthSettings dimBasicAuth, Guid tenantId, string hostingUrl, string baseUrl, bool isIssuer, CancellationToken cancellationToken) + public async Task GetCompanyData(BasicAuthSettings dimAuth, string dimBaseUrl, string tenantName, string application, CancellationToken cancellationToken) { - var client = await basicAuthTokenService.GetBasicAuthorizedClient(dimBasicAuth, cancellationToken).ConfigureAwait(false); - var data = new CreateCompanyIdentityRequest(new Payload( - hostingUrl, - new Network("web", "production"), - [new Service($"dim:web:{tenantId}", "CredentialService", "https://dis-agent-prod.eu10.dim.cloud.sap/api/v1.0.0/iatp")], - isIssuer ? - [ - new("SIGNING"), - new("SIGNING_VC") - ] : - new Key[] - { - new("SIGNING") - }, - "holder iatp")); - var result = await client.PostAsJsonAsync($"{baseUrl}/api/v2.0.0/companyIdentities", data, JsonSerializerExtensions.Options, cancellationToken) - .CatchingIntoServiceExceptionFor("create-company-identity", HttpAsyncResponseMessageExtension.RecoverOptions.INFRASTRUCTURE, - async m => - { - var message = await m.Content.ReadAsStringAsync().ConfigureAwait(false); - return (false, message); - }).ConfigureAwait(false); + var client = await basicAuthTokenService.GetBasicAuthorizedClient(dimAuth, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); + var filterString = $"name eq {tenantName} and application eq {application}"; + var result = await client.GetAsync($"{dimBaseUrl}/api/v2.0.0/companyIdentities?$filter={HttpUtility.UrlEncode(filterString)}", cancellationToken); try { var response = await result.Content - .ReadFromJsonAsync(JsonSerializerExtensions.Options, cancellationToken) + .ReadFromJsonAsync(JsonSerializerExtensions.Options, cancellationToken) .ConfigureAwait(false); - if (response == null) + if (response?.Data == null || response.Data.Count() != 1) { - throw new ServiceException("Response was empty", true); + throw new ConflictException("There is no matching company"); } - return response; + return response.Data.Single(); } catch (JsonException je) { @@ -72,102 +55,16 @@ [new Service($"dim:web:{tenantId}", "CredentialService", "https://dis-agent-prod } } - public async Task GetDidDocument(string url, CancellationToken cancellationToken) - { - var client = clientFactory.CreateClient("didDocumentDownload"); - using var result = await client.GetStreamAsync(url, cancellationToken).ConfigureAwait(false); - var document = await JsonDocument.ParseAsync(result, cancellationToken: cancellationToken).ConfigureAwait(false); - return document; - } - - public async Task CreateApplication(BasicAuthSettings dimAuth, string dimBaseUrl, string tenantName, CancellationToken cancellationToken) - { - var client = await basicAuthTokenService.GetBasicAuthorizedClient(dimAuth, cancellationToken).ConfigureAwait(false); - var data = new CreateApplicationRequest(new ApplicationPayload( - "catena-x-portal", - $"Catena-X Portal MIW for {tenantName}", - 6)); - var result = await client.PostAsJsonAsync($"{dimBaseUrl}/api/v2.0.0/applications", data, JsonSerializerExtensions.Options, cancellationToken) - .CatchingIntoServiceExceptionFor("create-application", HttpAsyncResponseMessageExtension.RecoverOptions.INFRASTRUCTURE, - async m => - { - var message = await m.Content.ReadAsStringAsync().ConfigureAwait(false); - return (false, message); - }).ConfigureAwait(false); - try - { - var response = await result.Content - .ReadFromJsonAsync(JsonSerializerExtensions.Options, cancellationToken) - .ConfigureAwait(false); - if (response == null) - { - throw new ServiceException("Response was empty", true); - } - - return response.Id; - } - catch (JsonException je) - { - throw new ServiceException(je.Message); - } - } - - public async Task GetApplication(BasicAuthSettings dimAuth, string dimBaseUrl, string applicationId, CancellationToken cancellationToken) - { - var client = await basicAuthTokenService.GetBasicAuthorizedClient(dimAuth, cancellationToken).ConfigureAwait(false); - var result = await client.GetAsync($"{dimBaseUrl}/api/v2.0.0/applications/{applicationId}", cancellationToken) - .CatchingIntoServiceExceptionFor("get-application", HttpAsyncResponseMessageExtension.RecoverOptions.INFRASTRUCTURE, - async m => - { - var message = await m.Content.ReadAsStringAsync().ConfigureAwait(false); - return (false, message); - }).ConfigureAwait(false); - try - { - var response = await result.Content - .ReadFromJsonAsync(JsonSerializerExtensions.Options, cancellationToken) - .ConfigureAwait(false); - if (response == null) - { - throw new ServiceException("Response must not be null"); - } - - return response.ApplicaitonKey; - } - catch (JsonException je) - { - throw new ServiceException(je.Message); - } - } - - public async Task AssignApplicationToCompany(BasicAuthSettings dimAuth, string dimBaseUrl, string applicationKey, Guid companyId, CancellationToken cancellationToken) - { - var client = await basicAuthTokenService.GetBasicAuthorizedClient(dimAuth, cancellationToken).ConfigureAwait(false); - var data = new CompanyIdentityPatch(new ApplicationUpdates(Enumerable.Repeat(applicationKey, 1))); - await client.PatchAsJsonAsync($"{dimBaseUrl}/api/v2.0.0/companyIdentities/{companyId}", data, JsonSerializerExtensions.Options, cancellationToken) - .CatchingIntoServiceExceptionFor("assign-application", HttpAsyncResponseMessageExtension.RecoverOptions.INFRASTRUCTURE, - async m => - { - var message = await m.Content.ReadAsStringAsync().ConfigureAwait(false); - return (false, message); - }).ConfigureAwait(false); - } - public async Task GetStatusList(BasicAuthSettings dimAuth, string dimBaseUrl, Guid companyId, CancellationToken cancellationToken) { - var client = await basicAuthTokenService.GetBasicAuthorizedClient(dimAuth, cancellationToken).ConfigureAwait(false); + var client = await basicAuthTokenService.GetBasicAuthorizedClient(dimAuth, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); var result = await client.GetAsync($"{dimBaseUrl}/api/v2.0.0/companyIdentities/{companyId}/revocationLists", cancellationToken); try { var response = await result.Content .ReadFromJsonAsync(JsonSerializerExtensions.Options, cancellationToken) - .ConfigureAwait(false); - if (response == null) - { - throw new ServiceException("Response must not be null"); - } - - if (!response.Data.Any(x => x.RemainingSpace > 0)) + .ConfigureAwait(ConfigureAwaitOptions.None); + if (response?.Data == null || !response.Data.Any(x => x.RemainingSpace > 0)) { throw new ConflictException("There is no status list with remaining space, please create a new one."); } @@ -182,7 +79,7 @@ public async Task GetStatusList(BasicAuthSettings dimAuth, string dimBas public async Task CreateStatusList(BasicAuthSettings dimAuth, string dimBaseUrl, Guid companyId, CancellationToken cancellationToken) { - var client = await basicAuthTokenService.GetBasicAuthorizedClient(dimAuth, cancellationToken).ConfigureAwait(false); + var client = await basicAuthTokenService.GetBasicAuthorizedClient(dimAuth, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); var data = new CreateStatusListRequest(new CreateStatusListPaypload(new CreateStatusList("StatusList2021", DateTimeOffset.UtcNow.ToString("yyyyMMdd"), "New revocation list", 2097152))); var result = await client.PostAsJsonAsync($"{dimBaseUrl}/api/v2.0.0/companyIdentities/{companyId}/revocationLists", data, JsonSerializerExtensions.Options, cancellationToken) .CatchingIntoServiceExceptionFor("assign-application", HttpAsyncResponseMessageExtension.RecoverOptions.INFRASTRUCTURE, @@ -195,7 +92,7 @@ public async Task CreateStatusList(BasicAuthSettings dimAuth, string dim { var response = await result.Content .ReadFromJsonAsync(JsonSerializerExtensions.Options, cancellationToken) - .ConfigureAwait(false); + .ConfigureAwait(ConfigureAwaitOptions.None); if (response == null) { throw new ServiceException("Response must not be null"); diff --git a/src/clients/Dim.Clients/Api/Dim/IDimClient.cs b/src/clients/Dim.Clients/Api/Dim/IDimClient.cs index bff98c4..63504e8 100644 --- a/src/clients/Dim.Clients/Api/Dim/IDimClient.cs +++ b/src/clients/Dim.Clients/Api/Dim/IDimClient.cs @@ -18,6 +18,7 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ +using Dim.Clients.Api.Dim.Models; using Dim.Clients.Token; using System.Text.Json; @@ -25,11 +26,7 @@ namespace Dim.Clients.Api.Dim; public interface IDimClient { - Task CreateCompanyIdentity(BasicAuthSettings dimBasicAuth, Guid tenantId, string hostingUrl, string baseUrl, bool isIssuer, CancellationToken cancellationToken); - Task GetDidDocument(string url, CancellationToken cancellationToken); - Task CreateApplication(BasicAuthSettings dimAuth, string dimBaseUrl, string tenantName, CancellationToken cancellationToken); - Task GetApplication(BasicAuthSettings dimAuth, string dimBaseUrl, string applicationId, CancellationToken cancellationToken); - Task AssignApplicationToCompany(BasicAuthSettings dimAuth, string dimBaseUrl, string applicationKey, Guid companyId, CancellationToken cancellationToken); + Task GetCompanyData(BasicAuthSettings dimAuth, string dimBaseUrl, string tenantName, string application, CancellationToken cancellationToken); Task GetStatusList(BasicAuthSettings dimAuth, string dimBaseUrl, Guid companyId, CancellationToken cancellationToken); Task CreateStatusList(BasicAuthSettings dimAuth, string dimBaseUrl, Guid companyId, CancellationToken cancellationToken); } diff --git a/src/clients/Dim.Clients/Api/Dim/CompanyIdentityPatch.cs b/src/clients/Dim.Clients/Api/Dim/Models/CompanyData.cs similarity index 77% rename from src/clients/Dim.Clients/Api/Dim/CompanyIdentityPatch.cs rename to src/clients/Dim.Clients/Api/Dim/Models/CompanyData.cs index f66264e..db8b66b 100644 --- a/src/clients/Dim.Clients/Api/Dim/CompanyIdentityPatch.cs +++ b/src/clients/Dim.Clients/Api/Dim/Models/CompanyData.cs @@ -20,12 +20,13 @@ using System.Text.Json.Serialization; -namespace Dim.Clients.Api.Dim; +namespace Dim.Clients.Api.Dim.Models; -public record CompanyIdentityPatch( - [property: JsonPropertyName("applicationUpdates")] ApplicationUpdates ApplicationUpdates +public record CompanyIdentitiesResponse( + [property: JsonPropertyName("data")] IEnumerable Data ); -public record ApplicationUpdates( - [property: JsonPropertyName("assignApplications")] IEnumerable AssignApplications +public record CompanyData( + [property: JsonPropertyName("id")] Guid CompanyId, + [property: JsonPropertyName("downloadURL")] string DownloadUrl ); diff --git a/src/clients/Dim.Clients/Api/Dim/StatusListResponse.cs b/src/clients/Dim.Clients/Api/Dim/Models/StatusListResponse.cs similarity index 98% rename from src/clients/Dim.Clients/Api/Dim/StatusListResponse.cs rename to src/clients/Dim.Clients/Api/Dim/Models/StatusListResponse.cs index fa3eb40..82ac365 100644 --- a/src/clients/Dim.Clients/Api/Dim/StatusListResponse.cs +++ b/src/clients/Dim.Clients/Api/Dim/Models/StatusListResponse.cs @@ -20,7 +20,7 @@ using System.Text.Json.Serialization; -namespace Dim.Clients.Api.Dim; +namespace Dim.Clients.Api.Dim.Models; public record CreateStatusListRequest( [property: JsonPropertyName("payload")] CreateStatusListPaypload Payload diff --git a/src/clients/Dim.Clients/Api/Directories/DependencyInjection/DirectoryClientServiceExtensions.cs b/src/clients/Dim.Clients/Api/Directories/DependencyInjection/DirectoryClientServiceExtensions.cs deleted file mode 100644 index 3d5a9e1..0000000 --- a/src/clients/Dim.Clients/Api/Directories/DependencyInjection/DirectoryClientServiceExtensions.cs +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using Dim.Clients.Extensions; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; - -namespace Dim.Clients.Api.Directories.DependencyInjection; - -public static class DirectoryClientServiceExtensions -{ - public static IServiceCollection AddDirectoryClient(this IServiceCollection services, IConfigurationSection section) - { - services.AddOptions() - .Bind(section) - .ValidateOnStart(); - - var sp = services.BuildServiceProvider(); - var settings = sp.GetRequiredService>(); - services - .AddCustomHttpClientWithAuthentication(settings.Value.BaseUrl, settings.Value.TokenAddress) - .AddTransient(); - - return services; - } -} diff --git a/src/clients/Dim.Clients/Api/Directories/DependencyInjection/DirectorySettings.cs b/src/clients/Dim.Clients/Api/Directories/DependencyInjection/DirectorySettings.cs deleted file mode 100644 index f4ce443..0000000 --- a/src/clients/Dim.Clients/Api/Directories/DependencyInjection/DirectorySettings.cs +++ /dev/null @@ -1,30 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using Dim.Clients.Token; -using System.ComponentModel.DataAnnotations; - -namespace Dim.Clients.Api.Directories.DependencyInjection; - -public class DirectorySettings : BasicAuthSettings -{ - [Required] - public string BaseUrl { get; set; } = null!; -} diff --git a/src/clients/Dim.Clients/Api/Directories/DirectoryClient.cs b/src/clients/Dim.Clients/Api/Directories/DirectoryClient.cs deleted file mode 100644 index fd50052..0000000 --- a/src/clients/Dim.Clients/Api/Directories/DirectoryClient.cs +++ /dev/null @@ -1,76 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using Dim.Clients.Api.Directories.DependencyInjection; -using Dim.Clients.Extensions; -using Dim.Clients.Token; -using Microsoft.Extensions.Options; -using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; -using Org.Eclipse.TractusX.Portal.Backend.Framework.HttpClientExtensions; -using System.Net.Http.Json; -using System.Text.Json; - -namespace Dim.Clients.Api.Directories; - -public class DirectoryClient : IDirectoryClient -{ - private readonly DirectorySettings _settings; - private readonly IBasicAuthTokenService _basicAuthTokenService; - - public DirectoryClient(IBasicAuthTokenService basicAuthTokenService, IOptions settings) - { - _basicAuthTokenService = basicAuthTokenService; - _settings = settings.Value; - } - - public async Task CreateDirectory(string description, string bpnl, Guid parentId, CancellationToken cancellationToken) - { - var client = await _basicAuthTokenService.GetBasicAuthorizedClient(_settings, cancellationToken).ConfigureAwait(false); - var directory = new DirectoryRequest( - description, - Enumerable.Repeat("phil.schneider@digitalnativesolutions.de", 1), - bpnl, - new Dictionary>() - { - { "cloud_management_service", new[] { "Created by API - Don't change it" } } - } - ); - - var result = await client.PostAsJsonAsync($"/accounts/v1/directories?parentId={parentId}", directory, JsonSerializerExtensions.Options, cancellationToken) - .CatchingIntoServiceExceptionFor("create-directory", HttpAsyncResponseMessageExtension.RecoverOptions.INFRASTRUCTURE).ConfigureAwait(false); - try - { - var response = await result.Content - .ReadFromJsonAsync(JsonSerializerExtensions.Options, cancellationToken) - .ConfigureAwait(false); - - if (response == null) - { - throw new ServiceException("Directory response must not be null"); - } - - return response.Id; - } - catch (JsonException je) - { - throw new ServiceException(je.Message); - } - } -} diff --git a/src/clients/Dim.Clients/Api/Directories/DirectoryRequest.cs b/src/clients/Dim.Clients/Api/Directories/DirectoryRequest.cs deleted file mode 100644 index 64cdd7a..0000000 --- a/src/clients/Dim.Clients/Api/Directories/DirectoryRequest.cs +++ /dev/null @@ -1,35 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using System.Text.Json.Serialization; - -namespace Dim.Clients.Api.Directories; - -public record DirectoryRequest( - [property: JsonPropertyName("description")] string Description, - [property: JsonPropertyName("directoryAdmins")] IEnumerable DirectoryAdmins, - [property: JsonPropertyName("displayName")] string DisplayName, - [property: JsonPropertyName("labels")] Dictionary> Labels -); - -public record DirectoryResponse( - [property: JsonPropertyName("guid")] Guid Id, - [property: JsonPropertyName("subdomain")] string Subdomain -); diff --git a/src/clients/Dim.Clients/Api/Directories/IDirectoryClient.cs b/src/clients/Dim.Clients/Api/Directories/IDirectoryClient.cs deleted file mode 100644 index a145d93..0000000 --- a/src/clients/Dim.Clients/Api/Directories/IDirectoryClient.cs +++ /dev/null @@ -1,26 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -namespace Dim.Clients.Api.Directories; - -public interface IDirectoryClient -{ - Task CreateDirectory(string description, string bpnl, Guid parentId, CancellationToken cancellationToken); -} diff --git a/src/clients/Dim.Clients/Api/Provisioning/DependencyInjection/ProvisioningClientServiceExtensions.cs b/src/clients/Dim.Clients/Api/Div/DependencyInjection/ProvisioningClientServiceExtensions.cs similarity index 71% rename from src/clients/Dim.Clients/Api/Provisioning/DependencyInjection/ProvisioningClientServiceExtensions.cs rename to src/clients/Dim.Clients/Api/Div/DependencyInjection/ProvisioningClientServiceExtensions.cs index c46310d..1f6d825 100644 --- a/src/clients/Dim.Clients/Api/Provisioning/DependencyInjection/ProvisioningClientServiceExtensions.cs +++ b/src/clients/Dim.Clients/Api/Div/DependencyInjection/ProvisioningClientServiceExtensions.cs @@ -19,16 +19,25 @@ ********************************************************************************/ using Dim.Clients.Extensions; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; -namespace Dim.Clients.Api.Provisioning.DependencyInjection; +namespace Dim.Clients.Api.Div.DependencyInjection; public static class ProvisioningClientServiceExtensions { - public static IServiceCollection AddProvisioningClient(this IServiceCollection services) + public static IServiceCollection AddProvisioningClient(this IServiceCollection services, IConfigurationSection section) { + services.AddOptions() + .Bind(section) + .ValidateOnStart() + .ValidateDataAnnotations(); + + var sp = services.BuildServiceProvider(); + var settings = sp.GetRequiredService>(); services - .AddCustomHttpClientWithAuthentication(null, null) + .AddCustomHttpClientWithAuthentication(settings.Value.BaseUrl, settings.Value.TokenAddress) .AddTransient(); return services; diff --git a/src/clients/Dim.Clients/Api/Provisioning/DependencyInjection/ProvisioningSettings.cs b/src/clients/Dim.Clients/Api/Div/DependencyInjection/ProvisioningSettings.cs similarity index 95% rename from src/clients/Dim.Clients/Api/Provisioning/DependencyInjection/ProvisioningSettings.cs rename to src/clients/Dim.Clients/Api/Div/DependencyInjection/ProvisioningSettings.cs index 48fd5a3..034953d 100644 --- a/src/clients/Dim.Clients/Api/Provisioning/DependencyInjection/ProvisioningSettings.cs +++ b/src/clients/Dim.Clients/Api/Div/DependencyInjection/ProvisioningSettings.cs @@ -21,7 +21,7 @@ using Dim.Clients.Token; using System.ComponentModel.DataAnnotations; -namespace Dim.Clients.Api.Provisioning.DependencyInjection; +namespace Dim.Clients.Api.Div.DependencyInjection; public class ProvisioningSettings : BasicAuthSettings { diff --git a/src/clients/Dim.Clients/Api/Provisioning/IProvisioningClient.cs b/src/clients/Dim.Clients/Api/Div/IProvisioningClient.cs similarity index 73% rename from src/clients/Dim.Clients/Api/Provisioning/IProvisioningClient.cs rename to src/clients/Dim.Clients/Api/Div/IProvisioningClient.cs index 41e3825..d1f12cc 100644 --- a/src/clients/Dim.Clients/Api/Provisioning/IProvisioningClient.cs +++ b/src/clients/Dim.Clients/Api/Div/IProvisioningClient.cs @@ -18,11 +18,12 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -using Dim.Clients.Api.Services; +using Dim.Clients.Api.Div.Models; -namespace Dim.Clients.Api.Provisioning; +namespace Dim.Clients.Api.Div; public interface IProvisioningClient { - Task CreateCloudFoundryEnvironment(string authUrl, BindingItem bindingData, string tenantName, string user, CancellationToken cancellationToken); + public Task CreateOperation(Guid customerId, string customerName, string applicationName, string companyName, string didDocumentLocation, bool isIssuer, CancellationToken cancellationToken); + public Task GetOperation(Guid operationId, CancellationToken cancellationToken); } diff --git a/src/clients/Dim.Clients/Api/Div/Models/CreateOperationRequest.cs b/src/clients/Dim.Clients/Api/Div/Models/CreateOperationRequest.cs new file mode 100644 index 0000000..ba2af39 --- /dev/null +++ b/src/clients/Dim.Clients/Api/Div/Models/CreateOperationRequest.cs @@ -0,0 +1,73 @@ +/******************************************************************************** + * Copyright (c) 2024 BMW Group AG + * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +using Dim.Clients.Api.Dim; +using System.Text.Json.Serialization; + +namespace Dim.Clients.Api.Div.Models; + +public record OperationCreationRequest( + [property: JsonPropertyName("action")] string Action, + [property: JsonPropertyName("entity")] string Entity, + [property: JsonPropertyName("payload")] OperationPayloadData Payload +); + +public record OperationPayloadData( + [property: JsonPropertyName("customerId")] string CustomerId, + [property: JsonPropertyName("customerName")] string CustomerName, + [property: JsonPropertyName("divWalletServiceName")] string WalletServiceName, + [property: JsonPropertyName("divWalletServiceParameters")] WalletServiceParameter WalletServiceParameter +); + +public record WalletServiceParameter( + [property: JsonPropertyName("applications")] IEnumerable Applications +); + +public record WalletApplication( + [property: JsonPropertyName("application")] string Application, + [property: JsonPropertyName("companies")] IEnumerable Companies, + [property: JsonPropertyName("trustedIssuers")] IEnumerable TrustedIssuers +); + +public record ApplicationCompany( + [property: JsonPropertyName("name")] string Name, + [property: JsonPropertyName("hostingURL")] string HostingUrl, + [property: JsonPropertyName("services")] IEnumerable Services, + [property: JsonPropertyName("keys")] IEnumerable Keys +); + +public record ApplicationCompanyService( + [property: JsonPropertyName("type")] string Type, + [property: JsonPropertyName("serviceEndpoint")] string ServiceEndpoint +); + +public record ApplicationCompanyKey( + [property: JsonPropertyName("type")] string Type +); + +public record TrustedIssuer( + [property: JsonPropertyName("name")] string Name, + [property: JsonPropertyName("did")] string Did, + [property: JsonPropertyName("ignoreMissingHashlist")] bool IgnoreMissingHashlist +); + +public record CreateOperationRequest( + [property: JsonPropertyName("operationId")] Guid OperationId +); diff --git a/src/clients/Dim.Clients/Api/Div/Models/OperationResponse.cs b/src/clients/Dim.Clients/Api/Div/Models/OperationResponse.cs new file mode 100644 index 0000000..61f77cf --- /dev/null +++ b/src/clients/Dim.Clients/Api/Div/Models/OperationResponse.cs @@ -0,0 +1,58 @@ +/******************************************************************************** + * Copyright (c) 2024 BMW Group AG + * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +using System.Text.Json.Serialization; + +namespace Dim.Clients.Api.Div.Models; + +public record OperationResponse( + [property: JsonPropertyName("operationId")] Guid OperationId, + [property: JsonPropertyName("status")] OperationResponseStatus Status, + [property: JsonPropertyName("createdAt")] DateTimeOffset? CreatedAt, + [property: JsonPropertyName("error")] string? Error, + [property: JsonPropertyName("data")] OperationResponseData? Data +); + +public record OperationResponseData( + [property: JsonPropertyName("customerWalletId")] Guid CustomerWalletId, + [property: JsonPropertyName("customerId")] string CustomerId, + [property: JsonPropertyName("customerName")] string CustomerName, + [property: JsonPropertyName("serviceKey")] ServiceKey ServiceKey +); + +public record ServiceKey( + [property: JsonPropertyName("uaa")] ServiceUaa Uaa, + [property: JsonPropertyName("url")] string Url, + [property: JsonPropertyName("vendor")] string Vendor +); + +public record ServiceUaa( + [property: JsonPropertyName("apiurl")] string ApiUrl, + [property: JsonPropertyName("url")] string Url, + [property: JsonPropertyName("clientid")] string ClientId, + [property: JsonPropertyName("clientsecret")] string ClientSecret +); + +public enum OperationResponseStatus +{ + pending = 1, + failed = 2, + completed = 3 +} diff --git a/src/clients/Dim.Clients/Api/Div/ProvisioningClient.cs b/src/clients/Dim.Clients/Api/Div/ProvisioningClient.cs new file mode 100644 index 0000000..9d21f77 --- /dev/null +++ b/src/clients/Dim.Clients/Api/Div/ProvisioningClient.cs @@ -0,0 +1,132 @@ +/******************************************************************************** + * Copyright (c) 2024 BMW Group AG + * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +using Dim.Clients.Api.Div.DependencyInjection; +using Dim.Clients.Api.Div.Models; +using Dim.Clients.Extensions; +using Dim.Clients.Token; +using Microsoft.Extensions.Options; +using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; +using Org.Eclipse.TractusX.Portal.Backend.Framework.HttpClientExtensions; +using System.Net.Http.Json; +using System.Text.Json; + +namespace Dim.Clients.Api.Div; + +public class ProvisioningClient(IBasicAuthTokenService basicAuthTokenService, IOptions settings) : IProvisioningClient +{ + private readonly ProvisioningSettings _settings = settings.Value; + + public async Task CreateOperation(Guid customerId, string customerName, string applicationName, string companyName, string didDocumentLocation, bool isIssuer, CancellationToken cancellationToken) + { + var data = new OperationCreationRequest( + "provision", + "customer-wallet", + new OperationPayloadData( + customerId.ToString(), + customerName, + "main", + new WalletServiceParameter( + Enumerable.Repeat(new WalletApplication( + applicationName, + Enumerable.Repeat(new ApplicationCompany( + companyName, + didDocumentLocation, + [new ApplicationCompanyService("CredentialService", "https://dis-agent-prod.eu10.dim.cloud.sap/api/v1.0.0/iatp")], + isIssuer ? + [ + new("SIGNING"), + new("SIGNING_VC") + ] : + new ApplicationCompanyKey[] + { + new("SIGNING") + } + ), 1), + Enumerable.Empty() + ), 1) + ) + ) + ); + var client = await basicAuthTokenService + .GetBasicAuthorizedClient(_settings, cancellationToken) + .ConfigureAwait(false); + var result = await client.PostAsJsonAsync("/api/v1.0.0/operations", data, JsonSerializerExtensions.Options, cancellationToken) + .CatchingIntoServiceExceptionFor("create-operation", HttpAsyncResponseMessageExtension.RecoverOptions.INFRASTRUCTURE, + async response => + { + var content = await response.Content.ReadAsStringAsync().ConfigureAwait(ConfigureAwaitOptions.None); + return (false, content); + }) + .ConfigureAwait(false); + try + { + var response = await result.Content + .ReadFromJsonAsync(JsonSerializerExtensions.Options, cancellationToken) + .ConfigureAwait(ConfigureAwaitOptions.None); + + if (response == null) + { + throw new ServiceException("response should never be null here"); + } + + return response.OperationId; + } + catch (JsonException je) + { + throw new ServiceException(je.Message); + } + } + + public async Task GetOperation(Guid operationId, CancellationToken cancellationToken) + { + var client = await basicAuthTokenService + .GetBasicAuthorizedClient(_settings, cancellationToken) + .ConfigureAwait(false); + var result = await client.GetAsync($"/api/v1.0.0/operations/{operationId}", cancellationToken) + .CatchingIntoServiceExceptionFor("get-operation", HttpAsyncResponseMessageExtension.RecoverOptions.INFRASTRUCTURE, + async response => + { + var content = await response.Content.ReadAsStringAsync().ConfigureAwait(ConfigureAwaitOptions.None); + return (false, content); + }).ConfigureAwait(false); + try + { + var response = await result.Content + .ReadFromJsonAsync(JsonSerializerExtensions.Options, cancellationToken) + .ConfigureAwait(ConfigureAwaitOptions.None); + if (response == null) + { + throw new ServiceException("Response must not be null"); + } + + if (response.Status == OperationResponseStatus.failed) + { + throw new ServiceException($"Operation Creation failed with error: {response.Error}"); + } + + return response; + } + catch (JsonException je) + { + throw new ServiceException(je.Message); + } + } +} diff --git a/src/clients/Dim.Clients/Api/Entitlements/CreateSubAccountRequest.cs b/src/clients/Dim.Clients/Api/Entitlements/CreateSubAccountRequest.cs deleted file mode 100644 index ce4d5c2..0000000 --- a/src/clients/Dim.Clients/Api/Entitlements/CreateSubAccountRequest.cs +++ /dev/null @@ -1,39 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using System.Text.Json.Serialization; - -namespace Dim.Clients.Api.Entitlements; - -public record CreateSubAccountRequest( - [property: JsonPropertyName("subaccountServicePlans")] IEnumerable SubaccountServicePlans -); - -public record SubaccountServicePlan( - [property: JsonPropertyName("assignmentInfo")] IEnumerable AssignmentInfo, - [property: JsonPropertyName("serviceName")] string ServiceName, - [property: JsonPropertyName("servicePlanName")] string ServicePlanName -); - -public record AssignmentInfo( - [property: JsonPropertyName("enable")] bool? Enabled, - [property: JsonPropertyName("amount")] int? Amount, - [property: JsonPropertyName("subaccountGUID")] Guid SubaccountId -); diff --git a/src/clients/Dim.Clients/Api/Entitlements/DependencyInjection/EntitlementClientServiceExtensions.cs b/src/clients/Dim.Clients/Api/Entitlements/DependencyInjection/EntitlementClientServiceExtensions.cs deleted file mode 100644 index 751b551..0000000 --- a/src/clients/Dim.Clients/Api/Entitlements/DependencyInjection/EntitlementClientServiceExtensions.cs +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using Dim.Clients.Extensions; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; - -namespace Dim.Clients.Api.Entitlements.DependencyInjection; - -public static class EntitlementClientServiceExtensions -{ - public static IServiceCollection AddEntitlementClient(this IServiceCollection services, IConfigurationSection section) - { - services.AddOptions() - .Bind(section) - .ValidateOnStart(); - - var sp = services.BuildServiceProvider(); - var settings = sp.GetRequiredService>(); - services - .AddCustomHttpClientWithAuthentication(settings.Value.BaseUrl, null) - .AddTransient(); - - return services; - } -} diff --git a/src/clients/Dim.Clients/Api/Entitlements/DependencyInjection/EntitlementSettings.cs b/src/clients/Dim.Clients/Api/Entitlements/DependencyInjection/EntitlementSettings.cs deleted file mode 100644 index 6fdd346..0000000 --- a/src/clients/Dim.Clients/Api/Entitlements/DependencyInjection/EntitlementSettings.cs +++ /dev/null @@ -1,29 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using System.ComponentModel.DataAnnotations; - -namespace Dim.Clients.Api.Entitlements.DependencyInjection; - -public class EntitlementSettings -{ - [Required] - public string BaseUrl { get; set; } = null!; -} diff --git a/src/clients/Dim.Clients/Api/Entitlements/EntitlementClient.cs b/src/clients/Dim.Clients/Api/Entitlements/EntitlementClient.cs deleted file mode 100644 index 5b79bdd..0000000 --- a/src/clients/Dim.Clients/Api/Entitlements/EntitlementClient.cs +++ /dev/null @@ -1,53 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using Dim.Clients.Extensions; -using Dim.Clients.Token; -using Org.Eclipse.TractusX.Portal.Backend.Framework.HttpClientExtensions; -using System.Net.Http.Json; - -namespace Dim.Clients.Api.Entitlements; - -public class EntitlementClient : IEntitlementClient -{ - private readonly IBasicAuthTokenService _basicAuthTokenService; - - public EntitlementClient(IBasicAuthTokenService basicAuthTokenService) - { - _basicAuthTokenService = basicAuthTokenService; - } - - public async Task AssignEntitlements(BasicAuthSettings basicAuthSettings, Guid subAccountId, CancellationToken cancellationToken) - { - var client = await _basicAuthTokenService.GetBasicAuthorizedClient(basicAuthSettings, cancellationToken).ConfigureAwait(false); - var data = new CreateSubAccountRequest( - new List - { - new(Enumerable.Repeat(new AssignmentInfo(true, null, subAccountId), 1), "cis", "local"), - new(Enumerable.Repeat(new AssignmentInfo(true, null, subAccountId), 1), "decentralized-identity-management-app", "standard"), - new(Enumerable.Repeat(new AssignmentInfo(null, 1, subAccountId), 1), "decentralized-identity-management", "standard"), - new(Enumerable.Repeat(new AssignmentInfo(true, null, subAccountId), 1), "auditlog-viewer", "free") - } - ); - - await client.PutAsJsonAsync("/entitlements/v1/subaccountServicePlans", data, JsonSerializerExtensions.Options, cancellationToken) - .CatchingIntoServiceExceptionFor("assign-entitlements", HttpAsyncResponseMessageExtension.RecoverOptions.ALLWAYS).ConfigureAwait(false); - } -} diff --git a/src/clients/Dim.Clients/Api/Entitlements/IEntitlementClient.cs b/src/clients/Dim.Clients/Api/Entitlements/IEntitlementClient.cs deleted file mode 100644 index da46df6..0000000 --- a/src/clients/Dim.Clients/Api/Entitlements/IEntitlementClient.cs +++ /dev/null @@ -1,28 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using Dim.Clients.Token; - -namespace Dim.Clients.Api.Entitlements; - -public interface IEntitlementClient -{ - Task AssignEntitlements(BasicAuthSettings basicAuthSettings, Guid subAccountId, CancellationToken cancellationToken); -} diff --git a/src/clients/Dim.Clients/Api/Provisioning/CreateCfeRequest.cs b/src/clients/Dim.Clients/Api/Provisioning/CreateCfeRequest.cs deleted file mode 100644 index 692da27..0000000 --- a/src/clients/Dim.Clients/Api/Provisioning/CreateCfeRequest.cs +++ /dev/null @@ -1,36 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using System.Text.Json.Serialization; - -namespace Dim.Clients.Api.Provisioning; - -public record CreateCfeRequest( - [property: JsonPropertyName("environmentType")] string EnvironmentType, - [property: JsonPropertyName("parameters")] Dictionary Parameters, - [property: JsonPropertyName("landscapeLabel")] string LandscapeLabel, - [property: JsonPropertyName("planName")] string PlanName, - [property: JsonPropertyName("serviceName")] string ServiceName, - [property: JsonPropertyName("user")] string User -); - -public record CreateCfeResponse( - [property: JsonPropertyName("id")] Guid Id -); diff --git a/src/clients/Dim.Clients/Api/Provisioning/ProvisioningClient.cs b/src/clients/Dim.Clients/Api/Provisioning/ProvisioningClient.cs deleted file mode 100644 index a986b87..0000000 --- a/src/clients/Dim.Clients/Api/Provisioning/ProvisioningClient.cs +++ /dev/null @@ -1,62 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using Dim.Clients.Api.Services; -using Dim.Clients.Extensions; -using Dim.Clients.Token; -using Org.Eclipse.TractusX.Portal.Backend.Framework.HttpClientExtensions; -using System.Net.Http.Json; - -namespace Dim.Clients.Api.Provisioning; - -public class ProvisioningClient : IProvisioningClient -{ - private readonly IBasicAuthTokenService _basicAuthTokenService; - - public ProvisioningClient(IBasicAuthTokenService basicAuthTokenService) - { - _basicAuthTokenService = basicAuthTokenService; - } - - public async Task CreateCloudFoundryEnvironment(string authUrl, BindingItem bindingData, string tenantName, string user, CancellationToken cancellationToken) - { - var authSettings = new BasicAuthSettings - { - TokenAddress = $"{authUrl}/oauth/token", - ClientId = bindingData.Credentials.Uaa.ClientId, - ClientSecret = bindingData.Credentials.Uaa.ClientSecret - }; - var client = await _basicAuthTokenService.GetBasicAuthorizedClient(authSettings, cancellationToken).ConfigureAwait(false); - var data = new CreateCfeRequest( - "cloudfoundry", - new Dictionary - { - { "instance_name", tenantName } - }, - "cf-eu10", - "standard", - "cloudfoundry", - user - ); - - await client.PostAsJsonAsync($"{bindingData.Credentials.Endpoints.ProvisioningServiceUrl}/provisioning/v1/environments", data, JsonSerializerExtensions.Options, cancellationToken) - .CatchingIntoServiceExceptionFor("create-cf-env", HttpAsyncResponseMessageExtension.RecoverOptions.INFRASTRUCTURE).ConfigureAwait(false); - } -} diff --git a/src/clients/Dim.Clients/Api/Services/CreateServiceInstanceRequest.cs b/src/clients/Dim.Clients/Api/Services/CreateServiceInstanceRequest.cs deleted file mode 100644 index e311673..0000000 --- a/src/clients/Dim.Clients/Api/Services/CreateServiceInstanceRequest.cs +++ /dev/null @@ -1,71 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using System.Text.Json.Serialization; - -namespace Dim.Clients.Api.Services; - -public record CreateServiceInstanceRequest( - [property: JsonPropertyName("name")] string Name, - [property: JsonPropertyName("service_offering_name")] string ServiceOfferingName, - [property: JsonPropertyName("service_plan_name")] string ServicePlanName, - [property: JsonPropertyName("parameters")] Dictionary Parameters -); - -public record CreateServiceInstanceResponse( - [property: JsonPropertyName("id")] string Id, - [property: JsonPropertyName("name")] string Name -); - -public record CreateServiceBindingRequest( - [property: JsonPropertyName("name")] string Name, - [property: JsonPropertyName("service_instance_id")] string ServiceInstanceId -); - -public record CreateServiceBindingResponse( - [property: JsonPropertyName("id")] string Id, - [property: JsonPropertyName("name")] string Name -); - -public record GetBindingResponse( - [property: JsonPropertyName("items")] IEnumerable Items -); - -public record BindingItem( - [property: JsonPropertyName("name")] string Name, - [property: JsonPropertyName("service_instance_id")] Guid ServiceInstanceId, - [property: JsonPropertyName("credentials")] BindingCredentials Credentials -); - -public record BindingCredentials( - [property: JsonPropertyName("endpoints")] GetBindingEndpoints Endpoints, - [property: JsonPropertyName("grant_type")] string GrantType, - [property: JsonPropertyName("uaa")] GetBindingUaa Uaa -); - -public record GetBindingUaa( - [property: JsonPropertyName("clientid")] string ClientId, - [property: JsonPropertyName("clientsecret")] string ClientSecret -); - -public record GetBindingEndpoints( - [property: JsonPropertyName("provisioning_service_url")] string ProvisioningServiceUrl, - [property: JsonPropertyName("saas_registry_service_url")] string SaasRegistryServiceUrl -); diff --git a/src/clients/Dim.Clients/Api/Services/DependencyInjection/ServiceClientServiceExtensions.cs b/src/clients/Dim.Clients/Api/Services/DependencyInjection/ServiceClientServiceExtensions.cs deleted file mode 100644 index ac75302..0000000 --- a/src/clients/Dim.Clients/Api/Services/DependencyInjection/ServiceClientServiceExtensions.cs +++ /dev/null @@ -1,36 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using Dim.Clients.Extensions; -using Microsoft.Extensions.DependencyInjection; - -namespace Dim.Clients.Api.Services.DependencyInjection; - -public static class ServiceClientServiceExtensions -{ - public static IServiceCollection AddServiceClient(this IServiceCollection services) - { - services - .AddCustomHttpClientWithAuthentication(null, null) - .AddTransient(); - - return services; - } -} diff --git a/src/clients/Dim.Clients/Api/Services/IServiceClient.cs b/src/clients/Dim.Clients/Api/Services/IServiceClient.cs deleted file mode 100644 index 230c7ce..0000000 --- a/src/clients/Dim.Clients/Api/Services/IServiceClient.cs +++ /dev/null @@ -1,30 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using Dim.Clients.Api.SubAccounts; - -namespace Dim.Clients.Api.Services; - -public interface IServiceClient -{ - Task CreateServiceInstance(ServiceManagementBindingItem saBinding, CancellationToken cancellationToken); - Task CreateServiceBinding(ServiceManagementBindingItem saBinding, string serviceInstanceId, CancellationToken cancellationToken); - Task GetServiceBinding(ServiceManagementBindingItem saBinding, string serviceBindingName, CancellationToken cancellationToken); -} diff --git a/src/clients/Dim.Clients/Api/Services/ServiceClient.cs b/src/clients/Dim.Clients/Api/Services/ServiceClient.cs deleted file mode 100644 index d6da273..0000000 --- a/src/clients/Dim.Clients/Api/Services/ServiceClient.cs +++ /dev/null @@ -1,146 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using Dim.Clients.Api.SubAccounts; -using Dim.Clients.Extensions; -using Dim.Clients.Token; -using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; -using Org.Eclipse.TractusX.Portal.Backend.Framework.HttpClientExtensions; -using System.Net.Http.Json; -using System.Text.Json; - -namespace Dim.Clients.Api.Services; - -public class ServiceClient : IServiceClient -{ - private readonly IBasicAuthTokenService _basicAuthTokenService; - - public ServiceClient(IBasicAuthTokenService basicAuthTokenService) - { - _basicAuthTokenService = basicAuthTokenService; - } - - public async Task CreateServiceInstance(ServiceManagementBindingItem saBinding, CancellationToken cancellationToken) - { - var serviceAuth = new BasicAuthSettings - { - TokenAddress = $"{saBinding.Url}/oauth/token", - ClientId = saBinding.ClientId, - ClientSecret = saBinding.ClientSecret - }; - var client = await _basicAuthTokenService.GetBasicAuthorizedClient(serviceAuth, cancellationToken).ConfigureAwait(false); - var directory = new CreateServiceInstanceRequest( - "cis-local-instance", - "cis", - "local", - new Dictionary - { - { "grantType", "clientCredentials" } - } - ); - - var result = await client.PostAsJsonAsync($"{saBinding.SmUrl}/v1/service_instances?async=false", directory, JsonSerializerExtensions.Options, cancellationToken) - .CatchingIntoServiceExceptionFor("create-service-instance", HttpAsyncResponseMessageExtension.RecoverOptions.ALLWAYS).ConfigureAwait(false); - try - { - var response = await result.Content - .ReadFromJsonAsync(JsonSerializerExtensions.Options, cancellationToken) - .ConfigureAwait(false); - if (response == null) - { - throw new ServiceException("Response was empty", true); - } - - return response; - } - catch (JsonException je) - { - throw new ServiceException(je.Message); - } - } - - public async Task CreateServiceBinding(ServiceManagementBindingItem saBinding, string serviceInstanceId, CancellationToken cancellationToken) - { - var serviceAuth = new BasicAuthSettings - { - TokenAddress = $"{saBinding.Url}/oauth/token", - ClientId = saBinding.ClientId, - ClientSecret = saBinding.ClientSecret - }; - var client = await _basicAuthTokenService.GetBasicAuthorizedClient(serviceAuth, cancellationToken).ConfigureAwait(false); - var data = new CreateServiceBindingRequest( - "cis-local-binding", - serviceInstanceId - ); - - var result = await client.PostAsJsonAsync($"{saBinding.SmUrl}/v1/service_bindings?async=false", data, JsonSerializerExtensions.Options, cancellationToken) - .CatchingIntoServiceExceptionFor("create-service-binding", HttpAsyncResponseMessageExtension.RecoverOptions.INFRASTRUCTURE).ConfigureAwait(false); - try - { - var response = await result.Content - .ReadFromJsonAsync(JsonSerializerExtensions.Options, cancellationToken) - .ConfigureAwait(false); - if (response == null) - { - throw new ServiceException("Response was empty", true); - } - - return response; - } - catch (JsonException je) - { - throw new ServiceException(je.Message); - } - } - - public async Task GetServiceBinding(ServiceManagementBindingItem saBinding, string serviceBindingName, CancellationToken cancellationToken) - { - var serviceAuth = new BasicAuthSettings - { - TokenAddress = $"{saBinding.Url}/oauth/token", - ClientId = saBinding.ClientId, - ClientSecret = saBinding.ClientSecret - }; - var client = await _basicAuthTokenService.GetBasicAuthorizedClient(serviceAuth, cancellationToken).ConfigureAwait(false); - var result = await client.GetAsync($"{saBinding.SmUrl}/v1/service_bindings?fieldQuery=name eq '{serviceBindingName}'", cancellationToken) - .CatchingIntoServiceExceptionFor("get-service-binding", HttpAsyncResponseMessageExtension.RecoverOptions.INFRASTRUCTURE).ConfigureAwait(false); - try - { - var response = await result.Content - .ReadFromJsonAsync(JsonSerializerExtensions.Options, cancellationToken) - .ConfigureAwait(false); - if (response == null) - { - throw new ServiceException("Response was empty", true); - } - - if (response.Items.Count() != 1) - { - throw new ServiceException($"There must be exactly one binding for {serviceBindingName}"); - } - - return response.Items.Single(); - } - catch (JsonException je) - { - throw new ServiceException(je.Message); - } - } -} diff --git a/src/clients/Dim.Clients/Api/SubAccounts/CreateSubAccountRequest.cs b/src/clients/Dim.Clients/Api/SubAccounts/CreateSubAccountRequest.cs deleted file mode 100644 index d7a7ccb..0000000 --- a/src/clients/Dim.Clients/Api/SubAccounts/CreateSubAccountRequest.cs +++ /dev/null @@ -1,51 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using System.Text.Json.Serialization; - -namespace Dim.Clients.Api.SubAccounts; - -public record CreateSubAccountRequest( - [property: JsonPropertyName("betaEnabled")] bool BetaEnabled, - [property: JsonPropertyName("description")] string Description, - [property: JsonPropertyName("displayName")] string DisplayName, - [property: JsonPropertyName("labels")] Dictionary> Labels, - [property: JsonPropertyName("origin")] string Origin, - [property: JsonPropertyName("parentGUID")] Guid ParentId, - [property: JsonPropertyName("region")] string Region, - [property: JsonPropertyName("subaccountAdmins")] IEnumerable SubaccountAdmins, - [property: JsonPropertyName("subdomain")] string Subdomain, - [property: JsonPropertyName("usedForProduction")] UsedForProduction UsedForProduction -); - -public record CreateSubaccountResponse( - [property: JsonPropertyName("guid")] Guid Id -); - -public record ServiceManagementBindingResponse( - [property: JsonPropertyName("items")] IEnumerable Items -); - -public record ServiceManagementBindingItem( - [property: JsonPropertyName("clientid")] string ClientId, - [property: JsonPropertyName("clientsecret")] string ClientSecret, - [property: JsonPropertyName("sm_url")] string SmUrl, - [property: JsonPropertyName("url")] string Url -); diff --git a/src/clients/Dim.Clients/Api/SubAccounts/DependencyInjection/SubAccountClientServiceExtensions.cs b/src/clients/Dim.Clients/Api/SubAccounts/DependencyInjection/SubAccountClientServiceExtensions.cs deleted file mode 100644 index 77be287..0000000 --- a/src/clients/Dim.Clients/Api/SubAccounts/DependencyInjection/SubAccountClientServiceExtensions.cs +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using Dim.Clients.Extensions; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; - -namespace Dim.Clients.Api.SubAccounts.DependencyInjection; - -public static class SubAccountClientServiceExtensions -{ - public static IServiceCollection AddSubAccountClient(this IServiceCollection services, IConfigurationSection section) - { - services.AddOptions() - .Bind(section) - .ValidateOnStart(); - - var sp = services.BuildServiceProvider(); - var settings = sp.GetRequiredService>(); - services - .AddCustomHttpClientWithAuthentication(settings.Value.BaseUrl, null) - .AddTransient(); - - return services; - } -} diff --git a/src/clients/Dim.Clients/Api/SubAccounts/DependencyInjection/SubAccountSettings.cs b/src/clients/Dim.Clients/Api/SubAccounts/DependencyInjection/SubAccountSettings.cs deleted file mode 100644 index aec5c64..0000000 --- a/src/clients/Dim.Clients/Api/SubAccounts/DependencyInjection/SubAccountSettings.cs +++ /dev/null @@ -1,29 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using System.ComponentModel.DataAnnotations; - -namespace Dim.Clients.Api.SubAccounts.DependencyInjection; - -public class SubAccountSettings -{ - [Required] - public string BaseUrl { get; set; } = null!; -} diff --git a/src/clients/Dim.Clients/Api/SubAccounts/ISubAccountClient.cs b/src/clients/Dim.Clients/Api/SubAccounts/ISubAccountClient.cs deleted file mode 100644 index 7c73a57..0000000 --- a/src/clients/Dim.Clients/Api/SubAccounts/ISubAccountClient.cs +++ /dev/null @@ -1,30 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using Dim.Clients.Token; - -namespace Dim.Clients.Api.SubAccounts; - -public interface ISubAccountClient -{ - Task CreateSubaccount(BasicAuthSettings basicAuthSettings, string adminMail, string tenantName, Guid directoryId, string bpn, CancellationToken cancellationToken); - Task CreateServiceManagerBindings(BasicAuthSettings basicAuthSettings, Guid subAccountId, CancellationToken cancellationToken); - Task GetServiceManagerBindings(BasicAuthSettings basicAuthSettings, Guid subAccountId, CancellationToken cancellationToken); -} diff --git a/src/clients/Dim.Clients/Api/SubAccounts/SubAccountClient.cs b/src/clients/Dim.Clients/Api/SubAccounts/SubAccountClient.cs deleted file mode 100644 index f968336..0000000 --- a/src/clients/Dim.Clients/Api/SubAccounts/SubAccountClient.cs +++ /dev/null @@ -1,114 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using Dim.Clients.Extensions; -using Dim.Clients.Token; -using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; -using Org.Eclipse.TractusX.Portal.Backend.Framework.HttpClientExtensions; -using System.Net.Http.Json; -using System.Text.Json; -using System.Text.RegularExpressions; - -namespace Dim.Clients.Api.SubAccounts; - -public class SubAccountClient(IBasicAuthTokenService basicAuthTokenService) : ISubAccountClient -{ - public async Task CreateSubaccount(BasicAuthSettings basicAuthSettings, string adminMail, string tenantName, Guid directoryId, string bpn, CancellationToken cancellationToken) - { - var client = await basicAuthTokenService.GetBasicAuthorizedClient(basicAuthSettings, cancellationToken).ConfigureAwait(false); - var directory = new CreateSubAccountRequest( - false, - $"CX customer sub-account {tenantName}", - tenantName, - new Dictionary> - { - { "cloud_management_service", new[] { "Created by API - Don't change it" } }, - { "tenantName", new[] { tenantName } } - }, - "API", - directoryId, - "eu10", - Enumerable.Repeat(adminMail, 1), - bpn, - UsedForProduction.USED_FOR_PRODUCTION - ); - - var result = await client.PostAsJsonAsync("/accounts/v1/subaccounts", directory, JsonSerializerExtensions.Options, cancellationToken) - .CatchingIntoServiceExceptionFor("create-subaccount", HttpAsyncResponseMessageExtension.RecoverOptions.INFRASTRUCTURE, async message => - { - var errorMessage = await message.Content.ReadAsStringAsync().ConfigureAwait(false); - return new(false, errorMessage); - }).ConfigureAwait(false); - try - { - var response = await result.Content - .ReadFromJsonAsync(JsonSerializerExtensions.Options, cancellationToken) - .ConfigureAwait(false); - - if (response == null) - { - throw new ServiceException("Response must not be null"); - } - - return response.Id; - } - catch (JsonException je) - { - throw new ServiceException(je.Message); - } - } - - public async Task CreateServiceManagerBindings(BasicAuthSettings basicAuthSettings, Guid subAccountId, CancellationToken cancellationToken) - { - var client = await basicAuthTokenService.GetBasicAuthorizedClient(basicAuthSettings, cancellationToken).ConfigureAwait(false); - var data = new - { - name = "accessServiceManager" - }; - - await client.PostAsJsonAsync($"/accounts/v2/subaccounts/{subAccountId}/serviceManagerBindings", data, JsonSerializerExtensions.Options, cancellationToken) - .CatchingIntoServiceExceptionFor("create-subaccount", HttpAsyncResponseMessageExtension.RecoverOptions.ALLWAYS).ConfigureAwait(false); - } - - public async Task GetServiceManagerBindings(BasicAuthSettings basicAuthSettings, Guid subAccountId, CancellationToken cancellationToken) - { - var client = await basicAuthTokenService.GetBasicAuthorizedClient(basicAuthSettings, cancellationToken).ConfigureAwait(false); - - var result = await client.GetAsync($"/accounts/v2/subaccounts/{subAccountId}/serviceManagerBindings", cancellationToken) - .CatchingIntoServiceExceptionFor("create-subaccount", HttpAsyncResponseMessageExtension.RecoverOptions.ALLWAYS).ConfigureAwait(false); - try - { - var response = await result.Content - .ReadFromJsonAsync(JsonSerializerExtensions.Options, cancellationToken) - .ConfigureAwait(false); - - if (response == null || response.Items.Count() != 1) - { - throw new ServiceException("Response must not be null and contain exactly 1 item"); - } - - return response.Items.Single(); - } - catch (JsonException je) - { - throw new ServiceException(je.Message); - } - } -} diff --git a/src/clients/Dim.Clients/Api/SubAccounts/UsedForProduction.cs b/src/clients/Dim.Clients/Api/SubAccounts/UsedForProduction.cs deleted file mode 100644 index 619234a..0000000 --- a/src/clients/Dim.Clients/Api/SubAccounts/UsedForProduction.cs +++ /dev/null @@ -1,28 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -namespace Dim.Clients.Api.SubAccounts; - -public enum UsedForProduction -{ - USED_FOR_PRODUCTION = 1, - NOT_USED_FOR_PRODUCTION = 2, - UNSET = 3 -} diff --git a/src/clients/Dim.Clients/Api/Subscriptions/DependencyInjection/SubscriptionClientServiceExtensions.cs b/src/clients/Dim.Clients/Api/Subscriptions/DependencyInjection/SubscriptionClientServiceExtensions.cs deleted file mode 100644 index 6a6e846..0000000 --- a/src/clients/Dim.Clients/Api/Subscriptions/DependencyInjection/SubscriptionClientServiceExtensions.cs +++ /dev/null @@ -1,36 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using Dim.Clients.Extensions; -using Microsoft.Extensions.DependencyInjection; - -namespace Dim.Clients.Api.Subscriptions.DependencyInjection; - -public static class SubscriptionClientServiceExtensions -{ - public static IServiceCollection AddSubscriptionClient(this IServiceCollection services) - { - services - .AddCustomHttpClientWithAuthentication(null, null) - .AddTransient(); - - return services; - } -} diff --git a/src/clients/Dim.Clients/Api/Subscriptions/ISubscriptionClient.cs b/src/clients/Dim.Clients/Api/Subscriptions/ISubscriptionClient.cs deleted file mode 100644 index 35fbd9a..0000000 --- a/src/clients/Dim.Clients/Api/Subscriptions/ISubscriptionClient.cs +++ /dev/null @@ -1,28 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using Dim.Clients.Api.Services; - -namespace Dim.Clients.Api.Subscriptions; - -public interface ISubscriptionClient -{ - Task SubscribeApplication(string authUrl, BindingItem bindingData, string applicationName, string planName, CancellationToken cancellationToken); -} diff --git a/src/clients/Dim.Clients/Api/Subscriptions/SubscriptionClient.cs b/src/clients/Dim.Clients/Api/Subscriptions/SubscriptionClient.cs deleted file mode 100644 index 04bb6b8..0000000 --- a/src/clients/Dim.Clients/Api/Subscriptions/SubscriptionClient.cs +++ /dev/null @@ -1,55 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2024 BMW Group AG - * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0. - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -using Dim.Clients.Api.Services; -using Dim.Clients.Extensions; -using Dim.Clients.Token; -using Org.Eclipse.TractusX.Portal.Backend.Framework.HttpClientExtensions; -using System.Net.Http.Json; - -namespace Dim.Clients.Api.Subscriptions; - -public class SubscriptionClient : ISubscriptionClient -{ - private readonly IBasicAuthTokenService _basicAuthTokenService; - - public SubscriptionClient(IBasicAuthTokenService basicAuthTokenService) - { - _basicAuthTokenService = basicAuthTokenService; - } - - public async Task SubscribeApplication(string authUrl, BindingItem bindingData, string applicationName, string planName, CancellationToken cancellationToken) - { - var authSettings = new BasicAuthSettings - { - TokenAddress = $"{authUrl}/oauth/token", - ClientId = bindingData.Credentials.Uaa.ClientId, - ClientSecret = bindingData.Credentials.Uaa.ClientSecret - }; - var client = await _basicAuthTokenService.GetBasicAuthorizedClient(authSettings, cancellationToken).ConfigureAwait(false); - var data = new - { - planName = planName - }; - - await client.PostAsJsonAsync($"{bindingData.Credentials.Endpoints.SaasRegistryServiceUrl}/saas-manager/v1/applications/{applicationName}/subscription", data, JsonSerializerExtensions.Options, cancellationToken) - .CatchingIntoServiceExceptionFor("subscribe-application", HttpAsyncResponseMessageExtension.RecoverOptions.INFRASTRUCTURE).ConfigureAwait(false); - } -} diff --git a/src/clients/Dim.Clients/Dim.Clients.csproj b/src/clients/Dim.Clients/Dim.Clients.csproj index 51fb073..25e2339 100644 --- a/src/clients/Dim.Clients/Dim.Clients.csproj +++ b/src/clients/Dim.Clients/Dim.Clients.csproj @@ -35,6 +35,7 @@ + diff --git a/src/clients/Dim.Clients/Token/AuthResponse.cs b/src/clients/Dim.Clients/Token/AuthResponse.cs index ac64494..c19a5dd 100644 --- a/src/clients/Dim.Clients/Token/AuthResponse.cs +++ b/src/clients/Dim.Clients/Token/AuthResponse.cs @@ -29,13 +29,3 @@ public record AuthResponse( [property: JsonPropertyName("jti")] string? Jti, [property: JsonPropertyName("scope")] string? Scope ); - -public record LegacyAuthResponse( - [property: JsonPropertyName("access_token")] string? AccessToken, - [property: JsonPropertyName("token_type")] string? TokenType, - [property: JsonPropertyName("id_token")] string? IdToken, - [property: JsonPropertyName("refresh_token")] string? RefreshToken, - [property: JsonPropertyName("expires_in")] int ExpiresIn, - [property: JsonPropertyName("scope")] string? Scope, - [property: JsonPropertyName("jti")] string? Jti -); diff --git a/src/clients/Dim.Clients/Token/BasicAuthTokenService.cs b/src/clients/Dim.Clients/Token/BasicAuthTokenService.cs index d817fbb..003ddc1 100644 --- a/src/clients/Dim.Clients/Token/BasicAuthTokenService.cs +++ b/src/clients/Dim.Clients/Token/BasicAuthTokenService.cs @@ -25,15 +25,9 @@ namespace Dim.Clients.Token; -public class BasicAuthTokenService : IBasicAuthTokenService +public class BasicAuthTokenService(IHttpClientFactory httpClientFactory) + : IBasicAuthTokenService { - private readonly IHttpClientFactory _httpClientFactory; - - public BasicAuthTokenService(IHttpClientFactory httpClientFactory) - { - _httpClientFactory = httpClientFactory; - } - public async Task GetBasicAuthorizedClient(BasicAuthSettings settings, CancellationToken cancellationToken) { var tokenParameters = new GetBasicTokenSettings( @@ -42,24 +36,9 @@ public async Task GetBasicAuthorizedClient(BasicAuthSettings sett settings.ClientSecret, settings.TokenAddress); - var token = await this.GetBasicTokenAsync(tokenParameters, cancellationToken).ConfigureAwait(false); + var token = await this.GetBasicTokenAsync(tokenParameters, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); - var httpClient = _httpClientFactory.CreateClient(typeof(T).Name); - httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); - return httpClient; - } - - public async Task GetBasicAuthorizedLegacyClient(BasicAuthSettings settings, CancellationToken cancellationToken) - { - var tokenParameters = new GetBasicTokenSettings( - $"{typeof(T).Name}Auth", - settings.ClientId, - settings.ClientSecret, - settings.TokenAddress); - - var token = await this.GetBasicLegacyToken(tokenParameters, cancellationToken).ConfigureAwait(false); - - var httpClient = _httpClientFactory.CreateClient(typeof(T).Name); + var httpClient = httpClientFactory.CreateClient(typeof(T).Name); httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); return httpClient; } @@ -71,7 +50,7 @@ public async Task GetBasicAuthorizedLegacyClient(BasicAuthSetting { "grant_type", "client_credentials" } }; var content = new FormUrlEncodedContent(formParameters); - var authClient = _httpClientFactory.CreateClient(settings.HttpClientName); + var authClient = httpClientFactory.CreateClient(settings.HttpClientName); var authenticationString = $"{settings.ClientId}:{settings.ClientSecret}"; var base64String = Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes(authenticationString)); @@ -80,29 +59,7 @@ public async Task GetBasicAuthorizedLegacyClient(BasicAuthSetting var response = await authClient.PostAsync(settings.TokenAddress, content, cancellationToken) .CatchingIntoServiceExceptionFor("token-post", HttpAsyncResponseMessageExtension.RecoverOptions.INFRASTRUCTURE).ConfigureAwait(false); - var responseObject = await response.Content.ReadFromJsonAsync(JsonSerializerExtensions.Options, cancellationToken).ConfigureAwait(false); - return responseObject?.AccessToken; - } - - private async Task GetBasicLegacyToken(GetBasicTokenSettings settings, CancellationToken cancellationToken) - { - var formParameters = new Dictionary - { - { "username", settings.ClientId }, - { "password", settings.ClientSecret }, - { "client_id", "cf" }, - { "grant_type", "password" }, - { "response_type", "token" } - }; - var content = new FormUrlEncodedContent(formParameters); - var authClient = _httpClientFactory.CreateClient(settings.HttpClientName); - var base64String = Convert.ToBase64String("cf:"u8.ToArray()); - authClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", base64String); - - var response = await authClient.PostAsync(settings.TokenAddress, content, cancellationToken) - .CatchingIntoServiceExceptionFor("token-post", HttpAsyncResponseMessageExtension.RecoverOptions.INFRASTRUCTURE).ConfigureAwait(false); - - var responseObject = await response.Content.ReadFromJsonAsync(JsonSerializerExtensions.Options, cancellationToken).ConfigureAwait(false); + var responseObject = await response.Content.ReadFromJsonAsync(JsonSerializerExtensions.Options, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); return responseObject?.AccessToken; } } diff --git a/src/clients/Dim.Clients/Token/GetBasicTokenSettings.cs b/src/clients/Dim.Clients/Token/GetBasicTokenSettings.cs index 54d8f03..dcd945d 100644 --- a/src/clients/Dim.Clients/Token/GetBasicTokenSettings.cs +++ b/src/clients/Dim.Clients/Token/GetBasicTokenSettings.cs @@ -21,13 +21,3 @@ namespace Dim.Clients.Token; public record GetBasicTokenSettings(string HttpClientName, string ClientId, string ClientSecret, string TokenAddress); - -public record GetTokenSettings( - string HttpClientName, - string Username, - string Password, - string ClientId, - string GrantType, - string ClientSecret, - string Scope, - string TokenUrl); diff --git a/src/clients/Dim.Clients/Token/IBasicAuthTokenService.cs b/src/clients/Dim.Clients/Token/IBasicAuthTokenService.cs index 9c37f2c..299030b 100644 --- a/src/clients/Dim.Clients/Token/IBasicAuthTokenService.cs +++ b/src/clients/Dim.Clients/Token/IBasicAuthTokenService.cs @@ -23,5 +23,4 @@ namespace Dim.Clients.Token; public interface IBasicAuthTokenService { Task GetBasicAuthorizedClient(BasicAuthSettings settings, CancellationToken cancellationToken); - Task GetBasicAuthorizedLegacyClient(BasicAuthSettings settings, CancellationToken cancellationToken); } diff --git a/src/database/Dim.DbAccess/DimRepositories.cs b/src/database/Dim.DbAccess/DimRepositories.cs index 542b173..91b73c8 100644 --- a/src/database/Dim.DbAccess/DimRepositories.cs +++ b/src/database/Dim.DbAccess/DimRepositories.cs @@ -26,27 +26,21 @@ namespace Dim.DbAccess; -public class DimRepositories : IDimRepositories +public class DimRepositories(DimDbContext dbContext) + : IDimRepositories { - private readonly DimDbContext _dbContext; - - private static readonly IReadOnlyDictionary> _types = new Dictionary> { + private static readonly IReadOnlyDictionary> Types = new Dictionary> { { typeof(IProcessStepRepository), context => new ProcessStepRepository(context) }, { typeof(ITenantRepository), context => new TenantRepository(context) } }.ToImmutableDictionary(); - public DimRepositories(DimDbContext dimDbContext) - { - _dbContext = dimDbContext; - } - public RepositoryType GetInstance() { - Object? repository = default; + object? repository = default; - if (_types.TryGetValue(typeof(RepositoryType), out var createFunc)) + if (Types.TryGetValue(typeof(RepositoryType), out var createFunc)) { - repository = createFunc(_dbContext); + repository = createFunc(dbContext); } return (RepositoryType)(repository ?? throw new ArgumentException($"unexpected type {typeof(RepositoryType).Name}", nameof(RepositoryType))); @@ -55,41 +49,17 @@ public RepositoryType GetInstance() /// public TEntity Attach(TEntity entity, Action? setOptionalParameters = null) where TEntity : class { - var attachedEntity = _dbContext.Attach(entity).Entity; + var attachedEntity = dbContext.Attach(entity).Entity; setOptionalParameters?.Invoke(attachedEntity); return attachedEntity; } - public void AttachRange(IEnumerable entities, Action setOptionalParameters) where TEntity : class - { - foreach (var entity in entities) - { - var attachedEntity = _dbContext.Attach(entity).Entity; - setOptionalParameters.Invoke(attachedEntity); - } - } - - public IEnumerable AttachRange(IEnumerable entities) where TEntity : class - { - foreach (var entity in entities) - { - yield return _dbContext.Attach(entity).Entity; - } - } - - /// - public TEntity Remove(TEntity entity) where TEntity : class - => _dbContext.Remove(entity).Entity; - - public void RemoveRange(IEnumerable entities) where TEntity : class - => _dbContext.RemoveRange(entities); - public Task SaveAsync() { try { - return _dbContext.SaveChangesAsync(); + return dbContext.SaveChangesAsync(); } catch (DbUpdateConcurrencyException e) { @@ -97,5 +67,5 @@ public Task SaveAsync() } } - public void Clear() => _dbContext.ChangeTracker.Clear(); + public void Clear() => dbContext.ChangeTracker.Clear(); } diff --git a/src/database/Dim.DbAccess/Extensions/WalletDataExtensions.cs b/src/database/Dim.DbAccess/Extensions/WalletDataExtensions.cs new file mode 100644 index 0000000..209379c --- /dev/null +++ b/src/database/Dim.DbAccess/Extensions/WalletDataExtensions.cs @@ -0,0 +1,38 @@ +using Dim.DbAccess.Models; +using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; + +namespace Dim.DbAccess.Extensions; + +public static class WalletDataExtensions +{ + public static (string TokenAddress, string ClientId, byte[] ClientSecret, byte[] InitializationVector, int EncryptionMode) ValidateData(this WalletData walletData) + { + var (tokenAddress, clientId, clientSecret, initializationVector, encryptionMode) = walletData; + if (string.IsNullOrWhiteSpace(tokenAddress)) + { + throw new ConflictException("TokenAddress must not be null"); + } + + if (string.IsNullOrWhiteSpace(clientId)) + { + throw new ConflictException("ClientId must not be null"); + } + + if (clientSecret == null) + { + throw new ConflictException("Secret must not be null"); + } + + if (initializationVector == null) + { + throw new ConflictException("Vector must not be null"); + } + + if (encryptionMode == null) + { + throw new ConflictException("EncryptionMode must not be null"); + } + + return (tokenAddress, clientId, clientSecret, initializationVector, encryptionMode.Value); + } +} diff --git a/src/database/Dim.DbAccess/IDimRepositories.cs b/src/database/Dim.DbAccess/IDimRepositories.cs index b054bf8..1670e59 100644 --- a/src/database/Dim.DbAccess/IDimRepositories.cs +++ b/src/database/Dim.DbAccess/IDimRepositories.cs @@ -23,17 +23,7 @@ namespace Dim.DbAccess; public interface IDimRepositories { RepositoryType GetInstance(); - - /// TEntity Attach(TEntity entity, Action? setOptionalParameters = null) where TEntity : class; - - void AttachRange(IEnumerable entities, Action setOptionalParameters) where TEntity : class; - IEnumerable AttachRange(IEnumerable entities) where TEntity : class; - - /// - TEntity Remove(TEntity entity) where TEntity : class; - - void RemoveRange(IEnumerable entities) where TEntity : class; Task SaveAsync(); void Clear(); } diff --git a/src/database/Dim.DbAccess/Models/WalletData.cs b/src/database/Dim.DbAccess/Models/WalletData.cs new file mode 100644 index 0000000..0a9d9a5 --- /dev/null +++ b/src/database/Dim.DbAccess/Models/WalletData.cs @@ -0,0 +1,9 @@ +namespace Dim.DbAccess.Models; + +public record WalletData( + string? TokenAddress, + string? ClientId, + byte[]? ClientSecret, + byte[]? InitializationVector, + int? EncryptionMode +); diff --git a/src/database/Dim.DbAccess/Repositories/ITenantRepository.cs b/src/database/Dim.DbAccess/Repositories/ITenantRepository.cs index 1b9240e..dbe7e23 100644 --- a/src/database/Dim.DbAccess/Repositories/ITenantRepository.cs +++ b/src/database/Dim.DbAccess/Repositories/ITenantRepository.cs @@ -18,9 +18,8 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ +using Dim.DbAccess.Models; using Dim.Entities.Entities; -using System; -using System.Threading.Tasks; namespace Dim.DbAccess.Repositories; @@ -29,25 +28,21 @@ public interface ITenantRepository Tenant CreateTenant(string companyName, string bpn, string didDocumentLocation, bool isIssuer, Guid processId, Guid operatorId); Task<(bool Exists, Guid TenantId, string CompanyName, string Bpn)> GetTenantDataForProcessId(Guid processId); void AttachAndModifyTenant(Guid tenantId, Action? initialize, Action modify); - Task GetSubAccountIdByTenantId(Guid tenantId); - Task<(Guid? SubAccountId, string? ServiceInstanceId)> GetSubAccountAndServiceInstanceIdsByTenantId(Guid tenantId); - Task<(Guid? SubAccountId, string? ServiceBindingName)> GetSubAccountIdAndServiceBindingNameByTenantId(Guid tenantId); - Task GetSpaceId(Guid tenantId); - Task GetDimInstanceId(Guid tenantId); - Task<(string bpn, string? DownloadUrl, string? Did, Guid? DimInstanceId)> GetCallbackData(Guid tenantId); - Task<(Guid? DimInstanceId, string HostingUrl, bool IsIssuer)> GetDimInstanceIdAndHostingUrl(Guid tenantId); - Task<(string? ApplicationId, Guid? CompanyId, Guid? DimInstanceId, bool IsIssuer)> GetApplicationAndCompanyId(Guid tenantId); - Task<(bool Exists, Guid? CompanyId, Guid? InstanceId)> GetCompanyAndInstanceIdForBpn(string bpn); + Task<(bool IsIssuer, string? HostingUrl)> GetHostingUrlAndIsIssuer(Guid tenantId); Task<(bool Exists, Guid TenantId)> GetTenantForBpn(string bpn); void CreateTenantTechnicalUser(Guid tenantId, string technicalUserName, Guid externalId, Guid processId); void AttachAndModifyTechnicalUser(Guid technicalUserId, Action? initialize, Action modify); Task<(bool Exists, Guid TechnicalUserId, string CompanyName, string Bpn)> GetTenantDataForTechnicalUserProcessId(Guid processId); - Task<(Guid? spaceId, string technicalUserName)> GetSpaceIdAndTechnicalUserName(Guid technicalUserId); - Task<(Guid ExternalId, string? TokenAddress, string? ClientId, byte[]? ClientSecret, byte[]? InitializationVector, int? EncryptionMode)> GetTechnicalUserCallbackData(Guid technicalUserId); - Task<(Guid? DimInstanceId, Guid? CompanyId)> GetDimInstanceIdAndDid(Guid tenantId); + Task<(Guid ExternalId, WalletData WalletData)> GetTechnicalUserCallbackData(Guid technicalUserId); Task<(bool Exists, Guid TechnicalUserId, Guid ProcessId)> GetTechnicalUserForBpn(string bpn, string technicalUserName); Task GetExternalIdForTechnicalUser(Guid technicalUserId); void RemoveTechnicalUser(Guid technicalUserId); Task IsTenantExisting(string companyName, string bpn); Task GetTenantBpn(Guid tenantId); + Task GetOperationId(Guid tenantId); + Task<(string? BaseUrl, WalletData WalletData)> GetCompanyRequestData(Guid tenantId); + Task<(bool Exists, Guid? CompanyId, string? BaseUrl, WalletData WalletData)> GetCompanyAndWalletDataForBpn(string bpn); + Task<(Guid? CompanyId, string? BaseUrl, WalletData WalletData)> GetStatusListCreationData(Guid tenantId); + Task<(string Bpn, string? BaseUrl, WalletData WalletData, string? Did, string? DownloadUrl)> GetCallbackData(Guid tenantId); + Task<(string? DownloadUrl, bool IsIssuer)> GetDownloadUrlAndIsIssuer(Guid tenantId); } diff --git a/src/database/Dim.DbAccess/Repositories/ProcessStepRepository.cs b/src/database/Dim.DbAccess/Repositories/ProcessStepRepository.cs index cc66c45..15c33c0 100644 --- a/src/database/Dim.DbAccess/Repositories/ProcessStepRepository.cs +++ b/src/database/Dim.DbAccess/Repositories/ProcessStepRepository.cs @@ -26,29 +26,19 @@ namespace Dim.DbAccess.Repositories; -public class ProcessStepRepository : IProcessStepRepository +public class ProcessStepRepository(DimDbContext dbContext) + : IProcessStepRepository { - private readonly DimDbContext _context; - - /// - /// Constructor - /// - /// DimDb context. - public ProcessStepRepository(DimDbContext dimDbContext) - { - _context = dimDbContext; - } - public Process CreateProcess(ProcessTypeId processTypeId) => - _context.Add(new Process(Guid.NewGuid(), processTypeId, Guid.NewGuid())).Entity; + dbContext.Add(new Process(Guid.NewGuid(), processTypeId, Guid.NewGuid())).Entity; - public ProcessStep CreateProcessStep(Dim.Entities.Enums.ProcessStepTypeId processStepTypeId, Dim.Entities.Enums.ProcessStepStatusId processStepStatusId, Guid processId) => - _context.Add(new ProcessStep(Guid.NewGuid(), processStepTypeId, processStepStatusId, processId, DateTimeOffset.UtcNow)).Entity; + public ProcessStep CreateProcessStep(ProcessStepTypeId processStepTypeId, ProcessStepStatusId processStepStatusId, Guid processId) => + dbContext.Add(new ProcessStep(Guid.NewGuid(), processStepTypeId, processStepStatusId, processId, DateTimeOffset.UtcNow)).Entity; - public IEnumerable CreateProcessStepRange(IEnumerable<(Dim.Entities.Enums.ProcessStepTypeId ProcessStepTypeId, Dim.Entities.Enums.ProcessStepStatusId ProcessStepStatusId, Guid ProcessId)> processStepTypeStatus) + public IEnumerable CreateProcessStepRange(IEnumerable<(ProcessStepTypeId ProcessStepTypeId, ProcessStepStatusId ProcessStepStatusId, Guid ProcessId)> processStepTypeStatus) { var processSteps = processStepTypeStatus.Select(x => new ProcessStep(Guid.NewGuid(), x.ProcessStepTypeId, x.ProcessStepStatusId, x.ProcessId, DateTimeOffset.UtcNow)).ToList(); - _context.AddRange(processSteps); + dbContext.AddRange(processSteps); return processSteps; } @@ -56,7 +46,7 @@ public void AttachAndModifyProcessStep(Guid processStepId, Action? { var step = new ProcessStep(processStepId, default, default, Guid.Empty, default); initialize?.Invoke(step); - _context.Attach(step); + dbContext.Attach(step); step.DateLastChanged = DateTimeOffset.UtcNow; modify(step); } @@ -69,7 +59,7 @@ public void AttachAndModifyProcessSteps(IEnumerable<(Guid ProcessStepId, Action< data.Initialize?.Invoke(step); return (Step: step, data.Modify); }).ToList(); - _context.AttachRange(stepModifyData.Select(data => data.Step)); + dbContext.AttachRange(stepModifyData.Select(data => data.Step)); stepModifyData.ForEach(data => { data.Step.DateLastChanged = DateTimeOffset.UtcNow; @@ -78,7 +68,7 @@ public void AttachAndModifyProcessSteps(IEnumerable<(Guid ProcessStepId, Action< } public IAsyncEnumerable GetActiveProcesses(IEnumerable processTypeIds, IEnumerable processStepTypeIds, DateTimeOffset lockExpiryDate) => - _context.Processes + dbContext.Processes .AsNoTracking() .Where(process => processTypeIds.Contains(process.ProcessTypeId) && @@ -87,7 +77,7 @@ public IAsyncEnumerable GetActiveProcesses(IEnumerable p .AsAsyncEnumerable(); public IAsyncEnumerable<(Guid ProcessStepId, ProcessStepTypeId ProcessStepTypeId)> GetProcessStepData(Guid processId) => - _context.ProcessSteps + dbContext.ProcessSteps .AsNoTracking() .Where(step => step.ProcessId == processId && diff --git a/src/database/Dim.DbAccess/Repositories/TenantRepository.cs b/src/database/Dim.DbAccess/Repositories/TenantRepository.cs index 1ff7088..576902b 100644 --- a/src/database/Dim.DbAccess/Repositories/TenantRepository.cs +++ b/src/database/Dim.DbAccess/Repositories/TenantRepository.cs @@ -18,21 +18,21 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ +using Dim.DbAccess.Models; using Dim.Entities; using Dim.Entities.Entities; using Microsoft.EntityFrameworkCore; -using System; -using System.Threading.Tasks; namespace Dim.DbAccess.Repositories; -public class TenantRepository(DimDbContext context) : ITenantRepository +public class TenantRepository(DimDbContext dbContext) + : ITenantRepository { public Tenant CreateTenant(string companyName, string bpn, string didDocumentLocation, bool isIssuer, Guid processId, Guid operatorId) => - context.Tenants.Add(new Tenant(Guid.NewGuid(), companyName, bpn, didDocumentLocation, isIssuer, processId, operatorId)).Entity; + dbContext.Tenants.Add(new Tenant(Guid.NewGuid(), companyName, bpn, didDocumentLocation, isIssuer, processId, operatorId)).Entity; public Task<(bool Exists, Guid TenantId, string CompanyName, string Bpn)> GetTenantDataForProcessId(Guid processId) => - context.Tenants + dbContext.Tenants .Where(x => x.ProcessId == processId) .Select(x => new ValueTuple(true, x.Id, x.CompanyName, x.Bpn)) .SingleOrDefaultAsync(); @@ -41,136 +41,148 @@ public void AttachAndModifyTenant(Guid tenantId, Action? initialize, Act { var tenant = new Tenant(tenantId, null!, null!, null!, default, Guid.Empty, Guid.Empty); initialize?.Invoke(tenant); - context.Tenants.Attach(tenant); + dbContext.Tenants.Attach(tenant); modify(tenant); } - public Task GetSubAccountIdByTenantId(Guid tenantId) - => context.Tenants + public Task<(bool IsIssuer, string? HostingUrl)> GetHostingUrlAndIsIssuer(Guid tenantId) + => dbContext.Tenants .Where(x => x.Id == tenantId) - .Select(x => x.SubAccountId) - .SingleOrDefaultAsync(); - - public Task<(Guid? SubAccountId, string? ServiceInstanceId)> GetSubAccountAndServiceInstanceIdsByTenantId(Guid tenantId) - => context.Tenants - .Where(x => x.Id == tenantId) - .Select(x => new ValueTuple(x.SubAccountId, x.ServiceInstanceId)) - .SingleOrDefaultAsync(); - - public Task<(Guid? SubAccountId, string? ServiceBindingName)> GetSubAccountIdAndServiceBindingNameByTenantId(Guid tenantId) - => context.Tenants - .Where(x => x.Id == tenantId) - .Select(x => new ValueTuple(x.SubAccountId, x.ServiceBindingName)) - .SingleOrDefaultAsync(); - - public Task GetSpaceId(Guid tenantId) - => context.Tenants - .Where(x => x.Id == tenantId) - .Select(x => x.SpaceId) - .SingleOrDefaultAsync(); - - public Task GetDimInstanceId(Guid tenantId) - => context.Tenants - .Where(x => x.Id == tenantId) - .Select(x => x.DimInstanceId) - .SingleOrDefaultAsync(); - - public Task<(string bpn, string? DownloadUrl, string? Did, Guid? DimInstanceId)> GetCallbackData(Guid tenantId) - => context.Tenants - .Where(x => x.Id == tenantId) - .Select(x => new ValueTuple(x.Bpn, x.DidDownloadUrl, x.Did, x.DimInstanceId)) - .SingleOrDefaultAsync(); - - public Task<(Guid? DimInstanceId, string HostingUrl, bool IsIssuer)> GetDimInstanceIdAndHostingUrl(Guid tenantId) - => context.Tenants - .Where(x => x.Id == tenantId) - .Select(x => new ValueTuple(x.DimInstanceId, x.DidDocumentLocation, x.IsIssuer)) - .SingleOrDefaultAsync(); - - public Task<(string? ApplicationId, Guid? CompanyId, Guid? DimInstanceId, bool IsIssuer)> GetApplicationAndCompanyId(Guid tenantId) => - context.Tenants - .Where(x => x.Id == tenantId) - .Select(x => new ValueTuple( - x.ApplicationId, - x.CompanyId, - x.DimInstanceId, - x.IsIssuer)) - .SingleOrDefaultAsync(); - - public Task<(bool Exists, Guid? CompanyId, Guid? InstanceId)> GetCompanyAndInstanceIdForBpn(string bpn) => - context.Tenants.Where(x => x.Bpn == bpn) - .Select(x => new ValueTuple(true, x.CompanyId, x.DimInstanceId)) + .Select(x => new ValueTuple(x.IsIssuer, x.DidDocumentLocation)) .SingleOrDefaultAsync(); public void CreateTenantTechnicalUser(Guid tenantId, string technicalUserName, Guid externalId, Guid processId) => - context.TechnicalUsers.Add(new TechnicalUser(Guid.NewGuid(), tenantId, externalId, technicalUserName, processId)); + dbContext.TechnicalUsers.Add(new TechnicalUser(Guid.NewGuid(), tenantId, externalId, technicalUserName, processId)); public void AttachAndModifyTechnicalUser(Guid technicalUserId, Action? initialize, Action modify) { var technicalUser = new TechnicalUser(technicalUserId, Guid.Empty, Guid.Empty, null!, Guid.Empty); initialize?.Invoke(technicalUser); - context.TechnicalUsers.Attach(technicalUser); + dbContext.TechnicalUsers.Attach(technicalUser); modify(technicalUser); } public Task<(bool Exists, Guid TenantId)> GetTenantForBpn(string bpn) => - context.Tenants.Where(x => x.Bpn == bpn) + dbContext.Tenants.Where(x => x.Bpn == bpn) .Select(x => new ValueTuple(true, x.Id)) .SingleOrDefaultAsync(); public Task<(bool Exists, Guid TechnicalUserId, string CompanyName, string Bpn)> GetTenantDataForTechnicalUserProcessId(Guid processId) => - context.TechnicalUsers + dbContext.TechnicalUsers .Where(x => x.ProcessId == processId) .Select(x => new ValueTuple(true, x.Id, x.Tenant!.CompanyName, x.Tenant.Bpn)) .SingleOrDefaultAsync(); - public Task<(Guid? spaceId, string technicalUserName)> GetSpaceIdAndTechnicalUserName(Guid technicalUserId) => - context.TechnicalUsers - .Where(x => x.Id == technicalUserId) - .Select(x => new ValueTuple(x.Tenant!.SpaceId, x.TechnicalUserName)) - .SingleOrDefaultAsync(); - - public Task<(Guid ExternalId, string? TokenAddress, string? ClientId, byte[]? ClientSecret, byte[]? InitializationVector, int? EncryptionMode)> GetTechnicalUserCallbackData(Guid technicalUserId) => - context.TechnicalUsers + public Task<(Guid ExternalId, WalletData WalletData)> GetTechnicalUserCallbackData(Guid technicalUserId) => + dbContext.TechnicalUsers .Where(x => x.Id == technicalUserId) - .Select(x => new ValueTuple( + .Select(x => new ValueTuple( x.ExternalId, - x.TokenAddress, - x.ClientId, - x.ClientSecret, - x.InitializationVector, - x.EncryptionMode)) - .SingleOrDefaultAsync(); - - public Task<(Guid? DimInstanceId, Guid? CompanyId)> GetDimInstanceIdAndDid(Guid tenantId) => - context.Tenants - .Where(x => x.Id == tenantId) - .Select(x => new ValueTuple(x.DimInstanceId, x.CompanyId)) + new WalletData( + x.TokenAddress, + x.ClientId, + x.ClientSecret, + x.InitializationVector, + x.EncryptionMode))) .SingleOrDefaultAsync(); public Task<(bool Exists, Guid TechnicalUserId, Guid ProcessId)> GetTechnicalUserForBpn(string bpn, string technicalUserName) => - context.TechnicalUsers + dbContext.TechnicalUsers .Where(x => x.TechnicalUserName == technicalUserName && x.Tenant!.Bpn == bpn) .Select(x => new ValueTuple(true, x.Id, x.ProcessId)) .SingleOrDefaultAsync(); public Task GetExternalIdForTechnicalUser(Guid technicalUserId) => - context.TechnicalUsers + dbContext.TechnicalUsers .Where(x => x.Id == technicalUserId) .Select(x => x.ExternalId) .SingleOrDefaultAsync(); public void RemoveTechnicalUser(Guid technicalUserId) => - context.TechnicalUsers + dbContext.TechnicalUsers .Remove(new TechnicalUser(technicalUserId, Guid.Empty, Guid.Empty, null!, Guid.Empty)); public Task IsTenantExisting(string companyName, string bpn) => - context.Tenants + dbContext.Tenants .AnyAsync(x => x.CompanyName == companyName && x.Bpn == bpn); public Task GetTenantBpn(Guid tenantId) => - context.Tenants + dbContext.Tenants .Where(x => x.Id == tenantId) .Select(x => x.Bpn) .SingleAsync(); + + public Task GetOperationId(Guid tenantId) => + dbContext.Tenants + .Where(x => x.Id == tenantId) + .Select(x => x.OperationId) + .SingleOrDefaultAsync(); + + public Task<(string? BaseUrl, WalletData WalletData)> GetCompanyRequestData(Guid tenantId) => + dbContext.Tenants + .Where(x => x.Id == tenantId) + .Select(x => new ValueTuple( + x.BaseUrl, + new WalletData( + x.TokenAddress, + x.ClientId, + x.ClientSecret, + x.InitializationVector, + x.EncryptionMode + ))) + .SingleOrDefaultAsync(); + + public Task<(bool Exists, Guid? CompanyId, string? BaseUrl, WalletData WalletData)> GetCompanyAndWalletDataForBpn(string bpn) => + dbContext.Tenants + .Where(x => x.Bpn == bpn) + .Select(x => new ValueTuple( + true, + x.CompanyId, + x.BaseUrl, + new WalletData( + x.TokenAddress, + x.ClientId, + x.ClientSecret, + x.InitializationVector, + x.EncryptionMode + ))) + .SingleOrDefaultAsync(); + + public Task<(Guid? CompanyId, string? BaseUrl, WalletData WalletData)> GetStatusListCreationData(Guid tenantId) => + dbContext.Tenants + .Where(x => x.Id == tenantId) + .Select(x => new ValueTuple( + x.CompanyId, + x.BaseUrl, + new WalletData( + x.TokenAddress, + x.ClientId, + x.ClientSecret, + x.InitializationVector, + x.EncryptionMode + ))) + .SingleOrDefaultAsync(); + + public Task<(string Bpn, string? BaseUrl, WalletData WalletData, string? Did, string? DownloadUrl)> GetCallbackData(Guid tenantId) => + dbContext.Tenants + .Where(x => x.Id == tenantId) + .Select(x => new ValueTuple( + x.Bpn, + x.BaseUrl, + new WalletData( + x.TokenAddress, + x.ClientId, + x.ClientSecret, + x.InitializationVector, + x.EncryptionMode + ), + x.Did, + x.DidDownloadUrl)) + .SingleOrDefaultAsync(); + + public Task<(string? DownloadUrl, bool IsIssuer)> GetDownloadUrlAndIsIssuer(Guid tenantId) => + dbContext.Tenants + .Where(x => x.Id == tenantId) + .Select(x => new ValueTuple(x.DidDownloadUrl, x.IsIssuer)) + .SingleOrDefaultAsync(); } diff --git a/src/database/Dim.Entities/Entities/ProcessStep.cs b/src/database/Dim.Entities/Entities/ProcessStep.cs index 09b1dd4..513d5ad 100644 --- a/src/database/Dim.Entities/Entities/ProcessStep.cs +++ b/src/database/Dim.Entities/Entities/ProcessStep.cs @@ -22,26 +22,22 @@ namespace Dim.Entities.Entities; -public class ProcessStep +public class ProcessStep( + Guid id, + ProcessStepTypeId processStepTypeId, + ProcessStepStatusId processStepStatusId, + Guid processId, + DateTimeOffset dateCreated) { - public ProcessStep(Guid id, ProcessStepTypeId processStepTypeId, ProcessStepStatusId processStepStatusId, Guid processId, DateTimeOffset dateCreated) - { - Id = id; - ProcessStepTypeId = processStepTypeId; - ProcessStepStatusId = processStepStatusId; - ProcessId = processId; - DateCreated = dateCreated; - } + public Guid Id { get; private set; } = id; - public Guid Id { get; private set; } + public ProcessStepTypeId ProcessStepTypeId { get; private set; } = processStepTypeId; - public ProcessStepTypeId ProcessStepTypeId { get; private set; } + public ProcessStepStatusId ProcessStepStatusId { get; set; } = processStepStatusId; - public ProcessStepStatusId ProcessStepStatusId { get; set; } + public Guid ProcessId { get; private set; } = processId; - public Guid ProcessId { get; private set; } - - public DateTimeOffset DateCreated { get; private set; } + public DateTimeOffset DateCreated { get; private set; } = dateCreated; public DateTimeOffset? DateLastChanged { get; set; } diff --git a/src/database/Dim.Entities/Entities/Tenant.cs b/src/database/Dim.Entities/Entities/Tenant.cs index 03053ec..d3f354e 100644 --- a/src/database/Dim.Entities/Entities/Tenant.cs +++ b/src/database/Dim.Entities/Entities/Tenant.cs @@ -32,28 +32,20 @@ public class Tenant( public Guid Id { get; set; } = id; public string CompanyName { get; set; } = companyName; public string Bpn { get; set; } = bpn; - public string DidDocumentLocation { get; set; } = didDocumentLocation; - public bool IsIssuer { get; set; } = isIssuer; - + public Guid OperatorId { get; set; } = operatorId; public Guid ProcessId { get; set; } = processId; - - public Guid? SubAccountId { get; set; } - - public string? ServiceInstanceId { get; set; } - - public string? ServiceBindingName { get; set; } - - public Guid? SpaceId { get; set; } - - public Guid? DimInstanceId { get; set; } + public Guid? OperationId { get; set; } + public string? TokenAddress { get; set; } + public string? BaseUrl { get; set; } + public string? ClientId { get; set; } + public byte[]? ClientSecret { get; set; } + public byte[]? InitializationVector { get; set; } + public int? EncryptionMode { get; set; } + public Guid? CompanyId { get; set; } public string? DidDownloadUrl { get; set; } public string? Did { get; set; } - public string? ApplicationId { get; set; } - public Guid? CompanyId { get; set; } - public string? ApplicationKey { get; set; } - public Guid OperatorId { get; set; } = operatorId; public virtual Process? Process { get; set; } public virtual ICollection TechnicalUsers { get; private set; } = new HashSet(); } diff --git a/src/database/Dim.Entities/Enums/ProcessStepTypeId.cs b/src/database/Dim.Entities/Enums/ProcessStepTypeId.cs index c11b7de..651aa4a 100644 --- a/src/database/Dim.Entities/Enums/ProcessStepTypeId.cs +++ b/src/database/Dim.Entities/Enums/ProcessStepTypeId.cs @@ -23,24 +23,12 @@ namespace Dim.Entities.Enums; public enum ProcessStepTypeId { // Setup Dim Process - CREATE_SUBACCOUNT = 1, - CREATE_SERVICEMANAGER_BINDINGS = 2, - ASSIGN_ENTITLEMENTS = 3, - CREATE_SERVICE_INSTANCE = 4, - CREATE_SERVICE_BINDING = 5, - SUBSCRIBE_APPLICATION = 6, - CREATE_CLOUD_FOUNDRY_ENVIRONMENT = 7, - CREATE_CLOUD_FOUNDRY_SPACE = 8, - ADD_SPACE_MANAGER_ROLE = 9, - ADD_SPACE_DEVELOPER_ROLE = 10, - CREATE_DIM_SERVICE_INSTANCE = 11, - CREATE_SERVICE_INSTANCE_BINDING = 12, - GET_DIM_DETAILS = 13, - CREATE_APPLICATION = 14, - CREATE_COMPANY_IDENTITY = 15, - ASSIGN_COMPANY_APPLICATION = 16, - CREATE_STATUS_LIST = 17, - SEND_CALLBACK = 18, + CREATE_WALLET = 1, + CHECK_OPERATION = 2, + GET_COMPANY = 3, + GET_DID_DOCUMENT = 4, + CREATE_STATUS_LIST = 5, + SEND_CALLBACK = 6, // Create Technical User CREATE_TECHNICAL_USER = 100, diff --git a/src/database/Dim.Migrations/Migrations/20240912045058_AddDivProvisioning.Designer.cs b/src/database/Dim.Migrations/Migrations/20240912045058_AddDivProvisioning.Designer.cs new file mode 100644 index 0000000..919dafe --- /dev/null +++ b/src/database/Dim.Migrations/Migrations/20240912045058_AddDivProvisioning.Designer.cs @@ -0,0 +1,515 @@ +/******************************************************************************** + * Copyright (c) 2024 BMW Group AG + * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +// +using System; +using Dim.Entities; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Dim.Migrations.Migrations +{ + [DbContext(typeof(DimDbContext))] + [Migration("20240912045058_AddDivProvisioning")] + partial class AddDivProvisioning + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("dim") + .UseCollation("en_US.utf8") + .HasAnnotation("ProductVersion", "8.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Dim.Entities.Entities.Process", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("LockExpiryDate") + .HasColumnType("timestamp with time zone") + .HasColumnName("lock_expiry_date"); + + b.Property("ProcessTypeId") + .HasColumnType("integer") + .HasColumnName("process_type_id"); + + b.Property("Version") + .IsConcurrencyToken() + .HasColumnType("uuid") + .HasColumnName("version"); + + b.HasKey("Id") + .HasName("pk_processes"); + + b.HasIndex("ProcessTypeId") + .HasDatabaseName("ix_processes_process_type_id"); + + b.ToTable("processes", "dim"); + }); + + modelBuilder.Entity("Dim.Entities.Entities.ProcessStep", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_created"); + + b.Property("DateLastChanged") + .HasColumnType("timestamp with time zone") + .HasColumnName("date_last_changed"); + + b.Property("Message") + .HasColumnType("text") + .HasColumnName("message"); + + b.Property("ProcessId") + .HasColumnType("uuid") + .HasColumnName("process_id"); + + b.Property("ProcessStepStatusId") + .HasColumnType("integer") + .HasColumnName("process_step_status_id"); + + b.Property("ProcessStepTypeId") + .HasColumnType("integer") + .HasColumnName("process_step_type_id"); + + b.HasKey("Id") + .HasName("pk_process_steps"); + + b.HasIndex("ProcessId") + .HasDatabaseName("ix_process_steps_process_id"); + + b.HasIndex("ProcessStepStatusId") + .HasDatabaseName("ix_process_steps_process_step_status_id"); + + b.HasIndex("ProcessStepTypeId") + .HasDatabaseName("ix_process_steps_process_step_type_id"); + + b.ToTable("process_steps", "dim"); + }); + + modelBuilder.Entity("Dim.Entities.Entities.ProcessStepStatus", b => + { + b.Property("Id") + .HasColumnType("integer") + .HasColumnName("id"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("label"); + + b.HasKey("Id") + .HasName("pk_process_step_statuses"); + + b.ToTable("process_step_statuses", "dim"); + + b.HasData( + new + { + Id = 1, + Label = "TODO" + }, + new + { + Id = 2, + Label = "DONE" + }, + new + { + Id = 3, + Label = "SKIPPED" + }, + new + { + Id = 4, + Label = "FAILED" + }, + new + { + Id = 5, + Label = "DUPLICATE" + }); + }); + + modelBuilder.Entity("Dim.Entities.Entities.ProcessStepType", b => + { + b.Property("Id") + .HasColumnType("integer") + .HasColumnName("id"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("label"); + + b.HasKey("Id") + .HasName("pk_process_step_types"); + + b.ToTable("process_step_types", "dim"); + + b.HasData( + new + { + Id = 1, + Label = "CREATE_WALLET" + }, + new + { + Id = 2, + Label = "CHECK_OPERATION" + }, + new + { + Id = 3, + Label = "GET_COMPANY" + }, + new + { + Id = 4, + Label = "GET_DID_DOCUMENT" + }, + new + { + Id = 5, + Label = "CREATE_STATUS_LIST" + }, + new + { + Id = 6, + Label = "SEND_CALLBACK" + }, + new + { + Id = 100, + Label = "CREATE_TECHNICAL_USER" + }, + new + { + Id = 101, + Label = "GET_TECHNICAL_USER_DATA" + }, + new + { + Id = 102, + Label = "SEND_TECHNICAL_USER_CREATION_CALLBACK" + }, + new + { + Id = 200, + Label = "DELETE_TECHNICAL_USER" + }, + new + { + Id = 201, + Label = "SEND_TECHNICAL_USER_DELETION_CALLBACK" + }); + }); + + modelBuilder.Entity("Dim.Entities.Entities.ProcessType", b => + { + b.Property("Id") + .HasColumnType("integer") + .HasColumnName("id"); + + b.Property("Label") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("label"); + + b.HasKey("Id") + .HasName("pk_process_types"); + + b.ToTable("process_types", "dim"); + + b.HasData( + new + { + Id = 1, + Label = "SETUP_DIM" + }, + new + { + Id = 2, + Label = "TECHNICAL_USER" + }); + }); + + modelBuilder.Entity("Dim.Entities.Entities.TechnicalUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("ClientId") + .HasColumnType("text") + .HasColumnName("client_id"); + + b.Property("ClientSecret") + .HasColumnType("bytea") + .HasColumnName("client_secret"); + + b.Property("EncryptionMode") + .HasColumnType("integer") + .HasColumnName("encryption_mode"); + + b.Property("ExternalId") + .HasColumnType("uuid") + .HasColumnName("external_id"); + + b.Property("InitializationVector") + .HasColumnType("bytea") + .HasColumnName("initialization_vector"); + + b.Property("ProcessId") + .HasColumnType("uuid") + .HasColumnName("process_id"); + + b.Property("TechnicalUserName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("technical_user_name"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("tenant_id"); + + b.Property("TokenAddress") + .HasColumnType("text") + .HasColumnName("token_address"); + + b.HasKey("Id") + .HasName("pk_technical_users"); + + b.HasIndex("ProcessId") + .HasDatabaseName("ix_technical_users_process_id"); + + b.HasIndex("TenantId") + .HasDatabaseName("ix_technical_users_tenant_id"); + + b.ToTable("technical_users", "dim"); + }); + + modelBuilder.Entity("Dim.Entities.Entities.Tenant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("BaseUrl") + .HasColumnType("text") + .HasColumnName("base_url"); + + b.Property("Bpn") + .IsRequired() + .HasColumnType("text") + .HasColumnName("bpn"); + + b.Property("ClientId") + .HasColumnType("text") + .HasColumnName("client_id"); + + b.Property("ClientSecret") + .HasColumnType("bytea") + .HasColumnName("client_secret"); + + b.Property("CompanyId") + .HasColumnType("uuid") + .HasColumnName("company_id"); + + b.Property("CompanyName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("company_name"); + + b.Property("Did") + .HasColumnType("text") + .HasColumnName("did"); + + b.Property("DidDocumentLocation") + .IsRequired() + .HasColumnType("text") + .HasColumnName("did_document_location"); + + b.Property("DidDownloadUrl") + .HasColumnType("text") + .HasColumnName("did_download_url"); + + b.Property("EncryptionMode") + .HasColumnType("integer") + .HasColumnName("encryption_mode"); + + b.Property("InitializationVector") + .HasColumnType("bytea") + .HasColumnName("initialization_vector"); + + b.Property("IsIssuer") + .HasColumnType("boolean") + .HasColumnName("is_issuer"); + + b.Property("OperationId") + .HasColumnType("uuid") + .HasColumnName("operation_id"); + + b.Property("OperatorId") + .HasColumnType("uuid") + .HasColumnName("operator_id"); + + b.Property("ProcessId") + .HasColumnType("uuid") + .HasColumnName("process_id"); + + b.Property("TokenAddress") + .HasColumnType("text") + .HasColumnName("token_address"); + + b.HasKey("Id") + .HasName("pk_tenants"); + + b.HasIndex("ProcessId") + .HasDatabaseName("ix_tenants_process_id"); + + b.ToTable("tenants", "dim"); + }); + + modelBuilder.Entity("Dim.Entities.Entities.Process", b => + { + b.HasOne("Dim.Entities.Entities.ProcessType", "ProcessType") + .WithMany("Processes") + .HasForeignKey("ProcessTypeId") + .IsRequired() + .HasConstraintName("fk_processes_process_types_process_type_id"); + + b.Navigation("ProcessType"); + }); + + modelBuilder.Entity("Dim.Entities.Entities.ProcessStep", b => + { + b.HasOne("Dim.Entities.Entities.Process", "Process") + .WithMany("ProcessSteps") + .HasForeignKey("ProcessId") + .IsRequired() + .HasConstraintName("fk_process_steps_processes_process_id"); + + b.HasOne("Dim.Entities.Entities.ProcessStepStatus", "ProcessStepStatus") + .WithMany("ProcessSteps") + .HasForeignKey("ProcessStepStatusId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_process_steps_process_step_statuses_process_step_status_id"); + + b.HasOne("Dim.Entities.Entities.ProcessStepType", "ProcessStepType") + .WithMany("ProcessSteps") + .HasForeignKey("ProcessStepTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_process_steps_process_step_types_process_step_type_id"); + + b.Navigation("Process"); + + b.Navigation("ProcessStepStatus"); + + b.Navigation("ProcessStepType"); + }); + + modelBuilder.Entity("Dim.Entities.Entities.TechnicalUser", b => + { + b.HasOne("Dim.Entities.Entities.Process", "Process") + .WithMany("TechnicalUsers") + .HasForeignKey("ProcessId") + .IsRequired() + .HasConstraintName("fk_technical_users_processes_process_id"); + + b.HasOne("Dim.Entities.Entities.Tenant", "Tenant") + .WithMany("TechnicalUsers") + .HasForeignKey("TenantId") + .IsRequired() + .HasConstraintName("fk_technical_users_tenants_tenant_id"); + + b.Navigation("Process"); + + b.Navigation("Tenant"); + }); + + modelBuilder.Entity("Dim.Entities.Entities.Tenant", b => + { + b.HasOne("Dim.Entities.Entities.Process", "Process") + .WithMany("Tenants") + .HasForeignKey("ProcessId") + .IsRequired() + .HasConstraintName("fk_tenants_processes_process_id"); + + b.Navigation("Process"); + }); + + modelBuilder.Entity("Dim.Entities.Entities.Process", b => + { + b.Navigation("ProcessSteps"); + + b.Navigation("TechnicalUsers"); + + b.Navigation("Tenants"); + }); + + modelBuilder.Entity("Dim.Entities.Entities.ProcessStepStatus", b => + { + b.Navigation("ProcessSteps"); + }); + + modelBuilder.Entity("Dim.Entities.Entities.ProcessStepType", b => + { + b.Navigation("ProcessSteps"); + }); + + modelBuilder.Entity("Dim.Entities.Entities.ProcessType", b => + { + b.Navigation("Processes"); + }); + + modelBuilder.Entity("Dim.Entities.Entities.Tenant", b => + { + b.Navigation("TechnicalUsers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/database/Dim.Migrations/Migrations/20240912045058_AddDivProvisioning.cs b/src/database/Dim.Migrations/Migrations/20240912045058_AddDivProvisioning.cs new file mode 100644 index 0000000..24f483e --- /dev/null +++ b/src/database/Dim.Migrations/Migrations/20240912045058_AddDivProvisioning.cs @@ -0,0 +1,349 @@ +/******************************************************************************** + * Copyright (c) 2024 BMW Group AG + * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +using Microsoft.EntityFrameworkCore.Migrations; +using System; + +#nullable disable + +#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional + +namespace Dim.Migrations.Migrations +{ + /// + public partial class AddDivProvisioning : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DeleteData( + schema: "dim", + table: "process_step_types", + keyColumn: "id", + keyValue: 7); + + migrationBuilder.DeleteData( + schema: "dim", + table: "process_step_types", + keyColumn: "id", + keyValue: 8); + + migrationBuilder.DeleteData( + schema: "dim", + table: "process_step_types", + keyColumn: "id", + keyValue: 9); + + migrationBuilder.DeleteData( + schema: "dim", + table: "process_step_types", + keyColumn: "id", + keyValue: 10); + + migrationBuilder.DeleteData( + schema: "dim", + table: "process_step_types", + keyColumn: "id", + keyValue: 11); + + migrationBuilder.DeleteData( + schema: "dim", + table: "process_step_types", + keyColumn: "id", + keyValue: 12); + + migrationBuilder.DeleteData( + schema: "dim", + table: "process_step_types", + keyColumn: "id", + keyValue: 13); + + migrationBuilder.DeleteData( + schema: "dim", + table: "process_step_types", + keyColumn: "id", + keyValue: 14); + + migrationBuilder.DeleteData( + schema: "dim", + table: "process_step_types", + keyColumn: "id", + keyValue: 15); + + migrationBuilder.DeleteData( + schema: "dim", + table: "process_step_types", + keyColumn: "id", + keyValue: 16); + + migrationBuilder.DeleteData( + schema: "dim", + table: "process_step_types", + keyColumn: "id", + keyValue: 17); + + migrationBuilder.DeleteData( + schema: "dim", + table: "process_step_types", + keyColumn: "id", + keyValue: 18); + + migrationBuilder.DropColumn( + name: "application_id", + schema: "dim", + table: "tenants"); + + migrationBuilder.DropColumn( + name: "dim_instance_id", + schema: "dim", + table: "tenants"); + + migrationBuilder.DropColumn( + name: "space_id", + schema: "dim", + table: "tenants"); + + migrationBuilder.RenameColumn( + name: "sub_account_id", + schema: "dim", + table: "tenants", + newName: "operation_id"); + + migrationBuilder.RenameColumn( + name: "service_instance_id", + schema: "dim", + table: "tenants", + newName: "token_address"); + + migrationBuilder.RenameColumn( + name: "service_binding_name", + schema: "dim", + table: "tenants", + newName: "client_id"); + + migrationBuilder.RenameColumn( + name: "application_key", + schema: "dim", + table: "tenants", + newName: "base_url"); + + migrationBuilder.AddColumn( + name: "client_secret", + schema: "dim", + table: "tenants", + type: "bytea", + nullable: true); + + migrationBuilder.AddColumn( + name: "encryption_mode", + schema: "dim", + table: "tenants", + type: "integer", + nullable: true); + + migrationBuilder.AddColumn( + name: "initialization_vector", + schema: "dim", + table: "tenants", + type: "bytea", + nullable: true); + + migrationBuilder.UpdateData( + schema: "dim", + table: "process_step_types", + keyColumn: "id", + keyValue: 1, + column: "label", + value: "CREATE_WALLET"); + + migrationBuilder.UpdateData( + schema: "dim", + table: "process_step_types", + keyColumn: "id", + keyValue: 2, + column: "label", + value: "CHECK_OPERATION"); + + migrationBuilder.UpdateData( + schema: "dim", + table: "process_step_types", + keyColumn: "id", + keyValue: 3, + column: "label", + value: "GET_COMPANY"); + + migrationBuilder.UpdateData( + schema: "dim", + table: "process_step_types", + keyColumn: "id", + keyValue: 4, + column: "label", + value: "GET_DID_DOCUMENT"); + + migrationBuilder.UpdateData( + schema: "dim", + table: "process_step_types", + keyColumn: "id", + keyValue: 5, + column: "label", + value: "CREATE_STATUS_LIST"); + + migrationBuilder.UpdateData( + schema: "dim", + table: "process_step_types", + keyColumn: "id", + keyValue: 6, + column: "label", + value: "SEND_CALLBACK"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "client_secret", + schema: "dim", + table: "tenants"); + + migrationBuilder.DropColumn( + name: "encryption_mode", + schema: "dim", + table: "tenants"); + + migrationBuilder.DropColumn( + name: "initialization_vector", + schema: "dim", + table: "tenants"); + + migrationBuilder.RenameColumn( + name: "token_address", + schema: "dim", + table: "tenants", + newName: "service_instance_id"); + + migrationBuilder.RenameColumn( + name: "operation_id", + schema: "dim", + table: "tenants", + newName: "sub_account_id"); + + migrationBuilder.RenameColumn( + name: "client_id", + schema: "dim", + table: "tenants", + newName: "service_binding_name"); + + migrationBuilder.RenameColumn( + name: "base_url", + schema: "dim", + table: "tenants", + newName: "application_key"); + + migrationBuilder.AddColumn( + name: "application_id", + schema: "dim", + table: "tenants", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "dim_instance_id", + schema: "dim", + table: "tenants", + type: "uuid", + nullable: true); + + migrationBuilder.AddColumn( + name: "space_id", + schema: "dim", + table: "tenants", + type: "uuid", + nullable: true); + + migrationBuilder.UpdateData( + schema: "dim", + table: "process_step_types", + keyColumn: "id", + keyValue: 1, + column: "label", + value: "CREATE_SUBACCOUNT"); + + migrationBuilder.UpdateData( + schema: "dim", + table: "process_step_types", + keyColumn: "id", + keyValue: 2, + column: "label", + value: "CREATE_SERVICEMANAGER_BINDINGS"); + + migrationBuilder.UpdateData( + schema: "dim", + table: "process_step_types", + keyColumn: "id", + keyValue: 3, + column: "label", + value: "ASSIGN_ENTITLEMENTS"); + + migrationBuilder.UpdateData( + schema: "dim", + table: "process_step_types", + keyColumn: "id", + keyValue: 4, + column: "label", + value: "CREATE_SERVICE_INSTANCE"); + + migrationBuilder.UpdateData( + schema: "dim", + table: "process_step_types", + keyColumn: "id", + keyValue: 5, + column: "label", + value: "CREATE_SERVICE_BINDING"); + + migrationBuilder.UpdateData( + schema: "dim", + table: "process_step_types", + keyColumn: "id", + keyValue: 6, + column: "label", + value: "SUBSCRIBE_APPLICATION"); + + migrationBuilder.InsertData( + schema: "dim", + table: "process_step_types", + columns: new[] { "id", "label" }, + values: new object[,] + { + { 7, "CREATE_CLOUD_FOUNDRY_ENVIRONMENT" }, + { 8, "CREATE_CLOUD_FOUNDRY_SPACE" }, + { 9, "ADD_SPACE_MANAGER_ROLE" }, + { 10, "ADD_SPACE_DEVELOPER_ROLE" }, + { 11, "CREATE_DIM_SERVICE_INSTANCE" }, + { 12, "CREATE_SERVICE_INSTANCE_BINDING" }, + { 13, "GET_DIM_DETAILS" }, + { 14, "CREATE_APPLICATION" }, + { 15, "CREATE_COMPANY_IDENTITY" }, + { 16, "ASSIGN_COMPANY_APPLICATION" }, + { 17, "CREATE_STATUS_LIST" }, + { 18, "SEND_CALLBACK" } + }); + } + } +} diff --git a/src/database/Dim.Migrations/Migrations/DimDbContextModelSnapshot.cs b/src/database/Dim.Migrations/Migrations/DimDbContextModelSnapshot.cs index e3b42c2..f86b696 100644 --- a/src/database/Dim.Migrations/Migrations/DimDbContextModelSnapshot.cs +++ b/src/database/Dim.Migrations/Migrations/DimDbContextModelSnapshot.cs @@ -35,7 +35,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder .HasDefaultSchema("dim") .UseCollation("en_US.utf8") - .HasAnnotation("ProductVersion", "8.0.5") + .HasAnnotation("ProductVersion", "8.0.7") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -181,91 +181,31 @@ protected override void BuildModel(ModelBuilder modelBuilder) new { Id = 1, - Label = "CREATE_SUBACCOUNT" + Label = "CREATE_WALLET" }, new { Id = 2, - Label = "CREATE_SERVICEMANAGER_BINDINGS" + Label = "CHECK_OPERATION" }, new { Id = 3, - Label = "ASSIGN_ENTITLEMENTS" + Label = "GET_COMPANY" }, new { Id = 4, - Label = "CREATE_SERVICE_INSTANCE" + Label = "GET_DID_DOCUMENT" }, new { Id = 5, - Label = "CREATE_SERVICE_BINDING" - }, - new - { - Id = 6, - Label = "SUBSCRIBE_APPLICATION" - }, - new - { - Id = 7, - Label = "CREATE_CLOUD_FOUNDRY_ENVIRONMENT" - }, - new - { - Id = 8, - Label = "CREATE_CLOUD_FOUNDRY_SPACE" - }, - new - { - Id = 9, - Label = "ADD_SPACE_MANAGER_ROLE" - }, - new - { - Id = 10, - Label = "ADD_SPACE_DEVELOPER_ROLE" - }, - new - { - Id = 11, - Label = "CREATE_DIM_SERVICE_INSTANCE" - }, - new - { - Id = 12, - Label = "CREATE_SERVICE_INSTANCE_BINDING" - }, - new - { - Id = 13, - Label = "GET_DIM_DETAILS" - }, - new - { - Id = 14, - Label = "CREATE_APPLICATION" - }, - new - { - Id = 15, - Label = "CREATE_COMPANY_IDENTITY" - }, - new - { - Id = 16, - Label = "ASSIGN_COMPANY_APPLICATION" - }, - new - { - Id = 17, Label = "CREATE_STATUS_LIST" }, new { - Id = 18, + Id = 6, Label = "SEND_CALLBACK" }, new @@ -388,19 +328,23 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid") .HasColumnName("id"); - b.Property("ApplicationId") + b.Property("BaseUrl") .HasColumnType("text") - .HasColumnName("application_id"); - - b.Property("ApplicationKey") - .HasColumnType("text") - .HasColumnName("application_key"); + .HasColumnName("base_url"); b.Property("Bpn") .IsRequired() .HasColumnType("text") .HasColumnName("bpn"); + b.Property("ClientId") + .HasColumnType("text") + .HasColumnName("client_id"); + + b.Property("ClientSecret") + .HasColumnType("bytea") + .HasColumnName("client_secret"); + b.Property("CompanyId") .HasColumnType("uuid") .HasColumnName("company_id"); @@ -423,14 +367,22 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("text") .HasColumnName("did_download_url"); - b.Property("DimInstanceId") - .HasColumnType("uuid") - .HasColumnName("dim_instance_id"); + b.Property("EncryptionMode") + .HasColumnType("integer") + .HasColumnName("encryption_mode"); + + b.Property("InitializationVector") + .HasColumnType("bytea") + .HasColumnName("initialization_vector"); b.Property("IsIssuer") .HasColumnType("boolean") .HasColumnName("is_issuer"); + b.Property("OperationId") + .HasColumnType("uuid") + .HasColumnName("operation_id"); + b.Property("OperatorId") .HasColumnType("uuid") .HasColumnName("operator_id"); @@ -439,21 +391,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid") .HasColumnName("process_id"); - b.Property("ServiceBindingName") - .HasColumnType("text") - .HasColumnName("service_binding_name"); - - b.Property("ServiceInstanceId") + b.Property("TokenAddress") .HasColumnType("text") - .HasColumnName("service_instance_id"); - - b.Property("SpaceId") - .HasColumnType("uuid") - .HasColumnName("space_id"); - - b.Property("SubAccountId") - .HasColumnType("uuid") - .HasColumnName("sub_account_id"); + .HasColumnName("token_address"); b.HasKey("Id") .HasName("pk_tenants"); diff --git a/src/database/Dim.Migrations/Program.cs b/src/database/Dim.Migrations/Program.cs index 8b257e6..5a661f9 100644 --- a/src/database/Dim.Migrations/Program.cs +++ b/src/database/Dim.Migrations/Program.cs @@ -49,7 +49,7 @@ } catch (Exception ex) when (!ex.GetType().Name.Equals("StopTheHostException", StringComparison.Ordinal)) { - Log.Fatal("Unhandled exception {Exception}", ex); + Log.Fatal(ex, "Unhandled exception {Exception}", ex); throw; } finally diff --git a/src/processes/DimProcess.Executor/DependencyInjection/DimProcessCollectionExtensions.cs b/src/processes/DimProcess.Executor/DependencyInjection/DimProcessCollectionExtensions.cs index 78be5c0..741d88c 100644 --- a/src/processes/DimProcess.Executor/DependencyInjection/DimProcessCollectionExtensions.cs +++ b/src/processes/DimProcess.Executor/DependencyInjection/DimProcessCollectionExtensions.cs @@ -21,7 +21,7 @@ using DimProcess.Library.DependencyInjection; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Org.Eclipse.TractusX.Portal.Backend.Processes.Worker.Library; +using Processes.Worker.Library; namespace DimProcess.Executor.DependencyInjection; diff --git a/src/processes/DimProcess.Executor/DimProcessTypeExecutor.cs b/src/processes/DimProcess.Executor/DimProcessTypeExecutor.cs index db9c5fd..6875779 100644 --- a/src/processes/DimProcess.Executor/DimProcessTypeExecutor.cs +++ b/src/processes/DimProcess.Executor/DimProcessTypeExecutor.cs @@ -23,7 +23,7 @@ using Dim.Entities.Enums; using DimProcess.Library; using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; -using Org.Eclipse.TractusX.Portal.Backend.Processes.Worker.Library; +using Processes.Worker.Library; using System.Collections.Immutable; namespace DimProcess.Executor; @@ -34,23 +34,10 @@ public class DimProcessTypeExecutor( : IProcessTypeExecutor { private readonly IEnumerable _executableProcessSteps = ImmutableArray.Create( - ProcessStepTypeId.CREATE_SUBACCOUNT, - ProcessStepTypeId.CREATE_SERVICEMANAGER_BINDINGS, - ProcessStepTypeId.ASSIGN_ENTITLEMENTS, - ProcessStepTypeId.CREATE_SERVICE_INSTANCE, - ProcessStepTypeId.CREATE_SERVICE_BINDING, - ProcessStepTypeId.SUBSCRIBE_APPLICATION, - ProcessStepTypeId.CREATE_CLOUD_FOUNDRY_ENVIRONMENT, - ProcessStepTypeId.CREATE_CLOUD_FOUNDRY_SPACE, - ProcessStepTypeId.ADD_SPACE_MANAGER_ROLE, - ProcessStepTypeId.ADD_SPACE_DEVELOPER_ROLE, - ProcessStepTypeId.CREATE_DIM_SERVICE_INSTANCE, - ProcessStepTypeId.CREATE_SERVICE_INSTANCE_BINDING, - ProcessStepTypeId.GET_DIM_DETAILS, - ProcessStepTypeId.CREATE_APPLICATION, - ProcessStepTypeId.CREATE_COMPANY_IDENTITY, - ProcessStepTypeId.CREATE_STATUS_LIST, - ProcessStepTypeId.ASSIGN_COMPANY_APPLICATION, + ProcessStepTypeId.CREATE_WALLET, + ProcessStepTypeId.CHECK_OPERATION, + ProcessStepTypeId.GET_COMPANY, + ProcessStepTypeId.GET_DID_DOCUMENT, ProcessStepTypeId.SEND_CALLBACK); private Guid _tenantId; @@ -70,7 +57,7 @@ public class DimProcessTypeExecutor( } _tenantId = tenantId; - _tenantName = $"{bpn}_{companyName}"; + _tenantName = $"{bpn}{companyName}"; return new IProcessTypeExecutor.InitializationResult(false, null); } @@ -90,37 +77,13 @@ public class DimProcessTypeExecutor( { (nextStepTypeIds, stepStatusId, modified, processMessage) = processStepTypeId switch { - ProcessStepTypeId.CREATE_SUBACCOUNT => await dimProcessHandler.CreateSubaccount(_tenantId, _tenantName, cancellationToken) + ProcessStepTypeId.CREATE_WALLET => await dimProcessHandler.CreateWallet(_tenantId, _tenantName, cancellationToken) .ConfigureAwait(false), - ProcessStepTypeId.CREATE_SERVICEMANAGER_BINDINGS => await dimProcessHandler.CreateServiceManagerBindings(_tenantId, cancellationToken) + ProcessStepTypeId.CHECK_OPERATION => await dimProcessHandler.CheckOperation(_tenantId, cancellationToken) .ConfigureAwait(false), - ProcessStepTypeId.ASSIGN_ENTITLEMENTS => await dimProcessHandler.AssignEntitlements(_tenantId, cancellationToken) + ProcessStepTypeId.GET_COMPANY => await dimProcessHandler.GetCompany(_tenantId, _tenantName, cancellationToken) .ConfigureAwait(false), - ProcessStepTypeId.CREATE_SERVICE_INSTANCE => await dimProcessHandler.CreateServiceInstance(_tenantId, cancellationToken) - .ConfigureAwait(false), - ProcessStepTypeId.CREATE_SERVICE_BINDING => await dimProcessHandler.CreateServiceBindings(_tenantId, cancellationToken) - .ConfigureAwait(false), - ProcessStepTypeId.SUBSCRIBE_APPLICATION => await dimProcessHandler.SubscribeApplication(_tenantId, cancellationToken) - .ConfigureAwait(false), - ProcessStepTypeId.CREATE_CLOUD_FOUNDRY_ENVIRONMENT => await dimProcessHandler.CreateCloudFoundryEnvironment(_tenantId, _tenantName, cancellationToken) - .ConfigureAwait(false), - ProcessStepTypeId.CREATE_CLOUD_FOUNDRY_SPACE => await dimProcessHandler.CreateCloudFoundrySpace(_tenantId, _tenantName, cancellationToken) - .ConfigureAwait(false), - ProcessStepTypeId.ADD_SPACE_MANAGER_ROLE => await dimProcessHandler.AddSpaceManagerRole(_tenantId, cancellationToken) - .ConfigureAwait(false), - ProcessStepTypeId.ADD_SPACE_DEVELOPER_ROLE => await dimProcessHandler.AddSpaceDeveloperRole(_tenantId, cancellationToken) - .ConfigureAwait(false), - ProcessStepTypeId.CREATE_DIM_SERVICE_INSTANCE => await dimProcessHandler.CreateDimServiceInstance(_tenantName, _tenantId, cancellationToken) - .ConfigureAwait(false), - ProcessStepTypeId.CREATE_SERVICE_INSTANCE_BINDING => await dimProcessHandler.CreateServiceInstanceBindings(_tenantName, _tenantId, cancellationToken) - .ConfigureAwait(false), - ProcessStepTypeId.GET_DIM_DETAILS => await dimProcessHandler.GetDimDetails(_tenantName, _tenantId, cancellationToken) - .ConfigureAwait(false), - ProcessStepTypeId.CREATE_APPLICATION => await dimProcessHandler.CreateApplication(_tenantName, _tenantId, cancellationToken) - .ConfigureAwait(false), - ProcessStepTypeId.CREATE_COMPANY_IDENTITY => await dimProcessHandler.CreateCompanyIdentity(_tenantId, cancellationToken) - .ConfigureAwait(false), - ProcessStepTypeId.ASSIGN_COMPANY_APPLICATION => await dimProcessHandler.AssignCompanyApplication(_tenantId, cancellationToken) + ProcessStepTypeId.GET_DID_DOCUMENT => await dimProcessHandler.GetDidDocument(_tenantId, cancellationToken) .ConfigureAwait(false), ProcessStepTypeId.CREATE_STATUS_LIST => await dimProcessHandler.CreateStatusList(_tenantId, cancellationToken) .ConfigureAwait(false), diff --git a/src/processes/DimProcess.Executor/TechnicalUserProcessTypeExecutor.cs b/src/processes/DimProcess.Executor/TechnicalUserProcessTypeExecutor.cs index 0919575..2903069 100644 --- a/src/processes/DimProcess.Executor/TechnicalUserProcessTypeExecutor.cs +++ b/src/processes/DimProcess.Executor/TechnicalUserProcessTypeExecutor.cs @@ -23,7 +23,7 @@ using Dim.Entities.Enums; using DimProcess.Library; using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; -using Org.Eclipse.TractusX.Portal.Backend.Processes.Worker.Library; +using Processes.Worker.Library; using System.Collections.Immutable; namespace DimProcess.Executor; @@ -57,7 +57,7 @@ public class TechnicalUserProcessTypeExecutor( } _technicalUserId = technicalUserId; - _tenantName = $"{bpn}_{companyName}"; + _tenantName = $"{bpn}{companyName}"; return new IProcessTypeExecutor.InitializationResult(false, null); } diff --git a/src/processes/DimProcess.Library/Callback/CallbackService.cs b/src/processes/DimProcess.Library/Callback/CallbackService.cs index cd0fa66..304ae99 100644 --- a/src/processes/DimProcess.Library/Callback/CallbackService.cs +++ b/src/processes/DimProcess.Library/Callback/CallbackService.cs @@ -18,7 +18,7 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -using Dim.Clients.Api.Cf; +using Dim.Clients.Api.Div.Models; using Dim.Clients.Extensions; using DimProcess.Library.Callback.DependencyInjection; using Microsoft.Extensions.Options; @@ -34,17 +34,14 @@ public class CallbackService(ITokenService tokenService, IOptions(_settings, cancellationToken) .ConfigureAwait(false); var data = new CallbackDataModel( did, didDocument, - new AuthenticationDetail( - dimDetails.Credentials.Uaa.Url, - dimDetails.Credentials.Uaa.ClientId, - dimDetails.Credentials.Uaa.ClientSecret) + authenticationDetail ); await httpClient.PostAsJsonAsync($"/api/administration/registration/dim/{bpn}", data, JsonSerializerExtensions.Options, cancellationToken) .CatchingIntoServiceExceptionFor("send-callback", HttpAsyncResponseMessageExtension.RecoverOptions.INFRASTRUCTURE) diff --git a/src/processes/DimProcess.Library/Callback/ICallbackService.cs b/src/processes/DimProcess.Library/Callback/ICallbackService.cs index 5f8806d..daa18ce 100644 --- a/src/processes/DimProcess.Library/Callback/ICallbackService.cs +++ b/src/processes/DimProcess.Library/Callback/ICallbackService.cs @@ -18,14 +18,13 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -using Dim.Clients.Api.Cf; using System.Text.Json; namespace DimProcess.Library.Callback; public interface ICallbackService { - Task SendCallback(string bpn, ServiceCredentialBindingDetailResponse dimDetails, JsonDocument didDocument, string did, CancellationToken cancellationToken); + Task SendCallback(string bpn, AuthenticationDetail authenticationDetail, JsonDocument didDocument, string did, CancellationToken cancellationToken); Task SendTechnicalUserCallback(Guid externalId, string tokenAddress, string clientId, string clientSecret, CancellationToken cancellationToken); diff --git a/src/processes/DimProcess.Library/DependencyInjection/DimHandlerExtensions.cs b/src/processes/DimProcess.Library/DependencyInjection/DimHandlerExtensions.cs index 09ca0de..590206b 100644 --- a/src/processes/DimProcess.Library/DependencyInjection/DimHandlerExtensions.cs +++ b/src/processes/DimProcess.Library/DependencyInjection/DimHandlerExtensions.cs @@ -18,13 +18,8 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -using Dim.Clients.Api.Cf.DependencyInjection; using Dim.Clients.Api.Dim.DependencyInjection; -using Dim.Clients.Api.Entitlements.DependencyInjection; -using Dim.Clients.Api.Provisioning.DependencyInjection; -using Dim.Clients.Api.Services.DependencyInjection; -using Dim.Clients.Api.SubAccounts.DependencyInjection; -using Dim.Clients.Api.Subscriptions.DependencyInjection; +using Dim.Clients.Api.Div.DependencyInjection; using Dim.Clients.Token; using DimProcess.Library.Callback.DependencyInjection; using Microsoft.Extensions.Configuration; @@ -43,12 +38,7 @@ public static IServiceCollection AddDimProcessHandler(this IServiceCollection se services .AddTransient() .AddTransient() - .AddSubAccountClient(config.GetSection("SubAccount")) - .AddEntitlementClient(config.GetSection("Entitlement")) - .AddServiceClient() - .AddSubscriptionClient() - .AddProvisioningClient() - .AddCfClient(config.GetSection("Cf")) + .AddProvisioningClient(config.GetSection("Provisioning")) .AddDimClient() .AddCallbackClient(config.GetSection("Callback")); @@ -64,7 +54,7 @@ public static IServiceCollection AddTechnicalUserProcessHandler(this IServiceCol services .AddTransient() .AddTransient() - .AddCfClient(config.GetSection("Cf")) + .AddProvisioningClient(config.GetSection("Provisioning")) .AddCallbackClient(config.GetSection("Callback")); return services; diff --git a/src/processes/DimProcess.Library/DependencyInjection/DimHandlerSettings.cs b/src/processes/DimProcess.Library/DependencyInjection/DimHandlerSettings.cs index 6b09d1d..bae117c 100644 --- a/src/processes/DimProcess.Library/DependencyInjection/DimHandlerSettings.cs +++ b/src/processes/DimProcess.Library/DependencyInjection/DimHandlerSettings.cs @@ -18,6 +18,8 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ +using Org.Eclipse.TractusX.Portal.Backend.Framework.Models.Configuration; +using Org.Eclipse.TractusX.Portal.Backend.Framework.Models.Validation; using System.ComponentModel.DataAnnotations; namespace DimProcess.Library.DependencyInjection; @@ -25,20 +27,12 @@ namespace DimProcess.Library.DependencyInjection; public class DimHandlerSettings { [Required(AllowEmptyStrings = false)] - public string AdminMail { get; set; } = null!; + public string ApplicationName { get; set; } = null!; [Required] - public Guid RootDirectoryId { get; set; } + public int EncryptionConfigIndex { get; set; } - [Required(AllowEmptyStrings = false)] - public string AuthUrl { get; set; } = null!; - - [Required(AllowEmptyStrings = false)] - public string ClientidCisCentral { get; set; } = null!; - - [Required(AllowEmptyStrings = false)] - public string ClientsecretCisCentral { get; set; } = null!; - - [Required(AllowEmptyStrings = false)] - public string EncryptionKey { get; set; } = null!; + [Required] + [DistinctValues("x => x.Index")] + public IEnumerable EncryptionConfigs { get; set; } = null!; } diff --git a/src/processes/DimProcess.Library/DimProcessHandler.cs b/src/processes/DimProcess.Library/DimProcessHandler.cs index d7c8946..855feb7 100644 --- a/src/processes/DimProcess.Library/DimProcessHandler.cs +++ b/src/processes/DimProcess.Library/DimProcessHandler.cs @@ -18,464 +18,168 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -using Dim.Clients.Api.Cf; using Dim.Clients.Api.Dim; -using Dim.Clients.Api.Entitlements; -using Dim.Clients.Api.Provisioning; -using Dim.Clients.Api.Services; -using Dim.Clients.Api.SubAccounts; -using Dim.Clients.Api.Subscriptions; +using Dim.Clients.Api.Div; +using Dim.Clients.Api.Div.Models; using Dim.Clients.Token; using Dim.DbAccess; +using Dim.DbAccess.Extensions; using Dim.DbAccess.Repositories; using Dim.Entities.Enums; using DimProcess.Library.Callback; using DimProcess.Library.DependencyInjection; using Microsoft.Extensions.Options; using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; +using Org.Eclipse.TractusX.Portal.Backend.Framework.Models.Configuration; +using System.Text.Json; namespace DimProcess.Library; public class DimProcessHandler( IDimRepositories dimRepositories, - ISubAccountClient subAccountClient, - IServiceClient serviceClient, - ISubscriptionClient subscriptionClient, - IEntitlementClient entitlementClient, IProvisioningClient provisioningClient, - ICfClient cfClient, IDimClient dimClient, ICallbackService callbackService, + IHttpClientFactory httpClientFactory, IOptions options) : IDimProcessHandler { private readonly DimHandlerSettings _settings = options.Value; - public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CreateSubaccount(Guid tenantId, string tenantName, CancellationToken cancellationToken) + public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CreateWallet(Guid tenantId, string tenantName, CancellationToken cancellationToken) { - var parentDirectoryId = _settings.RootDirectoryId; - var adminMail = _settings.AdminMail; - var subAccountAuth = new BasicAuthSettings - { - TokenAddress = $"{_settings.AuthUrl}/oauth/token", - ClientId = _settings.ClientidCisCentral, - ClientSecret = _settings.ClientsecretCisCentral - }; - - var bpn = await dimRepositories.GetInstance().GetTenantBpn(tenantId); - var subAccountId = await subAccountClient.CreateSubaccount(subAccountAuth, adminMail, tenantName, parentDirectoryId, bpn, cancellationToken).ConfigureAwait(false); - dimRepositories.GetInstance().AttachAndModifyTenant(tenantId, tenant => - { - tenant.SubAccountId = null; - }, - tenant => - { - tenant.SubAccountId = subAccountId; - }); - return new ValueTuple?, ProcessStepStatusId, bool, string?>( - Enumerable.Repeat(ProcessStepTypeId.CREATE_SERVICEMANAGER_BINDINGS, 1), - ProcessStepStatusId.DONE, - false, - null); - } - - public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CreateServiceManagerBindings(Guid tenantId, CancellationToken cancellationToken) - { - var subAccountAuth = new BasicAuthSettings - { - TokenAddress = $"{_settings.AuthUrl}/oauth/token", - ClientId = _settings.ClientidCisCentral, - ClientSecret = _settings.ClientsecretCisCentral - }; - var tenantRepository = dimRepositories.GetInstance(); - var subAccountId = await tenantRepository.GetSubAccountIdByTenantId(tenantId).ConfigureAwait(false); - if (subAccountId == null) + var (isIssuer, didDocumentLocation) = await tenantRepository.GetHostingUrlAndIsIssuer(tenantId).ConfigureAwait(ConfigureAwaitOptions.None); + if (didDocumentLocation == null) { - throw new ConflictException("SubAccountId must not be null."); + throw new UnexpectedConditionException("DidDocumentLocation must always be set"); } - await subAccountClient.CreateServiceManagerBindings(subAccountAuth, subAccountId.Value, cancellationToken).ConfigureAwait(false); + var operationId = await provisioningClient.CreateOperation(tenantId, tenantName, _settings.ApplicationName, tenantName, didDocumentLocation, isIssuer, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); + tenantRepository.AttachAndModifyTenant(tenantId, t => t.OperationId = null, t => t.OperationId = operationId); return new ValueTuple?, ProcessStepStatusId, bool, string?>( - Enumerable.Repeat(ProcessStepTypeId.ASSIGN_ENTITLEMENTS, 1), + Enumerable.Repeat(ProcessStepTypeId.CHECK_OPERATION, 1), ProcessStepStatusId.DONE, false, null); } - public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> AssignEntitlements(Guid tenantId, CancellationToken cancellationToken) + public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CheckOperation(Guid tenantId, CancellationToken cancellationToken) { - var subAccountAuth = new BasicAuthSettings - { - TokenAddress = $"{_settings.AuthUrl}/oauth/token", - ClientId = _settings.ClientidCisCentral, - ClientSecret = _settings.ClientsecretCisCentral - }; - var subAccountId = await dimRepositories.GetInstance().GetSubAccountIdByTenantId(tenantId).ConfigureAwait(false); - if (subAccountId == null) + var tenantRepository = dimRepositories.GetInstance(); + var operationId = await tenantRepository.GetOperationId(tenantId).ConfigureAwait(ConfigureAwaitOptions.None); + if (operationId == null) { - throw new ConflictException("SubAccountId must not be null."); + throw new UnexpectedConditionException("OperationId must always be set"); } - await entitlementClient.AssignEntitlements(subAccountAuth, subAccountId.Value, cancellationToken).ConfigureAwait(false); - - return new ValueTuple?, ProcessStepStatusId, bool, string?>( - Enumerable.Repeat(ProcessStepTypeId.CREATE_SERVICE_INSTANCE, 1), - ProcessStepStatusId.DONE, - false, - null); - } - - public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CreateServiceInstance(Guid tenantId, CancellationToken cancellationToken) - { - var subAccountAuth = new BasicAuthSettings - { - TokenAddress = $"{_settings.AuthUrl}/oauth/token", - ClientId = _settings.ClientidCisCentral, - ClientSecret = _settings.ClientsecretCisCentral - }; - var subAccountId = await dimRepositories.GetInstance().GetSubAccountIdByTenantId(tenantId).ConfigureAwait(false); - if (subAccountId == null) + var response = await provisioningClient.GetOperation(operationId.Value, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); + if (response.Status == OperationResponseStatus.pending) { - throw new ConflictException("SubAccountId must not be null."); + return new ValueTuple?, ProcessStepStatusId, bool, string?>( + null, + ProcessStepStatusId.TODO, + false, + null); } - var saBinding = await subAccountClient.GetServiceManagerBindings(subAccountAuth, subAccountId.Value, cancellationToken).ConfigureAwait(false); - var serviceInstance = await serviceClient.CreateServiceInstance(saBinding, cancellationToken).ConfigureAwait(false); - - dimRepositories.GetInstance().AttachAndModifyTenant(tenantId, tenant => - { - tenant.ServiceInstanceId = null; - }, - tenant => - { - tenant.ServiceInstanceId = serviceInstance.Id; - }); - return new ValueTuple?, ProcessStepStatusId, bool, string?>( - Enumerable.Repeat(ProcessStepTypeId.CREATE_SERVICE_BINDING, 1), - ProcessStepStatusId.DONE, - false, - null); - } - - public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CreateServiceBindings(Guid tenantId, CancellationToken cancellationToken) - { - var subAccountAuth = new BasicAuthSettings - { - TokenAddress = $"{_settings.AuthUrl}/oauth/token", - ClientId = _settings.ClientidCisCentral, - ClientSecret = _settings.ClientsecretCisCentral - }; - var (subAccountId, serviceInstanceId) = await dimRepositories.GetInstance().GetSubAccountAndServiceInstanceIdsByTenantId(tenantId).ConfigureAwait(false); - if (subAccountId == null) + if (response is { Status: OperationResponseStatus.completed, Data: null }) { - throw new ConflictException("SubAccountId must not be null."); + throw new UnexpectedConditionException($"Data should never be null when in status {OperationResponseStatus.completed}"); } - if (string.IsNullOrEmpty(serviceInstanceId)) - { - throw new ConflictException("ServiceInstanceId must not be null."); - } + var serviceKey = response.Data!.ServiceKey; - var saBinding = await subAccountClient.GetServiceManagerBindings(subAccountAuth, subAccountId.Value, cancellationToken).ConfigureAwait(false); - var serviceBinding = await serviceClient.CreateServiceBinding(saBinding, serviceInstanceId, cancellationToken).ConfigureAwait(false); + var cryptoHelper = _settings.EncryptionConfigs.GetCryptoHelper(_settings.EncryptionConfigIndex); + var (secret, initializationVector) = cryptoHelper.Encrypt(serviceKey.Uaa.ClientSecret); - dimRepositories.GetInstance().AttachAndModifyTenant(tenantId, tenant => + tenantRepository.AttachAndModifyTenant(tenantId, + t => { - tenant.ServiceBindingName = null; + t.TokenAddress = null; + t.BaseUrl = null; + t.ClientId = null; + t.ClientSecret = null; }, - tenant => + t => { - tenant.ServiceBindingName = serviceBinding.Name; + t.TokenAddress = serviceKey.Uaa.Url; + t.BaseUrl = serviceKey.Url; + t.ClientId = serviceKey.Uaa.ClientId; + t.ClientSecret = secret; + t.InitializationVector = initializationVector; + t.EncryptionMode = _settings.EncryptionConfigIndex; }); - return new ValueTuple?, ProcessStepStatusId, bool, string?>( - Enumerable.Repeat(ProcessStepTypeId.SUBSCRIBE_APPLICATION, 1), - ProcessStepStatusId.DONE, - false, - null); - } - - public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> SubscribeApplication(Guid tenantId, CancellationToken cancellationToken) - { - var subAccountAuth = new BasicAuthSettings - { - TokenAddress = $"{_settings.AuthUrl}/oauth/token", - ClientId = _settings.ClientidCisCentral, - ClientSecret = _settings.ClientsecretCisCentral - }; - var (subAccountId, serviceBindingName) = await dimRepositories.GetInstance().GetSubAccountIdAndServiceBindingNameByTenantId(tenantId).ConfigureAwait(false); - if (subAccountId == null) - { - throw new ConflictException("SubAccountId must not be null."); - } - - if (string.IsNullOrEmpty(serviceBindingName)) - { - throw new ConflictException("ServiceBindingName must not be null."); - } - - var saBinding = await subAccountClient.GetServiceManagerBindings(subAccountAuth, subAccountId.Value, cancellationToken).ConfigureAwait(false); - var bindingResponse = await serviceClient.GetServiceBinding(saBinding, serviceBindingName, cancellationToken).ConfigureAwait(false); - await subscriptionClient.SubscribeApplication(saBinding.Url, bindingResponse, "decentralized-identity-management-app", "standard", cancellationToken).ConfigureAwait(false); - - return new ValueTuple?, ProcessStepStatusId, bool, string?>( - Enumerable.Repeat(ProcessStepTypeId.CREATE_CLOUD_FOUNDRY_ENVIRONMENT, 1), - ProcessStepStatusId.DONE, - false, - null); - } - - public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CreateCloudFoundryEnvironment(Guid tenantId, string tenantName, CancellationToken cancellationToken) - { - var adminMail = _settings.AdminMail; - var subAccountAuth = new BasicAuthSettings - { - TokenAddress = $"{_settings.AuthUrl}/oauth/token", - ClientId = _settings.ClientidCisCentral, - ClientSecret = _settings.ClientsecretCisCentral - }; - var (subAccountId, serviceBindingName) = await dimRepositories.GetInstance().GetSubAccountIdAndServiceBindingNameByTenantId(tenantId).ConfigureAwait(false); - if (subAccountId == null) - { - throw new ConflictException("SubAccountId must not be null."); - } - - if (string.IsNullOrEmpty(serviceBindingName)) - { - throw new ConflictException("ServiceBindingName must not be null."); - } - - var saBinding = await subAccountClient.GetServiceManagerBindings(subAccountAuth, subAccountId.Value, cancellationToken).ConfigureAwait(false); - var bindingResponse = await serviceClient.GetServiceBinding(saBinding, serviceBindingName, cancellationToken).ConfigureAwait(false); - await provisioningClient.CreateCloudFoundryEnvironment(saBinding.Url, bindingResponse, tenantName, adminMail, cancellationToken) - .ConfigureAwait(false); return new ValueTuple?, ProcessStepStatusId, bool, string?>( - Enumerable.Repeat(ProcessStepTypeId.CREATE_CLOUD_FOUNDRY_SPACE, 1), + Enumerable.Repeat(ProcessStepTypeId.GET_COMPANY, 1), ProcessStepStatusId.DONE, false, null); } - public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CreateCloudFoundrySpace(Guid tenantId, string tenantName, CancellationToken cancellationToken) + public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> GetCompany(Guid tenantId, string tenantName, CancellationToken cancellationToken) { - var spaceId = await cfClient.CreateCloudFoundrySpace(tenantName, cancellationToken).ConfigureAwait(false); - - dimRepositories.GetInstance().AttachAndModifyTenant(tenantId, tenant => - { - tenant.SpaceId = null; - }, - tenant => - { - tenant.SpaceId = spaceId; - }); - return new ValueTuple?, ProcessStepStatusId, bool, string?>( - Enumerable.Repeat(ProcessStepTypeId.ADD_SPACE_MANAGER_ROLE, 1), - ProcessStepStatusId.DONE, - false, - null); - } - - public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> AddSpaceManagerRole(Guid tenantId, CancellationToken cancellationToken) - { - var adminMail = _settings.AdminMail; - var spaceId = await dimRepositories.GetInstance().GetSpaceId(tenantId).ConfigureAwait(false); - if (spaceId == null) - { - throw new ConflictException("SpaceId must not be null."); - } - - await cfClient.AddSpaceRoleToUser("space_manager", adminMail, spaceId.Value, cancellationToken).ConfigureAwait(false); - - return new ValueTuple?, ProcessStepStatusId, bool, string?>( - Enumerable.Repeat(ProcessStepTypeId.ADD_SPACE_DEVELOPER_ROLE, 1), - ProcessStepStatusId.DONE, - false, - null); - } - - public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> AddSpaceDeveloperRole(Guid tenantId, CancellationToken cancellationToken) - { - var adminMail = _settings.AdminMail; - var spaceId = await dimRepositories.GetInstance().GetSpaceId(tenantId).ConfigureAwait(false); - if (spaceId == null) - { - throw new ConflictException("SpaceId must not be null."); - } - - await cfClient.AddSpaceRoleToUser("space_developer", adminMail, spaceId.Value, cancellationToken).ConfigureAwait(false); - - return new ValueTuple?, ProcessStepStatusId, bool, string?>( - Enumerable.Repeat(ProcessStepTypeId.CREATE_DIM_SERVICE_INSTANCE, 1), - ProcessStepStatusId.DONE, - false, - null); - } - - public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CreateDimServiceInstance(string tenantName, Guid tenantId, CancellationToken cancellationToken) - { - var servicePlanId = await cfClient.GetServicePlan("decentralized-identity-management", "standard", cancellationToken).ConfigureAwait(false); - var spaceId = await cfClient.GetSpace(tenantName, cancellationToken).ConfigureAwait(false); - await cfClient.CreateDimServiceInstance(tenantName, spaceId, servicePlanId, cancellationToken).ConfigureAwait(false); - - return new ValueTuple?, ProcessStepStatusId, bool, string?>( - Enumerable.Repeat(ProcessStepTypeId.CREATE_SERVICE_INSTANCE_BINDING, 1), - ProcessStepStatusId.DONE, - false, - null); - } - - public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CreateServiceInstanceBindings(string tenantName, Guid tenantId, CancellationToken cancellationToken) - { - var spaceId = await dimRepositories.GetInstance().GetSpaceId(tenantId).ConfigureAwait(false); - if (spaceId == null) - { - throw new ConflictException("SpaceId must not be null."); - } - - await cfClient.CreateServiceInstanceBindings(tenantName, null, spaceId.Value, cancellationToken).ConfigureAwait(false); - - return new ValueTuple?, ProcessStepStatusId, bool, string?>( - Enumerable.Repeat(ProcessStepTypeId.GET_DIM_DETAILS, 1), - ProcessStepStatusId.DONE, - false, - null); - } - - public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> GetDimDetails(string tenantName, Guid tenantId, CancellationToken cancellationToken) - { - var spaceId = await dimRepositories.GetInstance().GetSpaceId(tenantId).ConfigureAwait(false); - if (spaceId == null) - { - throw new ConflictException("SpaceId must not be null."); - } - - var dimInstanceId = await cfClient.GetServiceBinding(tenantName, spaceId.Value, $"{tenantName}-dim-key01", cancellationToken).ConfigureAwait(false); - - dimRepositories.GetInstance().AttachAndModifyTenant(tenantId, tenant => - { - tenant.DimInstanceId = null; - }, - tenant => - { - tenant.DimInstanceId = dimInstanceId; - }); - return new ValueTuple?, ProcessStepStatusId, bool, string?>( - Enumerable.Repeat(ProcessStepTypeId.CREATE_APPLICATION, 1), - ProcessStepStatusId.DONE, - false, - null); - } + var tenantRepository = dimRepositories.GetInstance(); + var (baseUrl, walletData) = await tenantRepository.GetCompanyRequestData(tenantId).ConfigureAwait(ConfigureAwaitOptions.None); + var (tokenAddress, clientId, clientSecret, initializationVector, encryptionMode) = walletData.ValidateData(); - public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CreateApplication(string tenantName, Guid tenantId, CancellationToken cancellationToken) - { - var (dimInstanceId, _, _) = await dimRepositories.GetInstance().GetDimInstanceIdAndHostingUrl(tenantId).ConfigureAwait(false); - if (dimInstanceId == null) + if (string.IsNullOrWhiteSpace(baseUrl)) { - throw new ConflictException("DimInstanceId must not be null."); + throw new UnexpectedConditionException("BaseAddress must not be null"); } - var dimDetails = await cfClient.GetServiceBindingDetails(dimInstanceId.Value, cancellationToken).ConfigureAwait(false); + var cryptoHelper = _settings.EncryptionConfigs.GetCryptoHelper(encryptionMode); + var secret = cryptoHelper.Decrypt(clientSecret, initializationVector); var dimAuth = new BasicAuthSettings { - TokenAddress = $"{dimDetails.Credentials.Uaa.Url}/oauth/token", - ClientId = dimDetails.Credentials.Uaa.ClientId, - ClientSecret = dimDetails.Credentials.Uaa.ClientSecret + TokenAddress = $"{tokenAddress}/oauth/token", + ClientId = clientId, + ClientSecret = secret }; - var dimBaseUrl = dimDetails.Credentials.Url; - var applicationId = await dimClient.CreateApplication(dimAuth, dimBaseUrl, tenantName, cancellationToken).ConfigureAwait(false); - dimRepositories.GetInstance().AttachAndModifyTenant(tenantId, tenant => - { - tenant.ApplicationId = null; - }, - tenant => - { - tenant.ApplicationId = applicationId; - }); - return new ValueTuple?, ProcessStepStatusId, bool, string?>( - Enumerable.Repeat(ProcessStepTypeId.CREATE_COMPANY_IDENTITY, 1), - ProcessStepStatusId.DONE, - false, - null); - } + var companyData = await dimClient.GetCompanyData(dimAuth, baseUrl, tenantName, _settings.ApplicationName, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); - public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CreateCompanyIdentity(Guid tenantId, CancellationToken cancellationToken) - { - var (dimInstanceId, hostingUrl, isIssuer) = await dimRepositories.GetInstance().GetDimInstanceIdAndHostingUrl(tenantId).ConfigureAwait(false); - if (dimInstanceId == null) + tenantRepository.AttachAndModifyTenant(tenantId, t => { - throw new ConflictException("DimInstanceId must not be null."); - } - - var dimDetails = await cfClient.GetServiceBindingDetails(dimInstanceId.Value, cancellationToken).ConfigureAwait(false); - - var dimAuth = new BasicAuthSettings + t.DidDownloadUrl = null; + t.CompanyId = null; + }, + t => { - TokenAddress = $"{dimDetails.Credentials.Uaa.Url}/oauth/token", - ClientId = dimDetails.Credentials.Uaa.ClientId, - ClientSecret = dimDetails.Credentials.Uaa.ClientSecret - }; - var dimBaseUrl = dimDetails.Credentials.Url; - var result = await dimClient.CreateCompanyIdentity(dimAuth, tenantId, hostingUrl, dimBaseUrl, isIssuer, cancellationToken).ConfigureAwait(false); + t.DidDownloadUrl = companyData.DownloadUrl; + t.CompanyId = companyData.CompanyId; + }); - dimRepositories.GetInstance().AttachAndModifyTenant(tenantId, tenant => - { - tenant.DidDownloadUrl = null; - tenant.Did = null; - tenant.CompanyId = null; - }, - tenant => - { - tenant.DidDownloadUrl = result.DownloadUrl; - tenant.Did = result.Did; - tenant.CompanyId = result.CompanyId; - }); return new ValueTuple?, ProcessStepStatusId, bool, string?>( - Enumerable.Repeat(ProcessStepTypeId.ASSIGN_COMPANY_APPLICATION, 1), + Enumerable.Repeat(ProcessStepTypeId.GET_DID_DOCUMENT, 1), ProcessStepStatusId.DONE, false, null); } - public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> AssignCompanyApplication(Guid tenantId, CancellationToken cancellationToken) + public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> GetDidDocument(Guid tenantId, CancellationToken cancellationToken) { - var (applicationId, companyId, dimInstanceId, isIssuer) = await dimRepositories.GetInstance().GetApplicationAndCompanyId(tenantId).ConfigureAwait(false); - if (applicationId == null) - { - throw new ConflictException("ApplicationId must always be set here"); - } - - if (companyId == null) - { - throw new ConflictException("CompanyId must always be set here"); - } - - if (dimInstanceId == null) + var tenantRepository = dimRepositories.GetInstance(); + var (downloadUrl, isIssuer) = await tenantRepository.GetDownloadUrlAndIsIssuer(tenantId).ConfigureAwait(ConfigureAwaitOptions.None); + if (string.IsNullOrWhiteSpace(downloadUrl)) { - throw new ConflictException("DimInstanceId must not be null."); + throw new UnexpectedConditionException("DownloadUrl must not be null"); } - var dimDetails = await cfClient.GetServiceBindingDetails(dimInstanceId.Value, cancellationToken).ConfigureAwait(false); - var dimAuth = new BasicAuthSettings - { - TokenAddress = $"{dimDetails.Credentials.Uaa.Url}/oauth/token", - ClientId = dimDetails.Credentials.Uaa.ClientId, - ClientSecret = dimDetails.Credentials.Uaa.ClientSecret - }; - var dimBaseUrl = dimDetails.Credentials.Url; - var applicationKey = await dimClient.GetApplication(dimAuth, dimBaseUrl, applicationId, cancellationToken); - await dimClient.AssignApplicationToCompany(dimAuth, dimBaseUrl, applicationKey, companyId.Value, cancellationToken).ConfigureAwait(false); + var didDocument = await GetDidDocument(downloadUrl, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); + var did = didDocument.RootElement.GetProperty("id").GetString(); - dimRepositories.GetInstance().AttachAndModifyTenant(tenantId, tenant => + tenantRepository.AttachAndModifyTenant(tenantId, t => { - tenant.ApplicationKey = null; + t.Did = null; }, - tenant => + t => { - tenant.ApplicationKey = applicationKey; + t.Did = did; }); + return new ValueTuple?, ProcessStepStatusId, bool, string?>( Enumerable.Repeat(isIssuer ? ProcessStepTypeId.CREATE_STATUS_LIST : ProcessStepTypeId.SEND_CALLBACK, 1), ProcessStepStatusId.DONE, @@ -485,26 +189,29 @@ await provisioningClient.CreateCloudFoundryEnvironment(saBinding.Url, bindingRes public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CreateStatusList(Guid tenantId, CancellationToken cancellationToken) { - var (_, companyId, dimInstanceId, _) = await dimRepositories.GetInstance().GetApplicationAndCompanyId(tenantId).ConfigureAwait(false); + var (companyId, baseUrl, walletData) = await dimRepositories.GetInstance().GetStatusListCreationData(tenantId).ConfigureAwait(ConfigureAwaitOptions.None); + var (tokenAddress, clientId, clientSecret, initializationVector, encryptionMode) = walletData.ValidateData(); + if (companyId == null) { - throw new ConflictException("CompanyId must always be set here"); + throw new UnexpectedConditionException("CompanyId must not be null"); } - if (dimInstanceId == null) + if (baseUrl == null) { - throw new ConflictException("DimInstanceId must not be null."); + throw new UnexpectedConditionException("BaseUrl must not be null"); } - var dimDetails = await cfClient.GetServiceBindingDetails(dimInstanceId.Value, cancellationToken).ConfigureAwait(false); + var cryptoHelper = _settings.EncryptionConfigs.GetCryptoHelper(encryptionMode); + var secret = cryptoHelper.Decrypt(clientSecret, initializationVector); + var dimAuth = new BasicAuthSettings { - TokenAddress = $"{dimDetails.Credentials.Uaa.Url}/oauth/token", - ClientId = dimDetails.Credentials.Uaa.ClientId, - ClientSecret = dimDetails.Credentials.Uaa.ClientSecret + TokenAddress = $"{tokenAddress}/oauth/token", + ClientId = clientId, + ClientSecret = secret }; - var dimBaseUrl = dimDetails.Credentials.Url; - await dimClient.CreateStatusList(dimAuth, dimBaseUrl, companyId.Value, cancellationToken).ConfigureAwait(false); + await dimClient.CreateStatusList(dimAuth, baseUrl, companyId.Value, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); return new ValueTuple?, ProcessStepStatusId, bool, string?>( Enumerable.Repeat(ProcessStepTypeId.SEND_CALLBACK, 1), @@ -515,26 +222,29 @@ await provisioningClient.CreateCloudFoundryEnvironment(saBinding.Url, bindingRes public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> SendCallback(Guid tenantId, CancellationToken cancellationToken) { - var (bpn, downloadUrl, did, dimInstanceId) = await dimRepositories.GetInstance().GetCallbackData(tenantId).ConfigureAwait(false); - if (downloadUrl == null) + var (bpn, baseUrl, walletData, did, downloadUrl) = await dimRepositories.GetInstance().GetCallbackData(tenantId).ConfigureAwait(ConfigureAwaitOptions.None); + var (tokenAddress, clientId, clientSecret, initializationVector, encryptionMode) = walletData.ValidateData(); + if (baseUrl == null) { - throw new ConflictException("DownloadUrl must not be null."); + throw new UnexpectedConditionException("BaseUrl must always be set"); } if (did == null) { - throw new ConflictException("Did must not be null."); + throw new UnexpectedConditionException("Did must always be set"); } - if (dimInstanceId == null) + if (downloadUrl == null) { - throw new ConflictException("DimInstanceId must not be null."); + throw new UnexpectedConditionException("DownloadUrl must always be set"); } - var dimDetails = await cfClient.GetServiceBindingDetails(dimInstanceId.Value, cancellationToken).ConfigureAwait(false); - var didDocument = await dimClient.GetDidDocument(downloadUrl, cancellationToken).ConfigureAwait(false); + var cryptoHelper = _settings.EncryptionConfigs.GetCryptoHelper(encryptionMode); + var secret = cryptoHelper.Decrypt(clientSecret, initializationVector); - await callbackService.SendCallback(bpn, dimDetails, didDocument, did, cancellationToken).ConfigureAwait(false); + var didDocument = await GetDidDocument(downloadUrl, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); + var uaa = new AuthenticationDetail(tokenAddress, clientId, secret); + await callbackService.SendCallback(bpn, uaa, didDocument, did, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); return new ValueTuple?, ProcessStepStatusId, bool, string?>( null, @@ -542,4 +252,11 @@ await provisioningClient.CreateCloudFoundryEnvironment(saBinding.Url, bindingRes false, null); } + + private async Task GetDidDocument(string downloadUrl, CancellationToken cancellationToken) + { + var client = httpClientFactory.CreateClient("didDocumentDownload"); + using var result = await client.GetStreamAsync(downloadUrl, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); + return await JsonDocument.ParseAsync(result, cancellationToken: cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); + } } diff --git a/src/processes/DimProcess.Library/IDimProcessHandler.cs b/src/processes/DimProcess.Library/IDimProcessHandler.cs index 0fd68ad..964da62 100644 --- a/src/processes/DimProcess.Library/IDimProcessHandler.cs +++ b/src/processes/DimProcess.Library/IDimProcessHandler.cs @@ -24,22 +24,10 @@ namespace DimProcess.Library; public interface IDimProcessHandler { - Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CreateSubaccount(Guid tenantId, string tenantName, CancellationToken cancellationToken); - Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CreateServiceManagerBindings(Guid tenantId, CancellationToken cancellationToken); - Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> AssignEntitlements(Guid tenantId, CancellationToken cancellationToken); - Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CreateServiceInstance(Guid tenantId, CancellationToken cancellationToken); - Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CreateServiceBindings(Guid tenantId, CancellationToken cancellationToken); - Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> SubscribeApplication(Guid tenantId, CancellationToken cancellationToken); - Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CreateCloudFoundryEnvironment(Guid tenantId, string tenantName, CancellationToken cancellationToken); - Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CreateCloudFoundrySpace(Guid tenantId, string tenantName, CancellationToken cancellationToken); - Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> AddSpaceManagerRole(Guid tenantId, CancellationToken cancellationToken); - Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> AddSpaceDeveloperRole(Guid tenantId, CancellationToken cancellationToken); - Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CreateDimServiceInstance(string tenantName, Guid tenantId, CancellationToken cancellationToken); - Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CreateServiceInstanceBindings(string tenantName, Guid tenantId, CancellationToken cancellationToken); - Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CreateApplication(string tenantName, Guid tenantId, CancellationToken cancellationToken); - Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> GetDimDetails(string tenantName, Guid tenantId, CancellationToken cancellationToken); - Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CreateCompanyIdentity(Guid tenantId, CancellationToken cancellationToken); + Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CreateWallet(Guid tenantId, string tenantName, CancellationToken cancellationToken); + Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CheckOperation(Guid tenantId, CancellationToken cancellationToken); + Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> GetCompany(Guid tenantId, string tenantName, CancellationToken cancellationToken); + Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> GetDidDocument(Guid tenantId, CancellationToken cancellationToken); Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CreateStatusList(Guid tenantId, CancellationToken cancellationToken); - Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> AssignCompanyApplication(Guid tenantId, CancellationToken cancellationToken); Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> SendCallback(Guid tenantId, CancellationToken cancellationToken); } diff --git a/src/processes/DimProcess.Library/TechnicalUserProcessHandler.cs b/src/processes/DimProcess.Library/TechnicalUserProcessHandler.cs index 18f414e..5f1ff45 100644 --- a/src/processes/DimProcess.Library/TechnicalUserProcessHandler.cs +++ b/src/processes/DimProcess.Library/TechnicalUserProcessHandler.cs @@ -18,101 +18,48 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -using Dim.Clients.Api.Cf; using Dim.DbAccess; +using Dim.DbAccess.Extensions; using Dim.DbAccess.Repositories; using Dim.Entities.Enums; using DimProcess.Library.Callback; using DimProcess.Library.DependencyInjection; using Microsoft.Extensions.Options; -using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; using Org.Eclipse.TractusX.Portal.Backend.Framework.Models.Configuration; -using Org.Eclipse.TractusX.Portal.Backend.Framework.Models.Encryption; namespace DimProcess.Library; public class TechnicalUserProcessHandler( IDimRepositories dimRepositories, - ICfClient cfClient, ICallbackService callbackService, IOptions options) : ITechnicalUserProcessHandler { private readonly TechnicalUserSettings _settings = options.Value; - public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CreateServiceInstanceBindings(string tenantName, Guid technicalUserId, CancellationToken cancellationToken) + public Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> CreateServiceInstanceBindings(string tenantName, Guid technicalUserId, CancellationToken cancellationToken) { - var (spaceId, technicalUserName) = await dimRepositories.GetInstance().GetSpaceIdAndTechnicalUserName(technicalUserId).ConfigureAwait(false); - if (spaceId == null) - { - throw new ConflictException("SpaceId must not be null."); - } - - await cfClient.CreateServiceInstanceBindings(tenantName, technicalUserName, spaceId.Value, cancellationToken).ConfigureAwait(false); - - return new ValueTuple?, ProcessStepStatusId, bool, string?>( - Enumerable.Repeat(ProcessStepTypeId.GET_TECHNICAL_USER_DATA, 1), - ProcessStepStatusId.DONE, + return Task.FromResult(new ValueTuple?, ProcessStepStatusId, bool, string?>( + null, + ProcessStepStatusId.FAILED, false, - null); + "Technical User Creation is currently not supported")); } - public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> GetTechnicalUserData(string tenantName, Guid technicalUserId, CancellationToken cancellationToken) + public Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> GetTechnicalUserData(string tenantName, Guid technicalUserId, CancellationToken cancellationToken) { - var (spaceId, technicalUserName) = await dimRepositories.GetInstance().GetSpaceIdAndTechnicalUserName(technicalUserId).ConfigureAwait(false); - if (spaceId == null) - { - throw new ConflictException("SpaceId must not be null."); - } - - var dimInstanceId = await cfClient.GetServiceBinding(tenantName, spaceId.Value, $"{technicalUserName}-dim-key01", cancellationToken).ConfigureAwait(false); - var dimDetails = await cfClient.GetServiceBindingDetails(dimInstanceId, cancellationToken).ConfigureAwait(false); - - var cryptoHelper = _settings.EncryptionConfigs.GetCryptoHelper(_settings.EncryptionConfigIndex); - var (secret, initializationVector) = cryptoHelper.Encrypt(dimDetails.Credentials.Uaa.ClientSecret); - - dimRepositories.GetInstance().AttachAndModifyTechnicalUser(technicalUserId, technicalUser => - { - technicalUser.TokenAddress = null; - technicalUser.ClientId = null; - technicalUser.ClientSecret = null; - technicalUser.InitializationVector = null; - technicalUser.EncryptionMode = null; - }, - technicalUser => - { - technicalUser.TokenAddress = $"{dimDetails.Credentials.Uaa.Url}/oauth/token"; - technicalUser.ClientId = dimDetails.Credentials.Uaa.ClientId; - technicalUser.ClientSecret = secret; - technicalUser.InitializationVector = initializationVector; - technicalUser.EncryptionMode = _settings.EncryptionConfigIndex; - }); - return new ValueTuple?, ProcessStepStatusId, bool, string?>( - Enumerable.Repeat(ProcessStepTypeId.SEND_TECHNICAL_USER_CREATION_CALLBACK, 1), - ProcessStepStatusId.DONE, + return Task.FromResult(new ValueTuple?, ProcessStepStatusId, bool, string?>( + null, + ProcessStepStatusId.FAILED, false, - null); + "Technical User Creation is currently not supported")); } public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> SendCreateCallback(Guid technicalUserId, CancellationToken cancellationToken) { - var (externalId, tokenAddress, clientId, clientSecret, initializationVector, _) = await dimRepositories.GetInstance().GetTechnicalUserCallbackData(technicalUserId).ConfigureAwait(false); - - if (string.IsNullOrWhiteSpace(clientId)) - { - throw new ConflictException("ClientId must not be null"); - } + var (externalId, walletData) = await dimRepositories.GetInstance().GetTechnicalUserCallbackData(technicalUserId).ConfigureAwait(false); + var (tokenAddress, clientId, clientSecret, initializationVector, encryptionMode) = walletData.ValidateData(); - if (string.IsNullOrWhiteSpace(tokenAddress)) - { - throw new ConflictException("TokenAddress must not be null"); - } - - if (clientSecret == null) - { - throw new ConflictException("Secret must not be null"); - } - - var cryptoHelper = _settings.EncryptionConfigs.GetCryptoHelper(_settings.EncryptionConfigIndex); + var cryptoHelper = _settings.EncryptionConfigs.GetCryptoHelper(encryptionMode); var secret = cryptoHelper.Decrypt(clientSecret, initializationVector); await callbackService.SendTechnicalUserCallback(externalId, tokenAddress, clientId, secret, cancellationToken).ConfigureAwait(false); @@ -124,22 +71,13 @@ public class TechnicalUserProcessHandler( null); } - public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> DeleteServiceInstanceBindings(string tenantName, Guid technicalUserId, CancellationToken cancellationToken) + public Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> DeleteServiceInstanceBindings(string tenantName, Guid technicalUserId, CancellationToken cancellationToken) { - var (spaceId, technicalUserName) = await dimRepositories.GetInstance().GetSpaceIdAndTechnicalUserName(technicalUserId).ConfigureAwait(false); - if (spaceId == null) - { - throw new ConflictException("SpaceId must not be null."); - } - - var serviceBindingId = await cfClient.GetServiceBinding(tenantName, spaceId.Value, $"{technicalUserName}-dim-key01", cancellationToken).ConfigureAwait(false); - await cfClient.DeleteServiceInstanceBindings(serviceBindingId, cancellationToken).ConfigureAwait(false); - - return new ValueTuple?, ProcessStepStatusId, bool, string?>( - Enumerable.Repeat(ProcessStepTypeId.SEND_TECHNICAL_USER_DELETION_CALLBACK, 1), - ProcessStepStatusId.DONE, + return Task.FromResult(new ValueTuple?, ProcessStepStatusId, bool, string?>( + null, + ProcessStepStatusId.FAILED, false, - null); + "Technical User deletion is currently not supported")); } public async Task<(IEnumerable? nextStepTypeIds, ProcessStepStatusId stepStatusId, bool modified, string? processMessage)> SendDeleteCallback(Guid technicalUserId, CancellationToken cancellationToken) diff --git a/src/database/Dim.Entities/Entities/VerifyProcessData.cs b/src/processes/Processes.Library/VerifyProcessData.cs similarity index 94% rename from src/database/Dim.Entities/Entities/VerifyProcessData.cs rename to src/processes/Processes.Library/VerifyProcessData.cs index 01805b3..4af7c41 100644 --- a/src/database/Dim.Entities/Entities/VerifyProcessData.cs +++ b/src/processes/Processes.Library/VerifyProcessData.cs @@ -18,7 +18,9 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -namespace Dim.Entities.Entities; +using Dim.Entities.Entities; + +namespace Dim.Processes.Library; public record VerifyProcessData( Process? Process, diff --git a/src/processes/Processes.Worker.Library/IProcessExecutor.cs b/src/processes/Processes.Worker.Library/IProcessExecutor.cs index 88a0b87..22bb752 100644 --- a/src/processes/Processes.Worker.Library/IProcessExecutor.cs +++ b/src/processes/Processes.Worker.Library/IProcessExecutor.cs @@ -20,7 +20,7 @@ using Dim.Entities.Enums; -namespace Org.Eclipse.TractusX.Portal.Backend.Processes.Worker.Library; +namespace Processes.Worker.Library; public interface IProcessExecutor { diff --git a/src/processes/Processes.Worker.Library/IProcessTypeExecutor.cs b/src/processes/Processes.Worker.Library/IProcessTypeExecutor.cs index 051803a..213de9f 100644 --- a/src/processes/Processes.Worker.Library/IProcessTypeExecutor.cs +++ b/src/processes/Processes.Worker.Library/IProcessTypeExecutor.cs @@ -21,7 +21,7 @@ using Dim.Entities.Enums; using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; -namespace Org.Eclipse.TractusX.Portal.Backend.Processes.Worker.Library; +namespace Processes.Worker.Library; public interface IProcessTypeExecutor { diff --git a/src/processes/Processes.Worker.Library/ProcessExecutionService.cs b/src/processes/Processes.Worker.Library/ProcessExecutionService.cs index d8644c2..9b5de35 100644 --- a/src/processes/Processes.Worker.Library/ProcessExecutionService.cs +++ b/src/processes/Processes.Worker.Library/ProcessExecutionService.cs @@ -27,7 +27,7 @@ using Org.Eclipse.TractusX.Portal.Backend.Framework.DBAccess; using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; -namespace Org.Eclipse.TractusX.Portal.Backend.Processes.Worker.Library; +namespace Processes.Worker.Library; /// /// Service that reads all open/pending processSteps of a checklist and triggers their execution. diff --git a/src/processes/Processes.Worker.Library/ProcessExecutionServiceExtensions.cs b/src/processes/Processes.Worker.Library/ProcessExecutionServiceExtensions.cs index 4642c0c..8fd0c74 100644 --- a/src/processes/Processes.Worker.Library/ProcessExecutionServiceExtensions.cs +++ b/src/processes/Processes.Worker.Library/ProcessExecutionServiceExtensions.cs @@ -22,7 +22,7 @@ using Microsoft.Extensions.DependencyInjection; using Org.Eclipse.TractusX.Portal.Backend.Framework.DateTimeProvider; -namespace Org.Eclipse.TractusX.Portal.Backend.Processes.Worker.Library; +namespace Processes.Worker.Library; public static class ProcessExecutionServiceExtensions { diff --git a/src/processes/Processes.Worker.Library/ProcessExecutionServiceSettings.cs b/src/processes/Processes.Worker.Library/ProcessExecutionServiceSettings.cs index 1508a17..3f31291 100644 --- a/src/processes/Processes.Worker.Library/ProcessExecutionServiceSettings.cs +++ b/src/processes/Processes.Worker.Library/ProcessExecutionServiceSettings.cs @@ -20,7 +20,7 @@ using System.ComponentModel.DataAnnotations; -namespace Org.Eclipse.TractusX.Portal.Backend.Processes.Worker.Library; +namespace Processes.Worker.Library; public class ProcessExecutionServiceSettings { diff --git a/src/processes/Processes.Worker.Library/ProcessExecutor.cs b/src/processes/Processes.Worker.Library/ProcessExecutor.cs index 96905e0..367a392 100644 --- a/src/processes/Processes.Worker.Library/ProcessExecutor.cs +++ b/src/processes/Processes.Worker.Library/ProcessExecutor.cs @@ -27,7 +27,7 @@ using System.Collections.Immutable; using System.Runtime.CompilerServices; -namespace Org.Eclipse.TractusX.Portal.Backend.Processes.Worker.Library; +namespace Processes.Worker.Library; public class ProcessExecutor : IProcessExecutor { diff --git a/src/processes/Processes.Worker/Program.cs b/src/processes/Processes.Worker/Program.cs index 1d26e66..0820d06 100644 --- a/src/processes/Processes.Worker/Program.cs +++ b/src/processes/Processes.Worker/Program.cs @@ -24,7 +24,7 @@ using Microsoft.Extensions.Hosting; using Org.Eclipse.TractusX.Portal.Backend.Framework.Logging; using Org.Eclipse.TractusX.Portal.Backend.Framework.Token; -using Org.Eclipse.TractusX.Portal.Backend.Processes.Worker.Library; +using Processes.Worker.Library; using Serilog; LoggingExtensions.EnsureInitialized(); diff --git a/src/processes/Processes.Worker/appsettings.json b/src/processes/Processes.Worker/appsettings.json index 1e79cbc..28679a6 100644 --- a/src/processes/Processes.Worker/appsettings.json +++ b/src/processes/Processes.Worker/appsettings.json @@ -24,24 +24,23 @@ "DimDb": "Server=placeholder;Database=placeholder;Port=5432;User Id=placeholder;Password=placeholder;Ssl Mode=Disable;" }, "Dim": { - "AdminMail": "", - "RootDirectoryId": "", - "ClientidCisCentral": "", - "ClientsecretCisCentral": "", - "AuthUrl": "" + "ApplicationName": "" }, - "SubAccount": { - "BaseUrl": "" - }, - "Entitlement": { - "BaseUrl": "" - }, - "Cf": { + "Provisioning": { "ClientId": "", "ClientSecret": "", "TokenAddress": "", "BaseUrl": "", - "GrantType": "" + "GrantType": "", + "EncryptionConfigIndex": 0, + "EncryptionConfigs": [ + { + "Index": 0, + "EncryptionKey": "", + "CipherMode": "", + "PaddingMode": "" + } + ] }, "Callback": { "Username": "", diff --git a/src/web/Dim.Web/BusinessLogic/DimBusinessLogic.cs b/src/web/Dim.Web/BusinessLogic/DimBusinessLogic.cs index a57dd66..e7d3676 100644 --- a/src/web/Dim.Web/BusinessLogic/DimBusinessLogic.cs +++ b/src/web/Dim.Web/BusinessLogic/DimBusinessLogic.cs @@ -18,23 +18,23 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -using Dim.Clients.Api.Cf; using Dim.Clients.Api.Dim; using Dim.Clients.Token; using Dim.DbAccess; +using Dim.DbAccess.Extensions; using Dim.DbAccess.Repositories; using Dim.Entities.Enums; using Dim.Web.ErrorHandling; using Dim.Web.Models; using Microsoft.Extensions.Options; using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; +using Org.Eclipse.TractusX.Portal.Backend.Framework.Models.Configuration; using System.Text.RegularExpressions; namespace Dim.Web.BusinessLogic; public class DimBusinessLogic( IDimRepositories dimRepositories, - ICfClient cfClient, IDimClient dimClient, IOptions options) : IDimBusinessLogic @@ -47,12 +47,12 @@ public async Task StartSetupDim(string companyName, string bpn, string didDocume var tenant = TenantName.Replace(companyName, string.Empty).TrimStart('-').TrimEnd('-').ToLower(); if (await dimRepositories.GetInstance().IsTenantExisting(companyName, bpn).ConfigureAwait(ConfigureAwaitOptions.None)) { - throw new ConflictException($"Tenant {companyName} with Bpn {bpn} already exists"); + throw ConflictException.Create(DimErrors.TENANT_ALREADY_EXISTS, new ErrorParameter[] { new("companyName", companyName), new("bpn", bpn) }); } var processStepRepository = dimRepositories.GetInstance(); var processId = processStepRepository.CreateProcess(ProcessTypeId.SETUP_DIM).Id; - processStepRepository.CreateProcessStep(ProcessStepTypeId.CREATE_SUBACCOUNT, ProcessStepStatusId.TODO, processId); + processStepRepository.CreateProcessStep(ProcessStepTypeId.CREATE_WALLET, ProcessStepStatusId.TODO, processId); dimRepositories.GetInstance().CreateTenant(tenant, bpn, didDocumentLocation, isIssuer, processId, _settings.OperatorId); @@ -61,7 +61,8 @@ public async Task StartSetupDim(string companyName, string bpn, string didDocume public async Task GetStatusList(string bpn, CancellationToken cancellationToken) { - var (exists, companyId, instanceId) = await dimRepositories.GetInstance().GetCompanyAndInstanceIdForBpn(bpn).ConfigureAwait(false); + var (exists, companyId, baseUrl, walletData) = await dimRepositories.GetInstance().GetCompanyAndWalletDataForBpn(bpn).ConfigureAwait(false); + var (tokenAddress, clientId, clientSecret, initializationVector, encryptionMode) = walletData.ValidateData(); if (!exists) { throw NotFoundException.Create(DimErrors.NO_COMPANY_FOR_BPN, new ErrorParameter[] { new("bpn", bpn) }); @@ -72,25 +73,27 @@ public async Task GetStatusList(string bpn, CancellationToken cancellati throw ConflictException.Create(DimErrors.NO_COMPANY_ID_SET); } - if (instanceId is null) + if (baseUrl is null) { - throw ConflictException.Create(DimErrors.NO_INSTANCE_ID_SET); + throw ConflictException.Create(DimErrors.NO_BASE_URL_SET); } - var dimDetails = await cfClient.GetServiceBindingDetails(instanceId.Value, cancellationToken).ConfigureAwait(false); + var cryptoHelper = _settings.EncryptionConfigs.GetCryptoHelper(encryptionMode); + var secret = cryptoHelper.Decrypt(clientSecret, initializationVector); + var dimAuth = new BasicAuthSettings { - TokenAddress = $"{dimDetails.Credentials.Uaa.Url}/oauth/token", - ClientId = dimDetails.Credentials.Uaa.ClientId, - ClientSecret = dimDetails.Credentials.Uaa.ClientSecret + TokenAddress = $"{tokenAddress}/oauth/token", + ClientId = clientId, + ClientSecret = secret }; - var dimBaseUrl = dimDetails.Credentials.Url; - return await dimClient.GetStatusList(dimAuth, dimBaseUrl, companyId.Value, cancellationToken).ConfigureAwait(false); + return await dimClient.GetStatusList(dimAuth, baseUrl, companyId.Value, cancellationToken).ConfigureAwait(false); } public async Task CreateStatusList(string bpn, CancellationToken cancellationToken) { - var (exists, companyId, instanceId) = await dimRepositories.GetInstance().GetCompanyAndInstanceIdForBpn(bpn).ConfigureAwait(false); + var (exists, companyId, baseUrl, walletData) = await dimRepositories.GetInstance().GetCompanyAndWalletDataForBpn(bpn).ConfigureAwait(false); + var (tokenAddress, clientId, clientSecret, initializationVector, encryptionMode) = walletData.ValidateData(); if (!exists) { throw NotFoundException.Create(DimErrors.NO_COMPANY_FOR_BPN, new ErrorParameter[] { new("bpn", bpn) }); @@ -101,20 +104,21 @@ public async Task CreateStatusList(string bpn, CancellationToken cancell throw ConflictException.Create(DimErrors.NO_COMPANY_ID_SET); } - if (instanceId is null) + if (baseUrl is null) { - throw ConflictException.Create(DimErrors.NO_INSTANCE_ID_SET); + throw ConflictException.Create(DimErrors.NO_BASE_URL_SET); } - var dimDetails = await cfClient.GetServiceBindingDetails(instanceId.Value, cancellationToken).ConfigureAwait(false); + var cryptoHelper = _settings.EncryptionConfigs.GetCryptoHelper(encryptionMode); + var secret = cryptoHelper.Decrypt(clientSecret, initializationVector); + var dimAuth = new BasicAuthSettings { - TokenAddress = $"{dimDetails.Credentials.Uaa.Url}/oauth/token", - ClientId = dimDetails.Credentials.Uaa.ClientId, - ClientSecret = dimDetails.Credentials.Uaa.ClientSecret + TokenAddress = $"{tokenAddress}/oauth/token", + ClientId = clientId, + ClientSecret = secret }; - var dimBaseUrl = dimDetails.Credentials.Url; - return await dimClient.CreateStatusList(dimAuth, dimBaseUrl, companyId.Value, cancellationToken).ConfigureAwait(false); + return await dimClient.CreateStatusList(dimAuth, baseUrl, companyId.Value, cancellationToken).ConfigureAwait(false); } public async Task CreateTechnicalUser(string bpn, TechnicalUserData technicalUserData) @@ -146,11 +150,17 @@ public async Task DeleteTechnicalUser(string bpn, TechnicalUserData technicalUse var processStepRepository = dimRepositories.GetInstance(); processStepRepository.CreateProcessStep(ProcessStepTypeId.DELETE_TECHNICAL_USER, ProcessStepStatusId.TODO, processId); - dimRepositories.GetInstance().AttachAndModifyTechnicalUser(technicalUserId, null, t => - { - t.ExternalId = technicalUserData.ExternalId; - t.ProcessId = processId; - }); + dimRepositories.GetInstance().AttachAndModifyTechnicalUser(technicalUserId, + t => + { + t.ExternalId = Guid.Empty; + t.ProcessId = processId; + }, + t => + { + t.ExternalId = technicalUserData.ExternalId; + t.ProcessId = processId; + }); await dimRepositories.SaveAsync().ConfigureAwait(false); } diff --git a/src/web/Dim.Web/BusinessLogic/DimSettings.cs b/src/web/Dim.Web/BusinessLogic/DimSettings.cs index 2800a08..da6d6bb 100644 --- a/src/web/Dim.Web/BusinessLogic/DimSettings.cs +++ b/src/web/Dim.Web/BusinessLogic/DimSettings.cs @@ -18,9 +18,17 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ +using Org.Eclipse.TractusX.Portal.Backend.Framework.Models.Configuration; +using Org.Eclipse.TractusX.Portal.Backend.Framework.Models.Validation; +using System.ComponentModel.DataAnnotations; + namespace Dim.Web.BusinessLogic; public class DimSettings { public Guid OperatorId { get; set; } + + [Required] + [DistinctValues("x => x.Index")] + public IEnumerable EncryptionConfigs { get; set; } = null!; } diff --git a/src/web/Dim.Web/ErrorHandling/DimErrorMessageContainer.cs b/src/web/Dim.Web/ErrorHandling/DimErrorMessageContainer.cs index 3843828..b8ced57 100644 --- a/src/web/Dim.Web/ErrorHandling/DimErrorMessageContainer.cs +++ b/src/web/Dim.Web/ErrorHandling/DimErrorMessageContainer.cs @@ -28,10 +28,12 @@ namespace Dim.Web.ErrorHandling; public class DimErrorMessageContainer : IErrorMessageContainer { private static readonly IReadOnlyDictionary _messageContainer = new Dictionary { + { DimErrors.TENANT_ALREADY_EXISTS, "Tenant {companyName} with Bpn {bpn} already exists" }, { DimErrors.NO_COMPANY_FOR_BPN, "No Tenant found for Bpn {bpn}" }, { DimErrors.NO_COMPANY_ID_SET, "No Company Id set" }, { DimErrors.NO_INSTANCE_ID_SET, "No Instnace Id set" }, { DimErrors.NO_TECHNICAL_USER_FOUND, "No Technical User found" }, + { DimErrors.NO_BASE_URL_SET, "No BaseUrl for the wallet set" }, }.ToImmutableDictionary(x => (int)x.Key, x => x.Value); public Type Type { get => typeof(DimErrors); } @@ -40,8 +42,10 @@ public class DimErrorMessageContainer : IErrorMessageContainer public enum DimErrors { + TENANT_ALREADY_EXISTS, NO_COMPANY_FOR_BPN, NO_COMPANY_ID_SET, NO_INSTANCE_ID_SET, - NO_TECHNICAL_USER_FOUND + NO_TECHNICAL_USER_FOUND, + NO_BASE_URL_SET, } diff --git a/src/web/Dim.Web/Extensions/ServiceCollectionExtensions.cs b/src/web/Dim.Web/Extensions/ServiceCollectionExtensions.cs index 9eca2df..122edae 100644 --- a/src/web/Dim.Web/Extensions/ServiceCollectionExtensions.cs +++ b/src/web/Dim.Web/Extensions/ServiceCollectionExtensions.cs @@ -28,7 +28,8 @@ public static IServiceCollection AddDim(this IServiceCollection services, IConfi { services.AddOptions() .Bind(section) - .ValidateOnStart(); + .ValidateOnStart() + .ValidateDataAnnotations(); return services; } } diff --git a/src/web/Dim.Web/Program.cs b/src/web/Dim.Web/Program.cs index 6b5e0be..f994df6 100644 --- a/src/web/Dim.Web/Program.cs +++ b/src/web/Dim.Web/Program.cs @@ -18,8 +18,8 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -using Dim.Clients.Api.Cf.DependencyInjection; using Dim.Clients.Api.Dim.DependencyInjection; +using Dim.Clients.Api.Div.DependencyInjection; using Dim.Clients.Token; using Dim.DbAccess.DependencyInjection; using Dim.Web.Authentication; @@ -39,7 +39,6 @@ await WebApplicationBuildRunner .AddTransient() .AddTransient() .AddDimClient() - .AddCfClient(builder.Configuration.GetSection("Cf")) .AddDim(builder.Configuration.GetSection("Dim")) .AddEndpointsApiExplorer() .AddDatabase(builder.Configuration) diff --git a/src/web/Dim.Web/appsettings.json b/src/web/Dim.Web/appsettings.json index ec589e9..0c5c620 100644 --- a/src/web/Dim.Web/appsettings.json +++ b/src/web/Dim.Web/appsettings.json @@ -10,15 +10,15 @@ "DimDb": "Server=placeholder;Database=placeholder;Port=5432;User Id=placeholder;Password=placeholder;Ssl Mode=Disable;" }, "Dim": { - "RootDirectoryId": "", - "OperatorId": "" - }, - "Cf": { - "ClientId": "", - "ClientSecret": "", - "TokenAddress": "", - "BaseUrl": "", - "GrantType": "" + "OperatorId": "", + "EncryptionConfigs": [ + { + "Index": 0, + "EncryptionKey": "", + "CipherMode": "", + "PaddingMode": "" + } + ] }, "SwaggerEnabled": false, "JwtBearerOptions": { diff --git a/tests/clients/Dim.Clients.Tests/Dim.Clients.Tests.csproj b/tests/clients/Dim.Clients.Tests/Dim.Clients.Tests.csproj new file mode 100644 index 0000000..af31a14 --- /dev/null +++ b/tests/clients/Dim.Clients.Tests/Dim.Clients.Tests.csproj @@ -0,0 +1,48 @@ + + + + + net8.0 + enable + enable + false + Dim.Clients.Tests + Dim.Clients.Tests + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + diff --git a/tests/clients/Dim.Clients.Tests/DimClientTests.cs b/tests/clients/Dim.Clients.Tests/DimClientTests.cs new file mode 100644 index 0000000..2b868f0 --- /dev/null +++ b/tests/clients/Dim.Clients.Tests/DimClientTests.cs @@ -0,0 +1,192 @@ +using AutoFixture; +using AutoFixture.AutoFakeItEasy; +using Dim.Clients.Api.Dim; +using Dim.Clients.Api.Dim.Models; +using Dim.Clients.Extensions; +using Dim.Clients.Tests.Extensions; +using Dim.Clients.Token; +using FakeItEasy; +using FluentAssertions; +using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; +using System.Net; +using System.Text.Json; +using Xunit; + +namespace Dim.Clients.Tests; + +public class DimClientTests +{ + #region Initialization + + private readonly IFixture _fixture; + + public DimClientTests() + { + _fixture = new Fixture().Customize(new AutoFakeItEasyCustomization { ConfigureMembers = true }); + _fixture.Behaviors.OfType().ToList() + .ForEach(b => _fixture.Behaviors.Remove(b)); + _fixture.Behaviors.Add(new OmitOnRecursionBehavior()); + } + + #endregion + + #region GetCompanyData + + [Fact] + public async Task GetCompanyData_WithNoContent_ThrowsServiceException() + { + // Arrange + _fixture.ConfigureTokenServiceFixture(new HttpResponseMessage(HttpStatusCode.OK)); + var sut = _fixture.Create(); + Task Act() => sut.GetCompanyData(_fixture.Create(), "https://example.org", "tenant", "app", CancellationToken.None); + + // Act + var ex = await Assert.ThrowsAsync(Act); + + // Assert + ex.Message.Should().Contain("The input does not contain any JSON tokens"); + } + + [Fact] + public async Task GetCompanyData_WithNoCompany_ThrowsConflictException() + { + // Arrange + var data = new CompanyIdentitiesResponse(Enumerable.Empty()); + _fixture.ConfigureTokenServiceFixture(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(JsonSerializer.Serialize(data, JsonSerializerExtensions.Options)) }); + var sut = _fixture.Create(); + Task Act() => sut.GetCompanyData(_fixture.Create(), "https://example.org", "tenant", "app", CancellationToken.None); + + // Act + var ex = await Assert.ThrowsAsync(Act); + + // Assert + ex.Message.Should().Be("There is no matching company"); + } + + [Fact] + public async Task GetCompanyData_WithValidData_ReturnsCompanyData() + { + // Arrange + var companyData = new CompanyData(Guid.NewGuid(), "https://example.org/download"); + var data = new CompanyIdentitiesResponse(Enumerable.Repeat(companyData, 1)); + _fixture.ConfigureTokenServiceFixture(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(JsonSerializer.Serialize(data, JsonSerializerExtensions.Options)) }); + var sut = _fixture.Create(); + + // Act + var result = await sut.GetCompanyData(_fixture.Create(), "https://example.org", "tenant", "app", CancellationToken.None); + + // Assert + result.CompanyId.Should().Be(companyData.CompanyId); + result.DownloadUrl.Should().Be(companyData.DownloadUrl); + } + + #endregion + + #region GetStatusList + + [Fact] + public async Task GetStatusList_WithNoContent_ThrowsServiceException() + { + // Arrange + _fixture.ConfigureTokenServiceFixture(new HttpResponseMessage(HttpStatusCode.OK)); + var sut = _fixture.Create(); + Task Act() => sut.GetStatusList(_fixture.Create(), "https://example.org", Guid.NewGuid(), CancellationToken.None); + + // Act + var ex = await Assert.ThrowsAsync(Act); + + // Assert + ex.Message.Should().Contain("The input does not contain any JSON tokens"); + } + + [Fact] + public async Task GetStatusList_WithNoSpaceLeft_ThrowsConflictException() + { + // Arrange + var data = new StatusListListResponse(1, new[] { new StatusListResponse(Guid.NewGuid().ToString(), "test", "test", "test", 1024, 0) }); + _fixture.ConfigureTokenServiceFixture(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(JsonSerializer.Serialize(data, JsonSerializerExtensions.Options)) }); + var sut = _fixture.Create(); + Task Act() => sut.GetStatusList(_fixture.Create(), "https://example.org", Guid.NewGuid(), CancellationToken.None); + + // Act + var ex = await Assert.ThrowsAsync(Act); + + // Assert + ex.Message.Should().Be("There is no status list with remaining space, please create a new one."); + } + + [Fact] + public async Task GetStatusList_WithValidData_ReturnsCompanyData() + { + // Arrange + var data = new StatusListListResponse(1, new[] { new StatusListResponse(Guid.NewGuid().ToString(), "test", "testCred", "test", 1024, 100) }); + _fixture.ConfigureTokenServiceFixture(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(JsonSerializer.Serialize(data, JsonSerializerExtensions.Options)) }); + var sut = _fixture.Create(); + + // Act + var result = await sut.GetStatusList(_fixture.Create(), "https://example.org", Guid.NewGuid(), CancellationToken.None); + + // Assert + result.Should().Be("testCred"); + } + + #endregion + + #region CreateStatusList + + [Fact] + public async Task CreateStatusList_WithNoContent_ThrowsServiceException() + { + // Arrange + HttpRequestMessage? request = null; + _fixture.ConfigureTokenServiceFixture( + new HttpResponseMessage(HttpStatusCode.OK), + requestMessage => request = requestMessage); + var sut = _fixture.Create(); + Task Act() => sut.CreateStatusList(_fixture.Create(), "https://example.org", Guid.NewGuid(), CancellationToken.None); + + // Act + var ex = await Assert.ThrowsAsync(Act); + + // Assert + ex.Message.Should().Contain("The input does not contain any JSON tokens"); + } + + [Fact] + public async Task CreateStatusList_WithNoSpaceLeft_ThrowsConflictException() + { + // Arrange + _fixture.ConfigureTokenServiceFixture(new HttpResponseMessage(HttpStatusCode.BadRequest)); + var sut = _fixture.Create(); + Task Act() => sut.CreateStatusList(_fixture.Create(), "https://example.org", Guid.NewGuid(), CancellationToken.None); + + // Act + var ex = await Assert.ThrowsAsync(Act); + + // Assert + ex.Message.Should().Be("call to external system assign-application failed with statuscode 400"); + } + + [Fact] + public async Task CreateStatusList_WithValidData_ReturnsCompanyData() + { + // Arrange + var recovationVc = Guid.NewGuid().ToString(); + var data = new CreateStatusListResponse(Guid.NewGuid(), new RevocationVc(recovationVc)); + + _fixture.ConfigureTokenServiceFixture( + new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(JsonSerializer.Serialize(data, JsonSerializerExtensions.Options)) + }); + var sut = _fixture.Create(); + + // Act + var result = await sut.CreateStatusList(_fixture.Create(), "https://example.org", Guid.NewGuid(), CancellationToken.None); + + // Assert + result.Should().Be(recovationVc); + } + + #endregion +} diff --git a/tests/clients/Dim.Clients.Tests/Extensions/AutoFixtureExtensions.cs b/tests/clients/Dim.Clients.Tests/Extensions/AutoFixtureExtensions.cs new file mode 100644 index 0000000..44d787c --- /dev/null +++ b/tests/clients/Dim.Clients.Tests/Extensions/AutoFixtureExtensions.cs @@ -0,0 +1,27 @@ +using AutoFixture; +using Dim.Clients.Token; +using FakeItEasy; + +namespace Dim.Clients.Tests.Extensions; + +public static class AutoFixtureExtensions +{ + public static void ConfigureTokenServiceFixture(this IFixture fixture, HttpResponseMessage httpResponseMessage, Action? setMessage = null) + { + var messageHandler = A.Fake(); + A.CallTo(messageHandler) // mock protected method + .Where(x => x.Method.Name == "SendAsync") + .WithReturnType>() + .ReturnsLazily(call => + { + var message = call.Arguments.Get(0); + setMessage?.Invoke(message); + return Task.FromResult(httpResponseMessage); + }); + var httpClient = new HttpClient(messageHandler) { BaseAddress = new Uri("https://example.com/path/test/") }; + fixture.Inject(httpClient); + + var tokenService = fixture.Freeze>(); + A.CallTo(() => tokenService.FakedObject.GetBasicAuthorizedClient(A._, A._)).Returns(httpClient); + } +} diff --git a/tests/clients/Dim.Clients.Tests/ProvisioningClientTests.cs b/tests/clients/Dim.Clients.Tests/ProvisioningClientTests.cs new file mode 100644 index 0000000..2346b9a --- /dev/null +++ b/tests/clients/Dim.Clients.Tests/ProvisioningClientTests.cs @@ -0,0 +1,139 @@ +using AutoFixture; +using AutoFixture.AutoFakeItEasy; +using Dim.Clients.Api.Dim; +using Dim.Clients.Api.Dim.Models; +using Dim.Clients.Api.Div; +using Dim.Clients.Api.Div.Models; +using Dim.Clients.Extensions; +using Dim.Clients.Tests.Extensions; +using Dim.Clients.Token; +using FakeItEasy; +using FluentAssertions; +using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; +using System.Net; +using System.Text.Json; +using Xunit; + +namespace Dim.Clients.Tests; + +public class ProvisioningClientTests +{ + #region Initialization + + private readonly IFixture _fixture; + + public ProvisioningClientTests() + { + _fixture = new Fixture().Customize(new AutoFakeItEasyCustomization { ConfigureMembers = true }); + _fixture.Behaviors.OfType().ToList() + .ForEach(b => _fixture.Behaviors.Remove(b)); + _fixture.Behaviors.Add(new OmitOnRecursionBehavior()); + } + + #endregion + + #region GetOperation + + [Fact] + public async Task GetOperation_WithStatePending_Returns() + { + // Arrange + var data = new OperationResponse(Guid.NewGuid(), OperationResponseStatus.pending, null, null, null); + _fixture.ConfigureTokenServiceFixture(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(JsonSerializer.Serialize(data, JsonSerializerExtensions.Options)) }); + var sut = _fixture.Create(); + + // Act + var response = await sut.GetOperation(Guid.NewGuid(), CancellationToken.None); + + // Assert + response.Should().Be(data); + } + + [Fact] + public async Task GetOperation_WithStatusFailed_ThrowsServiceException() + { + // Arrange + var data = new OperationResponse(Guid.NewGuid(), OperationResponseStatus.failed, null, "test error", null); + _fixture.ConfigureTokenServiceFixture(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(JsonSerializer.Serialize(data, JsonSerializerExtensions.Options)) }); + var sut = _fixture.Create(); + Task Act() => sut.GetOperation(Guid.NewGuid(), CancellationToken.None); + + // Act + var ex = await Assert.ThrowsAsync(Act); + + // Assert + ex.Message.Should().Be("Operation Creation failed with error: test error"); + } + + [Fact] + public async Task GetOperation_WithValidData_ReturnsCompanyData() + { + // Arrange + var data = new OperationResponse(Guid.NewGuid(), OperationResponseStatus.completed, null, null, _fixture.Create()); + _fixture.ConfigureTokenServiceFixture(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(JsonSerializer.Serialize(data, JsonSerializerExtensions.Options)) }); + var sut = _fixture.Create(); + + // Act + var result = await sut.GetOperation(Guid.NewGuid(), CancellationToken.None); + + // Assert + result.Should().Be(data); + } + + #endregion + + #region CreateOperation + + [Fact] + public async Task CreateOperation_WithNoContent_ThrowsServiceException() + { + // Arrange + _fixture.ConfigureTokenServiceFixture(new HttpResponseMessage(HttpStatusCode.OK)); + var sut = _fixture.Create(); + Task Act() => sut.CreateOperation(Guid.NewGuid(), "corp", "application1", "test corp", "https://example.org/did", false, CancellationToken.None); + + // Act + var ex = await Assert.ThrowsAsync(Act); + + // Assert + ex.Message.Should().Contain("The input does not contain any JSON tokens"); + } + + [Fact] + public async Task CreateOperation_WithNoSpaceLeft_ThrowsConflictException() + { + // Arrange + _fixture.ConfigureTokenServiceFixture(new HttpResponseMessage(HttpStatusCode.BadRequest)); + var sut = _fixture.Create(); + Task Act() => sut.CreateOperation(Guid.NewGuid(), "corp", "application1", "test corp", "https://example.org/did", false, CancellationToken.None); + + // Act + var ex = await Assert.ThrowsAsync(Act); + + // Assert + ex.Message.Should().Be("call to external system create-operation failed with statuscode 400"); + } + + [Fact] + public async Task CreateOperation_WithValidData_ReturnsCompanyData() + { + // Arrange + var operationId = Guid.NewGuid(); + var data = new CreateOperationRequest(operationId); + + _fixture.ConfigureTokenServiceFixture( + new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(JsonSerializer.Serialize(data, JsonSerializerExtensions.Options)) + }); + var sut = _fixture.Create(); + + // Act + var result = await sut.CreateOperation(Guid.NewGuid(), "corp", "application1", "test corp", "https://example.org/did", false, CancellationToken.None); + + // Assert + result.Should().Be(operationId); + } + + #endregion +} diff --git a/tests/database/Dim.DbAccess.Tests/Dim.DbAccess.Tests.csproj b/tests/database/Dim.DbAccess.Tests/Dim.DbAccess.Tests.csproj new file mode 100644 index 0000000..d3cd159 --- /dev/null +++ b/tests/database/Dim.DbAccess.Tests/Dim.DbAccess.Tests.csproj @@ -0,0 +1,63 @@ + + + + + Dim.DbAccess.Tests + Dim.DbAccess.Tests + net8.0 + enable + enable + false + + + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + + + + + + + + diff --git a/tests/database/Dim.DbAccess.Tests/DimRepositoriesTests.cs b/tests/database/Dim.DbAccess.Tests/DimRepositoriesTests.cs new file mode 100644 index 0000000..a289c90 --- /dev/null +++ b/tests/database/Dim.DbAccess.Tests/DimRepositoriesTests.cs @@ -0,0 +1,139 @@ +/******************************************************************************** + * Copyright (c) 2024 BMW Group AG + * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +using AutoFixture; +using AutoFixture.AutoFakeItEasy; +using Dim.DbAccess.Repositories; +using Dim.DbAccess.Tests.Setup; +using Dim.Entities; +using Dim.Entities.Entities; +using Dim.Entities.Enums; +using FluentAssertions; +using Microsoft.EntityFrameworkCore; +using Xunit; +using Xunit.Extensions.AssemblyFixture; + +namespace Dim.DbAccess.Tests; + +public class DimRepositoriesTests : IAssemblyFixture +{ + private readonly TestDbFixture _dbTestDbFixture; + + public DimRepositoriesTests(TestDbFixture testDbFixture) + { + var fixture = new Fixture().Customize(new AutoFakeItEasyCustomization { ConfigureMembers = true }); + fixture.Behaviors.OfType().ToList() + .ForEach(b => fixture.Behaviors.Remove(b)); + + fixture.Behaviors.Add(new OmitOnRecursionBehavior()); + _dbTestDbFixture = testDbFixture; + } + + #region GetInstance + + [Fact] + public async Task GetInstance_TenantRepository_CreatesSuccessfully() + { + // Arrange + var sut = await CreateSut(); + + // Act + var result = sut.GetInstance(); + + // Assert + result.Should().BeOfType(); + } + + [Fact] + public async Task GetInstance_ProcessStepRepository_CreatesSuccessfully() + { + // Arrange + var sut = await CreateSut(); + + // Act + var result = sut.GetInstance(); + + // Assert + result.Should().BeOfType(); + } + + #endregion + + #region Clear + + [Fact] + public async Task Clear_CreateSuccessfully() + { + // Arrange + var (sut, dbContext) = await CreateSutWithContext(); + var changeTracker = dbContext.ChangeTracker; + dbContext.Processes.Add(new Process(Guid.NewGuid(), ProcessTypeId.SETUP_DIM, Guid.NewGuid())); + + // Act + sut.Clear(); + + // Assert + changeTracker.HasChanges().Should().BeFalse(); + changeTracker.Entries().Should().BeEmpty(); + } + + #endregion + + #region Attach + + [Fact] + public async Task Attach_CreateSuccessfully() + { + // Arrange + var (sut, dbContext) = await CreateSutWithContext(); + var changeTracker = dbContext.ChangeTracker; + var now = DateTimeOffset.Now; + + // Act + sut.Attach(new Process(new Guid("dd371565-9489-4907-a2e4-b8cbfe7a8cd2"), default, Guid.Empty), p => + { + p.LockExpiryDate = now; + p.ProcessTypeId = ProcessTypeId.SETUP_DIM; + }); + + // Assert + changeTracker.HasChanges().Should().BeTrue(); + changeTracker.Entries().Should() + .ContainSingle() + .Which.State.Should().Be(EntityState.Modified); + changeTracker.Entries().Select(x => x.Entity).Cast() + .Should().Satisfy(x => x.ProcessTypeId == ProcessTypeId.SETUP_DIM); + } + + #endregion + + private async Task<(DimRepositories sut, DimDbContext dbContext)> CreateSutWithContext() + { + var context = await _dbTestDbFixture.GetDbContext(); + var sut = new DimRepositories(context); + return (sut, context); + } + + private async Task CreateSut() + { + var context = await _dbTestDbFixture.GetDbContext(); + return new DimRepositories(context); + } +} diff --git a/tests/database/Dim.DbAccess.Tests/Extensions/WalletDataExtensionsTests.cs b/tests/database/Dim.DbAccess.Tests/Extensions/WalletDataExtensionsTests.cs new file mode 100644 index 0000000..26463bf --- /dev/null +++ b/tests/database/Dim.DbAccess.Tests/Extensions/WalletDataExtensionsTests.cs @@ -0,0 +1,81 @@ +using Dim.DbAccess.Extensions; +using Dim.DbAccess.Models; +using FluentAssertions; +using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; +using System.Text; +using Xunit; + +namespace Dim.DbAccess.Tests.Extensions; + +public class WalletDataExtensionsTests +{ + [Fact] + public void ValidateData_WithMissingTokenAddress_ThrowsConflictException() + { + // Arrange + var walletData = new WalletData(null, null, null, null, null); + void Act() => walletData.ValidateData(); + + // Act + var ex = Assert.Throws(Act); + + // Assert + ex.Message.Should().Be("TokenAddress must not be null"); + } + + [Fact] + public void ValidateData_WithMissingClientId_ThrowsConflictException() + { + // Arrange + var walletData = new WalletData("https://example.org/token", null, null, null, null); + void Act() => walletData.ValidateData(); + + // Act + var ex = Assert.Throws(Act); + + // Assert + ex.Message.Should().Be("ClientId must not be null"); + } + + [Fact] + public void ValidateData_WithMissingSecret_ThrowsConflictException() + { + // Arrange + var walletData = new WalletData("https://example.org/token", "cl1", null, null, null); + void Act() => walletData.ValidateData(); + + // Act + var ex = Assert.Throws(Act); + + // Assert + ex.Message.Should().Be("Secret must not be null"); + } + + [Fact] + public void ValidateData_WithMissingVector_ThrowsConflictException() + { + // Arrange + var walletData = new WalletData("https://example.org/token", "cl1", "test"u8.ToArray(), null, null); + void Act() => walletData.ValidateData(); + + // Act + var ex = Assert.Throws(Act); + + // Assert + ex.Message.Should().Be("Vector must not be null"); + } + + [Fact] + public void ValidateData_WithMissingMode_ThrowsConflictException() + { + // Arrange + var walletData = new WalletData("https://example.org/token", "cl1", "test"u8.ToArray(), "test"u8.ToArray(), null); + void Act() => walletData.ValidateData(); + + // Act + var ex = Assert.Throws(Act); + + // Assert + ex.Message.Should().Be("EncryptionMode must not be null"); + } +} diff --git a/tests/database/Dim.DbAccess.Tests/ProcessStepRepositoryTests.cs b/tests/database/Dim.DbAccess.Tests/ProcessStepRepositoryTests.cs new file mode 100644 index 0000000..ffef165 --- /dev/null +++ b/tests/database/Dim.DbAccess.Tests/ProcessStepRepositoryTests.cs @@ -0,0 +1,358 @@ +/******************************************************************************** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +using AutoFixture; +using AutoFixture.AutoFakeItEasy; +using Dim.DbAccess.Repositories; +using Dim.DbAccess.Tests.Setup; +using Dim.Entities; +using Dim.Entities.Entities; +using Dim.Entities.Enums; +using FluentAssertions; +using Microsoft.EntityFrameworkCore; +using System.Collections.Immutable; +using Xunit; +using Xunit.Extensions.AssemblyFixture; + +namespace Dim.DbAccess.Tests; + +public class ProcessStepRepositoryTests : IAssemblyFixture +{ + private readonly IFixture _fixture; + private readonly TestDbFixture _dbTestDbFixture; + + public ProcessStepRepositoryTests(TestDbFixture testDbFixture) + { + _fixture = new Fixture().Customize(new AutoFakeItEasyCustomization { ConfigureMembers = true }); + _fixture.Behaviors.OfType().ToList() + .ForEach(b => _fixture.Behaviors.Remove(b)); + + _fixture.Behaviors.Add(new OmitOnRecursionBehavior()); + _dbTestDbFixture = testDbFixture; + } + + #region CreateProcess + + [Fact] + public async Task CreateProcess_CreatesSuccessfully() + { + // Arrange + var (sut, dbContext) = await CreateSutWithContext(); + var changeTracker = dbContext.ChangeTracker; + + // Act + var result = sut.CreateProcess(ProcessTypeId.SETUP_DIM); + + // Assert + changeTracker.HasChanges().Should().BeTrue(); + changeTracker.Entries().Should().HaveCount(1) + .And.AllSatisfy(x => + { + x.State.Should().Be(EntityState.Added); + x.Entity.Should().BeOfType(); + }); + changeTracker.Entries().Select(x => x.Entity).Cast() + .Should().Satisfy( + x => x.Id == result.Id && x.ProcessTypeId == ProcessTypeId.SETUP_DIM + ); + } + + #endregion + + #region CreateProcessStepRange + + [Fact] + public async Task CreateProcessStepRange_CreateSuccessfully() + { + // Arrange + var processId = Guid.NewGuid(); + var processStepTypeIds = _fixture.CreateMany(3).ToImmutableArray(); + var (sut, dbContext) = await CreateSutWithContext(); + var changeTracker = dbContext.ChangeTracker; + + // Act + var result = sut.CreateProcessStepRange(processStepTypeIds.Select(processStepTypeId => (processStepTypeId, ProcessStepStatusId.TODO, processId))); + + // Assert + changeTracker.HasChanges().Should().BeTrue(); + changeTracker.Entries().Should() + .HaveSameCount(processStepTypeIds) + .And.AllSatisfy(x => + { + x.State.Should().Be(EntityState.Added); + x.Entity.Should().BeOfType(); + }); + changeTracker.Entries().Select(x => x.Entity).Cast() + .Should().Satisfy( + x => x.Id == result.ElementAt(0).Id && x.ProcessId == processId && x.ProcessStepTypeId == processStepTypeIds[0] && x.ProcessStepStatusId == ProcessStepStatusId.TODO, + x => x.Id == result.ElementAt(1).Id && x.ProcessId == processId && x.ProcessStepTypeId == processStepTypeIds[1] && x.ProcessStepStatusId == ProcessStepStatusId.TODO, + x => x.Id == result.ElementAt(2).Id && x.ProcessId == processId && x.ProcessStepTypeId == processStepTypeIds[2] && x.ProcessStepStatusId == ProcessStepStatusId.TODO + ); + } + + #endregion + + #region CreateProcessStep + + [Fact] + public async Task CreateProcessStep_CreateSuccessfully() + { + // Arrange + var processId = Guid.NewGuid(); + var (sut, dbContext) = await CreateSutWithContext(); + var changeTracker = dbContext.ChangeTracker; + + // Act + sut.CreateProcessStep(ProcessStepTypeId.CREATE_WALLET, ProcessStepStatusId.TODO, processId); + + // Assert + changeTracker.HasChanges().Should().BeTrue(); + changeTracker.Entries().Should() + .ContainSingle() + .Which.State.Should().Be(EntityState.Added); + changeTracker.Entries().Select(x => x.Entity).Cast() + .Should().Satisfy( + x => x.ProcessId == processId && x.ProcessStepTypeId == ProcessStepTypeId.CREATE_WALLET && x.ProcessStepStatusId == ProcessStepStatusId.TODO + ); + } + + #endregion + + #region AttachAndModifyProcessStep + + [Fact] + public async Task AttachAndModifyProcessStep_WithExistingProcessStep_UpdatesStatus() + { + // Arrange + var (sut, dbContext) = await CreateSutWithContext(); + + // Act + sut.AttachAndModifyProcessStep(new Guid("48f35f84-8d98-4fbd-ba80-8cbce5eeadb5"), + existing => + { + existing.ProcessStepStatusId = ProcessStepStatusId.TODO; + }, + modify => + { + modify.ProcessStepStatusId = ProcessStepStatusId.DONE; + } + ); + + // Assert + var changeTracker = dbContext.ChangeTracker; + var changedEntries = changeTracker.Entries().ToList(); + changeTracker.HasChanges().Should().BeTrue(); + changedEntries.Should().NotBeEmpty(); + changedEntries.Should().HaveCount(1); + var changedEntity = changedEntries.Single(); + changedEntity.State.Should().Be(EntityState.Modified); + changedEntity.Entity.Should().BeOfType().Which.ProcessStepStatusId.Should().Be(ProcessStepStatusId.DONE); + } + + #endregion + + #region AttachAndModifyProcessSteps + + [Fact] + public async Task AttachAndModifyProcessSteps_UpdatesStatus() + { + // Arrange + var stepData = _fixture.CreateMany<(Guid ProcessStepId, ProcessStep InitialStep, ProcessStep ModifiedStep)>(5).ToImmutableArray(); + + var (sut, dbContext) = await CreateSutWithContext(); + + // Act + sut.AttachAndModifyProcessSteps(stepData.Select(data => new ValueTuple?, Action>( + data.ProcessStepId, + step => + { + step.ProcessStepStatusId = data.InitialStep.ProcessStepStatusId; + step.DateLastChanged = data.InitialStep.DateLastChanged; + step.Message = data.InitialStep.Message; + }, + step => + { + step.ProcessStepStatusId = data.ModifiedStep.ProcessStepStatusId; + step.DateLastChanged = data.ModifiedStep.DateLastChanged; + step.Message = data.ModifiedStep.Message; + }))); + + // Assert + var changeTracker = dbContext.ChangeTracker; + var changedEntries = changeTracker.Entries().ToList(); + changeTracker.HasChanges().Should().BeTrue(); + changedEntries.Should().HaveCount(5).And.AllSatisfy(entry => entry.State.Should().Be(EntityState.Modified)); + changedEntries.Select(entry => entry.Entity).Should().AllBeOfType().Which.Should().Satisfy( + step => step.Id == stepData[0].ProcessStepId && step.ProcessStepStatusId == stepData[0].ModifiedStep.ProcessStepStatusId && step.DateLastChanged == stepData[0].ModifiedStep.DateLastChanged && step.Message == stepData[0].ModifiedStep.Message, + step => step.Id == stepData[1].ProcessStepId && step.ProcessStepStatusId == stepData[1].ModifiedStep.ProcessStepStatusId && step.DateLastChanged == stepData[1].ModifiedStep.DateLastChanged && step.Message == stepData[1].ModifiedStep.Message, + step => step.Id == stepData[2].ProcessStepId && step.ProcessStepStatusId == stepData[2].ModifiedStep.ProcessStepStatusId && step.DateLastChanged == stepData[2].ModifiedStep.DateLastChanged && step.Message == stepData[2].ModifiedStep.Message, + step => step.Id == stepData[3].ProcessStepId && step.ProcessStepStatusId == stepData[3].ModifiedStep.ProcessStepStatusId && step.DateLastChanged == stepData[3].ModifiedStep.DateLastChanged && step.Message == stepData[3].ModifiedStep.Message, + step => step.Id == stepData[4].ProcessStepId && step.ProcessStepStatusId == stepData[4].ModifiedStep.ProcessStepStatusId && step.DateLastChanged == stepData[4].ModifiedStep.DateLastChanged && step.Message == stepData[4].ModifiedStep.Message + ); + } + + [Fact] + public async Task AttachAndModifyProcessSteps_WithUnmodifiedData_SkipsUpdateStatus() + { + // Arrange + var stepData = _fixture.CreateMany<(Guid ProcessStepId, ProcessStep InitialStep)>(5).ToImmutableArray(); + + var (sut, dbContext) = await CreateSutWithContext(); + + // Act + sut.AttachAndModifyProcessSteps(stepData.Select(data => new ValueTuple?, Action>( + data.ProcessStepId, + step => + { + step.ProcessStepStatusId = data.InitialStep.ProcessStepStatusId; + step.DateLastChanged = data.InitialStep.DateLastChanged; + step.Message = data.InitialStep.Message; + }, + step => + { + step.DateLastChanged = data.InitialStep.DateLastChanged; + }))); + + // Assert + var changeTracker = dbContext.ChangeTracker; + var changedEntries = changeTracker.Entries().ToList(); + changeTracker.HasChanges().Should().BeFalse(); + changedEntries.Should().HaveCount(5).And.AllSatisfy(entry => entry.State.Should().Be(EntityState.Unchanged)); + changedEntries.Select(entry => entry.Entity).Should().AllBeOfType().Which.Should().Satisfy( + step => step.Id == stepData[0].ProcessStepId && step.ProcessStepStatusId == stepData[0].InitialStep.ProcessStepStatusId && step.DateLastChanged == stepData[0].InitialStep.DateLastChanged && step.Message == stepData[0].InitialStep.Message, + step => step.Id == stepData[1].ProcessStepId && step.ProcessStepStatusId == stepData[1].InitialStep.ProcessStepStatusId && step.DateLastChanged == stepData[1].InitialStep.DateLastChanged && step.Message == stepData[1].InitialStep.Message, + step => step.Id == stepData[2].ProcessStepId && step.ProcessStepStatusId == stepData[2].InitialStep.ProcessStepStatusId && step.DateLastChanged == stepData[2].InitialStep.DateLastChanged && step.Message == stepData[2].InitialStep.Message, + step => step.Id == stepData[3].ProcessStepId && step.ProcessStepStatusId == stepData[3].InitialStep.ProcessStepStatusId && step.DateLastChanged == stepData[3].InitialStep.DateLastChanged && step.Message == stepData[3].InitialStep.Message, + step => step.Id == stepData[4].ProcessStepId && step.ProcessStepStatusId == stepData[4].InitialStep.ProcessStepStatusId && step.DateLastChanged == stepData[4].InitialStep.DateLastChanged && step.Message == stepData[4].InitialStep.Message + ); + } + + [Fact] + public async Task AttachAndModifyProcessSteps_WithUnmodifiedData_UpdatesLastChanged() + { + // Arrange + var stepData = _fixture.CreateMany<(Guid ProcessStepId, ProcessStep InitialStep)>(5).ToImmutableArray(); + + var (sut, dbContext) = await CreateSutWithContext(); + + // Act + sut.AttachAndModifyProcessSteps(stepData.Select(data => new ValueTuple?, Action>( + data.ProcessStepId, + step => + { + step.ProcessStepStatusId = data.InitialStep.ProcessStepStatusId; + step.DateLastChanged = data.InitialStep.DateLastChanged; + step.Message = data.InitialStep.Message; + }, + _ => { }))); + + // Assert + var changeTracker = dbContext.ChangeTracker; + var changedEntries = changeTracker.Entries().ToList(); + changeTracker.HasChanges().Should().BeTrue(); + changedEntries.Should().HaveCount(5).And.AllSatisfy(entry => entry.State.Should().Be(EntityState.Modified)); + changedEntries.Select(entry => entry.Entity).Should().AllBeOfType().Which.Should().Satisfy( + step => step.Id == stepData[0].ProcessStepId && step.ProcessStepStatusId == stepData[0].InitialStep.ProcessStepStatusId && step.DateLastChanged != stepData[0].InitialStep.DateLastChanged && step.Message == stepData[0].InitialStep.Message, + step => step.Id == stepData[1].ProcessStepId && step.ProcessStepStatusId == stepData[1].InitialStep.ProcessStepStatusId && step.DateLastChanged != stepData[1].InitialStep.DateLastChanged && step.Message == stepData[1].InitialStep.Message, + step => step.Id == stepData[2].ProcessStepId && step.ProcessStepStatusId == stepData[2].InitialStep.ProcessStepStatusId && step.DateLastChanged != stepData[2].InitialStep.DateLastChanged && step.Message == stepData[2].InitialStep.Message, + step => step.Id == stepData[3].ProcessStepId && step.ProcessStepStatusId == stepData[3].InitialStep.ProcessStepStatusId && step.DateLastChanged != stepData[3].InitialStep.DateLastChanged && step.Message == stepData[3].InitialStep.Message, + step => step.Id == stepData[4].ProcessStepId && step.ProcessStepStatusId == stepData[4].InitialStep.ProcessStepStatusId && step.DateLastChanged != stepData[4].InitialStep.DateLastChanged && step.Message == stepData[4].InitialStep.Message + ); + } + + #endregion + + #region GetActiveProcesses + + [Fact] + public async Task GetActiveProcess_LockExpired_ReturnsExpected() + { + // Arrange + var processTypeIds = new[] { ProcessTypeId.SETUP_DIM }; + var processStepTypeIds = new[] { + ProcessStepTypeId.CREATE_WALLET, + ProcessStepTypeId.CHECK_OPERATION, + ProcessStepTypeId.GET_COMPANY, + ProcessStepTypeId.GET_DID_DOCUMENT + }; + + var sut = await CreateSut(); + + // Act + var result = await sut.GetActiveProcesses(processTypeIds, processStepTypeIds, DateTimeOffset.UtcNow).ToListAsync(); + result.Should().HaveCount(1) + .And.Satisfy( + x => x.Id == new Guid("dd371565-9489-4907-a2e4-b8cbfe7a8cd2") && x.ProcessTypeId == ProcessTypeId.SETUP_DIM && x.LockExpiryDate == null + ); + } + + [Fact] + public async Task GetActiveProcess_Locked_ReturnsExpected() + { + // Arrange + var processTypeIds = new[] { ProcessTypeId.SETUP_DIM }; + var processStepTypeIds = new[] { + ProcessStepTypeId.CREATE_WALLET, + ProcessStepTypeId.CHECK_OPERATION, + ProcessStepTypeId.GET_COMPANY, + ProcessStepTypeId.GET_DID_DOCUMENT + }; + + var sut = await CreateSut(); + + // Act + var result = await sut.GetActiveProcesses(processTypeIds, processStepTypeIds, DateTimeOffset.UtcNow).ToListAsync(); + result.Should().HaveCount(1); + } + + #endregion + + #region GetProcessStepData + + [Fact] + public async Task GetProcessStepData_ReturnsExpected() + { + // Arrange + var processId = new Guid("dd371565-9489-4907-a2e4-b8cbfe7a8cd2"); + var sut = await CreateSut(); + + // Act + var result = await sut.GetProcessStepData(processId).ToListAsync(); + result.Should().HaveCount(2) + .And.Satisfy( + x => x.ProcessStepId == new Guid("80771e4a-0d69-43b8-b278-25884da7f97d") && x.ProcessStepTypeId == ProcessStepTypeId.CREATE_WALLET, + x => x.ProcessStepId == new Guid("cd231cb8-55de-4ae4-b93f-d440512341fb") && x.ProcessStepTypeId == ProcessStepTypeId.GET_COMPANY + ); + } + + #endregion + + private async Task<(ProcessStepRepository sut, DimDbContext dbContext)> CreateSutWithContext() + { + var context = await _dbTestDbFixture.GetDbContext(); + var sut = new ProcessStepRepository(context); + return (sut, context); + } + + private async Task CreateSut() + { + var context = await _dbTestDbFixture.GetDbContext(); + return new ProcessStepRepository(context); + } +} diff --git a/tests/database/Dim.DbAccess.Tests/Seeder/BatchInsertSeeder.cs b/tests/database/Dim.DbAccess.Tests/Seeder/BatchInsertSeeder.cs new file mode 100644 index 0000000..81c4af1 --- /dev/null +++ b/tests/database/Dim.DbAccess.Tests/Seeder/BatchInsertSeeder.cs @@ -0,0 +1,92 @@ +/******************************************************************************** + * Copyright (c) 2024 BMW Group AG + * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +using Dim.Entities; +using Dim.Entities.Entities; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Org.Eclipse.TractusX.Portal.Backend.Framework.Seeding; + +namespace Dim.DbAccess.Tests.Seeder; + +/// +/// Seeder to seed the base entities (those with an id as primary key) +/// +public class BatchInsertSeeder : ICustomSeeder +{ + private readonly DimDbContext _context; + private readonly ILogger _logger; + private readonly SeederSettings _settings; + + /// + /// Constructor + /// + /// The database context + /// The logger + /// The options + public BatchInsertSeeder(DimDbContext context, ILogger logger, IOptions options) + { + _context = context; + _logger = logger; + _settings = options.Value; + } + + /// + public int Order => 1; + + /// + public async Task ExecuteAsync(CancellationToken cancellationToken) + { + if (!_settings.DataPaths.Any()) + { + _logger.LogInformation("There a no data paths configured, therefore the {SeederName} will be skipped", nameof(BatchInsertSeeder)); + return; + } + + _logger.LogInformation("Start BaseEntityBatch Seeder"); + await SeedTable("tenants", x => x.Id, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); + await SeedTable("process_steps", x => x.Id, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); + await SeedTable("processes", x => x.Id, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); + await SeedTable("technical_users", x => x.Id, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); + + await _context.SaveChangesAsync(cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); + _logger.LogInformation("Finished BaseEntityBatch Seeder"); + } + + private async Task SeedTable(string fileName, Func keySelector, CancellationToken cancellationToken) where T : class + { + _logger.LogInformation("Start seeding {Filename}", fileName); + var additionalEnvironments = _settings.TestDataEnvironments ?? Enumerable.Empty(); + var data = await SeederHelper.GetSeedData(_logger, fileName, _settings.DataPaths, cancellationToken, additionalEnvironments.ToArray()).ConfigureAwait(ConfigureAwaitOptions.None); + _logger.LogInformation("Found {ElementCount} data", data.Count); + if (data.Any()) + { + var typeName = typeof(T).Name; + _logger.LogInformation("Started to Seed {TableName}", typeName); + data = data.GroupJoin(_context.Set(), keySelector, keySelector, (d, dbEntry) => new { d, dbEntry }) + .SelectMany(t => t.dbEntry.DefaultIfEmpty(), (t, x) => new { t, x }) + .Where(t => t.x == null) + .Select(t => t.t.d).ToList(); + _logger.LogInformation("Seeding {DataCount} {TableName}", data.Count, typeName); + await _context.Set().AddRangeAsync(data, cancellationToken).ConfigureAwait(ConfigureAwaitOptions.None); + _logger.LogInformation("Seeded {TableName}", typeName); + } + } +} diff --git a/tests/database/Dim.DbAccess.Tests/Seeder/Data/process_steps.json b/tests/database/Dim.DbAccess.Tests/Seeder/Data/process_steps.json new file mode 100644 index 0000000..e8c04f4 --- /dev/null +++ b/tests/database/Dim.DbAccess.Tests/Seeder/Data/process_steps.json @@ -0,0 +1,50 @@ +[ + { + "id": "5c9a4f56-0609-49a5-ab86-dd8f93dfd3fa", + "process_step_type_id": 1, + "process_step_status_id": 2, + "process_id": "dd371565-9489-4907-a2e4-b8cbfe7a8cd1", + "date_created": "2023-02-21 08:15:20.479000 +00:00", + "date_last_changed": null + }, + { + "id": "80771e4a-0d69-43b8-b278-25884da7f97d", + "process_step_type_id": 1, + "process_step_status_id": 1, + "process_id": "dd371565-9489-4907-a2e4-b8cbfe7a8cd2", + "date_created": "2023-02-21 08:15:20.479000 +00:00", + "date_last_changed": null + }, + { + "id": "629cc08f-bb0b-43ba-b20a-45c3538789b5", + "process_step_type_id": 2, + "process_step_status_id": 2, + "process_id": "dd371565-9489-4907-a2e4-b8cbfe7a8cd2", + "date_created": "2023-02-21 08:15:20.479000 +00:00", + "date_last_changed": null + }, + { + "id": "cd231cb8-55de-4ae4-b93f-d440512341fb", + "process_step_type_id": 3, + "process_step_status_id": 1, + "process_id": "dd371565-9489-4907-a2e4-b8cbfe7a8cd2", + "date_created": "2023-02-21 08:15:20.479000 +00:00", + "date_last_changed": null + }, + { + "id": "e64393ad-a885-45ad-8e7b-265ef1b4c691", + "process_step_type_id": 100, + "process_step_status_id": 1, + "process_id": "e64393ad-a885-45ad-8e7b-265ef1b4c691", + "date_created": "2023-02-21 08:15:20.479000 +00:00", + "date_last_changed": null + }, + { + "id": "e64393ad-a885-45ad-8e7b-265ef1b4c692", + "process_step_type_id": 200, + "process_step_status_id": 1, + "process_id": "e64393ad-a885-45ad-8e7b-265ef1b4c692", + "date_created": "2023-02-21 08:15:20.479000 +00:00", + "date_last_changed": null + } +] diff --git a/tests/database/Dim.DbAccess.Tests/Seeder/Data/processes.json b/tests/database/Dim.DbAccess.Tests/Seeder/Data/processes.json new file mode 100644 index 0000000..4f5f412 --- /dev/null +++ b/tests/database/Dim.DbAccess.Tests/Seeder/Data/processes.json @@ -0,0 +1,26 @@ +[ + { + "id": "dd371565-9489-4907-a2e4-b8cbfe7a8cd1", + "process_type_id" : 1, + "lock_expiry_date" : "2023-03-01 00:00:00.000000 +00:00", + "version" : "deadbeef-dead-beef-dead-beefdeadbeef" + }, + { + "id": "dd371565-9489-4907-a2e4-b8cbfe7a8cd2", + "process_type_id" : 1, + "lock_expiry_date" : null, + "version" : "deadbeef-dead-beef-dead-beefdeadbeef" + }, + { + "id": "e64393ad-a885-45ad-8e7b-265ef1b4c691", + "process_type_id" : 2, + "lock_expiry_date" : null, + "version" : "deadbeef-dead-beef-dead-beefdeadbeef" + }, + { + "id": "e64393ad-a885-45ad-8e7b-265ef1b4c692", + "process_type_id" : 2, + "lock_expiry_date" : null, + "version" : "deadbeef-dead-beef-dead-beefdeadbeef" + } +] diff --git a/tests/database/Dim.DbAccess.Tests/Seeder/Data/technical_users.json b/tests/database/Dim.DbAccess.Tests/Seeder/Data/technical_users.json new file mode 100644 index 0000000..148e379 --- /dev/null +++ b/tests/database/Dim.DbAccess.Tests/Seeder/Data/technical_users.json @@ -0,0 +1,16 @@ +[ + { + "id": "abb769d6-337f-4d1f-9f42-5230541a2d51", + "tenant_id": "5c9a4f56-0609-49a5-ab86-dd8f93dfd3fa", + "external_id": "a140e80f-f9fb-4e68-bd34-52943622c63d", + "technical_user_name": "dim-sa-1", + "process_id": "e64393ad-a885-45ad-8e7b-265ef1b4c691" + }, + { + "id": "abb769d6-337f-4d1f-9f42-5230541a2d52", + "tenant_id": "5ced499f-7e7a-4e3c-aee6-41ae96df6d40", + "external_id": "a140e80f-f9fb-4e68-bd34-52943622c63b", + "technical_user_name": "dim-sa-1", + "process_id": "e64393ad-a885-45ad-8e7b-265ef1b4c692" + } +] diff --git a/tests/database/Dim.DbAccess.Tests/Seeder/Data/tenants.json b/tests/database/Dim.DbAccess.Tests/Seeder/Data/tenants.json new file mode 100644 index 0000000..866bdd4 --- /dev/null +++ b/tests/database/Dim.DbAccess.Tests/Seeder/Data/tenants.json @@ -0,0 +1,23 @@ +[ + { + "id": "5c9a4f56-0609-49a5-ab86-dd8f93dfd3fa", + "company_name": "issuer company", + "bpn": "BPNL000001ISSUER", + "did_document_location": "https://example.org/BPNL000001ISSUER", + "process_id": "dd371565-9489-4907-a2e4-b8cbfe7a8cd1", + "is_issuer": true, + "operator_id": "a7444c10-5b07-430d-a3c1-c52675c4fa5a", + "operation_id": "6dcac248-57ab-4309-9477-ee21586b3738", + "company_id": "6dcac248-57ab-4309-9477-ee21586b3666", + "base_url": "https://example.org/base" + }, + { + "id": "5ced499f-7e7a-4e3c-aee6-41ae96df6d40", + "company_name": "test corp", + "bpn": "BPNL0000001CORP", + "did_document_location": "https://example.org/BPNL0000001CORP", + "process_id": "dd371565-9489-4907-a2e4-b8cbfe7a8cd2", + "is_issuer": false, + "operator_id": "a7444c10-5b07-430d-a3c1-c52675c4fa5a" + } +] diff --git a/tests/database/Dim.DbAccess.Tests/Setup/TestDbFixture.cs b/tests/database/Dim.DbAccess.Tests/Setup/TestDbFixture.cs new file mode 100644 index 0000000..be648d6 --- /dev/null +++ b/tests/database/Dim.DbAccess.Tests/Setup/TestDbFixture.cs @@ -0,0 +1,104 @@ +/******************************************************************************** + * Copyright (c) 2024 BMW Group AG + * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +using Dim.DbAccess.Tests.Seeder; +using Dim.Entities; +using Dim.Migrations.Migrations; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Org.Eclipse.TractusX.Portal.Backend.Framework.Seeding; +using Testcontainers.PostgreSql; +using Xunit; +using Xunit.Extensions.AssemblyFixture; + +[assembly: TestFramework(AssemblyFixtureFramework.TypeName, AssemblyFixtureFramework.AssemblyName)] +namespace Dim.DbAccess.Tests.Setup; + +public class TestDbFixture : IAsyncLifetime +{ + private readonly PostgreSqlContainer _container = new PostgreSqlBuilder() + .WithDatabase("test_db") + .WithImage("postgres") + .WithCleanUp(true) + .WithName(Guid.NewGuid().ToString()) + .Build(); + + /// + /// Foreach test a new DimDbContext will be created and filled with the custom seeding data. + /// + /// + /// In this method the migrations don't need to get executed since they are already on the testcontainer. + /// Because of that the EnsureCreatedAsync is enough. + /// + /// Additional data for the database + /// Returns the created DimDbContext + public async Task GetDbContext(params Action[] seedActions) + { + var optionsBuilder = new DbContextOptionsBuilder(); + + optionsBuilder.UseNpgsql( + _container.GetConnectionString(), + x => x.MigrationsAssembly(typeof(_120).Assembly.GetName().Name) + .MigrationsHistoryTable("__efmigrations_history_dim") + ); + var context = new DimDbContext(optionsBuilder.Options); + await context.Database.EnsureCreatedAsync(); + foreach (var seedAction in seedActions) + { + seedAction.Invoke(context); + } + + await context.SaveChangesAsync(); + return context; + } + + /// + /// This method is used to initially setup the database and run all migrations + /// + public async Task InitializeAsync() + { + await _container.StartAsync() + ; + + var optionsBuilder = new DbContextOptionsBuilder(); + + optionsBuilder.UseNpgsql( + _container.GetConnectionString(), + x => x.MigrationsAssembly(typeof(_120).Assembly.GetName().Name) + .MigrationsHistoryTable("__efmigrations_history_dim") + ); + var context = new DimDbContext(optionsBuilder.Options); + await context.Database.MigrateAsync(); + + var seederOptions = Options.Create(new SeederSettings + { + TestDataEnvironments = Enumerable.Empty(), + DataPaths = new[] { "Seeder/Data" } + }); + var insertSeeder = new BatchInsertSeeder(context, + LoggerFactory.Create(builder => builder.AddConsole()).CreateLogger(), + seederOptions); + await insertSeeder.ExecuteAsync(CancellationToken.None); + } + + /// + public async Task DisposeAsync() => await _container.DisposeAsync(); +} diff --git a/tests/database/Dim.DbAccess.Tests/TenantRepositoryTests.cs b/tests/database/Dim.DbAccess.Tests/TenantRepositoryTests.cs new file mode 100644 index 0000000..f81e7f3 --- /dev/null +++ b/tests/database/Dim.DbAccess.Tests/TenantRepositoryTests.cs @@ -0,0 +1,472 @@ +using AutoFixture; +using AutoFixture.AutoFakeItEasy; +using Dim.DbAccess.Repositories; +using Dim.DbAccess.Tests.Setup; +using Dim.Entities; +using Dim.Entities.Entities; +using Dim.Entities.Enums; +using FluentAssertions; +using Microsoft.EntityFrameworkCore; +using Xunit; +using Xunit.Extensions.AssemblyFixture; + +namespace Dim.DbAccess.Tests; + +public class TenantRepositoryTests : IAssemblyFixture +{ + private readonly TestDbFixture _dbTestDbFixture; + + public TenantRepositoryTests(TestDbFixture testDbFixture) + { + var fixture = new Fixture().Customize(new AutoFakeItEasyCustomization { ConfigureMembers = true }); + fixture.Behaviors.OfType().ToList() + .ForEach(b => fixture.Behaviors.Remove(b)); + + fixture.Behaviors.Add(new OmitOnRecursionBehavior()); + _dbTestDbFixture = testDbFixture; + } + + #region CreateTenant + + [Fact] + public async Task CreateTenant_CreatesSuccessfully() + { + // Arrange + var (sut, dbContext) = await CreateSutWithContext(); + var changeTracker = dbContext.ChangeTracker; + + // Act + var result = sut.CreateTenant("test corp", "BPNL00001TEST", "https://example.org/test", false, Guid.NewGuid(), Guid.NewGuid()); + + // Assert + changeTracker.HasChanges().Should().BeTrue(); + changeTracker.Entries().Should().HaveCount(1) + .And.AllSatisfy(x => + { + x.State.Should().Be(EntityState.Added); + x.Entity.Should().BeOfType(); + }); + changeTracker.Entries().Select(x => x.Entity).Cast() + .Should().Satisfy( + x => x.Id == result.Id && x.CompanyName == "test corp" + ); + } + + #endregion + + #region CreateTechnicalUser + + [Fact] + public async Task CreateTechnicalUser_CreatesSuccessfully() + { + // Arrange + var (sut, dbContext) = await CreateSutWithContext(); + var changeTracker = dbContext.ChangeTracker; + + // Act + sut.CreateTenantTechnicalUser(Guid.NewGuid(), "testUser", Guid.NewGuid(), Guid.NewGuid()); + + // Assert + changeTracker.HasChanges().Should().BeTrue(); + changeTracker.Entries().Should().HaveCount(1) + .And.AllSatisfy(x => + { + x.State.Should().Be(EntityState.Added); + x.Entity.Should().BeOfType(); + }); + changeTracker.Entries().Select(x => x.Entity).Cast() + .Should().Satisfy( + x => x.TechnicalUserName == "testUser" + ); + } + + #endregion + + #region AttachAndModifyTenant + + [Fact] + public async Task AttachAndModifyTenant_WithExistingTenant_UpdatesStatus() + { + // Arrange + var (sut, dbContext) = await CreateSutWithContext(); + + // Act + sut.AttachAndModifyTenant(new Guid("48f35f84-8d98-4fbd-ba80-8cbce5eeadb5"), + existing => + { + existing.Bpn = "BPNL000001TEST"; + }, + modify => + { + modify.Bpn = "BPNL000001NEW"; + } + ); + + // Assert + var changeTracker = dbContext.ChangeTracker; + var changedEntries = changeTracker.Entries().ToList(); + changeTracker.HasChanges().Should().BeTrue(); + changedEntries.Should().NotBeEmpty(); + changedEntries.Should().HaveCount(1); + var changedEntity = changedEntries.Single(); + changedEntity.State.Should().Be(EntityState.Modified); + changedEntity.Entity.Should().BeOfType().Which.Bpn.Should().Be("BPNL000001NEW"); + } + + #endregion + + #region AttachAndModifyTechnicalUser + + [Fact] + public async Task AttachAndModifyTechnicalUser_WithExistingTechnicalUser_UpdatesStatus() + { + // Arrange + var (sut, dbContext) = await CreateSutWithContext(); + + // Act + sut.AttachAndModifyTechnicalUser(new Guid("48f35f84-8d98-4fbd-ba80-8cbce5eeadb5"), + existing => + { + existing.ClientId = "cl1"; + }, + modify => + { + modify.ClientId = "clNew"; + } + ); + + // Assert + var changeTracker = dbContext.ChangeTracker; + var changedEntries = changeTracker.Entries().ToList(); + changeTracker.HasChanges().Should().BeTrue(); + changedEntries.Should().NotBeEmpty(); + changedEntries.Should().HaveCount(1); + var changedEntity = changedEntries.Single(); + changedEntity.State.Should().Be(EntityState.Modified); + changedEntity.Entity.Should().BeOfType().Which.ClientId.Should().Be("clNew"); + } + + #endregion + + #region GetTenantDataForProcessId + + [Fact] + public async Task GetTenantDataForProcessId_WithExistingTenant_ReturnsExpected() + { + // Arrange + var sut = await CreateSut(); + + // Act + var result = await sut.GetTenantDataForProcessId(new Guid("dd371565-9489-4907-a2e4-b8cbfe7a8cd1")); + + // Assert + result.Exists.Should().BeTrue(); + result.Bpn.Should().Be("BPNL000001ISSUER"); + result.CompanyName.Should().Be("issuer company"); + result.TenantId.Should().Be(new Guid("5c9a4f56-0609-49a5-ab86-dd8f93dfd3fa")); + } + + [Fact] + public async Task GetTenantDataForProcessId_WithoutExistingTenant_ReturnsExpected() + { + // Arrange + var sut = await CreateSut(); + + // Act + var result = await sut.GetTenantDataForProcessId(Guid.NewGuid()); + + // Assert + result.Exists.Should().BeFalse(); + result.Bpn.Should().BeNull(); + result.CompanyName.Should().BeNull(); + result.TenantId.Should().BeEmpty(); + } + + #endregion + + #region GetHostingUrlAndIsIssuer + + [Fact] + public async Task GetHostingUrlAndIsIssuer_WithExistingTenant_ReturnsExpected() + { + // Arrange + var sut = await CreateSut(); + + // Act + var result = await sut.GetHostingUrlAndIsIssuer(new Guid("5c9a4f56-0609-49a5-ab86-dd8f93dfd3fa")); + + // Assert + result.IsIssuer.Should().BeTrue(); + result.HostingUrl.Should().Be("https://example.org/BPNL000001ISSUER"); + } + + #endregion + + #region GetTenantForBpn + + [Fact] + public async Task GetTenantForBpn_WithExistingTenant_ReturnsExpected() + { + // Arrange + var sut = await CreateSut(); + + // Act + var result = await sut.GetTenantForBpn("BPNL000001ISSUER"); + + // Assert + result.Exists.Should().BeTrue(); + result.TenantId.Should().Be(new Guid("5c9a4f56-0609-49a5-ab86-dd8f93dfd3fa")); + } + + #endregion + + #region GetTenantDataForTechnicalUserProcessId + + [Fact] + public async Task GetTenantDataForTechnicalUserProcessId_WithExistingTechnicalUser_ReturnsExpected() + { + // Arrange + var sut = await CreateSut(); + + // Act + var result = await sut.GetTenantDataForTechnicalUserProcessId(new Guid("e64393ad-a885-45ad-8e7b-265ef1b4c691")); + + // Assert + result.Exists.Should().BeTrue(); + result.Bpn.Should().Be("BPNL000001ISSUER"); + result.CompanyName.Should().Be("issuer company"); + result.TechnicalUserId.Should().Be(new Guid("abb769d6-337f-4d1f-9f42-5230541a2d51")); + } + + #endregion + + #region GetTechnicalUserCallbackData + + [Fact] + public async Task GetTechnicalUserCallbackData_WithExistingTechnicalUser_ReturnsExpected() + { + // Arrange + var sut = await CreateSut(); + + // Act + var result = await sut.GetTechnicalUserCallbackData(new Guid("abb769d6-337f-4d1f-9f42-5230541a2d51")); + + // Assert + result.ExternalId.Should().Be(new Guid("a140e80f-f9fb-4e68-bd34-52943622c63d")); + } + + #endregion + + #region GetTechnicalUserForBpn + + [Fact] + public async Task GetTechnicalUserForBpn_WithExistingTechnicalUser_ReturnsExpected() + { + // Arrange + var sut = await CreateSut(); + + // Act + var result = await sut.GetTechnicalUserForBpn("BPNL000001ISSUER", "dim-sa-1"); + + // Assert + result.Exists.Should().BeTrue(); + result.TechnicalUserId.Should().Be(new Guid("abb769d6-337f-4d1f-9f42-5230541a2d51")); + result.ProcessId.Should().Be(new Guid("e64393ad-a885-45ad-8e7b-265ef1b4c691")); + } + + #endregion + + #region GetExternalIdForTechnicalUser + + [Fact] + public async Task GetExternalIdForTechnicalUser_WithExistingTechnicalUser_ReturnsExpected() + { + // Arrange + var sut = await CreateSut(); + + // Act + var result = await sut.GetExternalIdForTechnicalUser(new Guid("abb769d6-337f-4d1f-9f42-5230541a2d51")); + + // Assert + result.Should().Be(new Guid("a140e80f-f9fb-4e68-bd34-52943622c63d")); + } + + #endregion + + #region IsTenantExisting + + [Fact] + public async Task IsTenantExisting_WithExisting_ReturnsExpected() + { + // Arrange + var sut = await CreateSut(); + + // Act + var result = await sut.IsTenantExisting("issuer company", "BPNL000001ISSUER"); + + // Assert + result.Should().BeTrue(); + } + + [Fact] + public async Task IsTenantExisting_WithoutExisting_ReturnsExpected() + { + // Arrange + var sut = await CreateSut(); + + // Act + var result = await sut.IsTenantExisting("issuer company", "BPNL000NOTEXISTING"); + + // Assert + result.Should().BeFalse(); + } + + #endregion + + #region GetOperationId + + [Fact] + public async Task GetOperationId_WithExisting_ReturnsExpected() + { + // Arrange + var sut = await CreateSut(); + + // Act + var result = await sut.GetOperationId(new Guid("5c9a4f56-0609-49a5-ab86-dd8f93dfd3fa")); + + // Assert + result.Should().Be(new Guid("6dcac248-57ab-4309-9477-ee21586b3738")); + } + + #endregion + + #region GetCompanyRequestData + + [Fact] + public async Task GetCompanyRequestData_WithExisting_ReturnsExpected() + { + // Arrange + var sut = await CreateSut(); + + // Act + var result = await sut.GetCompanyRequestData(new Guid("5c9a4f56-0609-49a5-ab86-dd8f93dfd3fa")); + + // Assert + result.BaseUrl.Should().Be("https://example.org/base"); + } + + #endregion + + #region GetCompanyAndWalletDataForBpn + + [Fact] + public async Task GetCompanyAndWalletDataForBpn_WithExistingTechnicalUser_ReturnsExpected() + { + // Arrange + var sut = await CreateSut(); + + // Act + var result = await sut.GetCompanyAndWalletDataForBpn("BPNL000001ISSUER"); + + // Assert + result.Exists.Should().BeTrue(); + result.CompanyId.Should().Be(new Guid("6dcac248-57ab-4309-9477-ee21586b3666")); + } + + #endregion + + #region GetStatusListCreationData + + [Fact] + public async Task GetStatusListCreationData_WithExisting_ReturnsExpected() + { + // Arrange + var sut = await CreateSut(); + + // Act + var result = await sut.GetStatusListCreationData(new Guid("5c9a4f56-0609-49a5-ab86-dd8f93dfd3fa")); + + // Assert + result.CompanyId.Should().Be(new Guid("6dcac248-57ab-4309-9477-ee21586b3666")); + result.BaseUrl.Should().Be("https://example.org/base"); + } + + #endregion + + #region GetCallbackData + + [Fact] + public async Task GetCallbackData_WithExisting_ReturnsExpected() + { + // Arrange + var sut = await CreateSut(); + + // Act + var result = await sut.GetCallbackData(new Guid("5c9a4f56-0609-49a5-ab86-dd8f93dfd3fa")); + + // Assert + result.Bpn.Should().Be("BPNL000001ISSUER"); + result.BaseUrl.Should().Be("https://example.org/base"); + } + + #endregion + + #region GetDownloadUrlAndIsIssuer + + [Fact] + public async Task GetDownloadUrlAndIsIssuer_WithExisting_ReturnsExpected() + { + // Arrange + var sut = await CreateSut(); + + // Act + var result = await sut.GetDownloadUrlAndIsIssuer(new Guid("5c9a4f56-0609-49a5-ab86-dd8f93dfd3fa")); + + // Assert + result.IsIssuer.Should().BeTrue(); + } + + #endregion + + #region CreateTechnicalUser + + [Fact] + public async Task RemoveTechnicalUser_CreatesSuccessfully() + { + // Arrange + var technicalUserId = Guid.NewGuid(); + var (sut, dbContext) = await CreateSutWithContext(); + var changeTracker = dbContext.ChangeTracker; + + // Act + sut.RemoveTechnicalUser(technicalUserId); + + // Assert + changeTracker.HasChanges().Should().BeTrue(); + changeTracker.Entries().Should().HaveCount(1) + .And.AllSatisfy(x => + { + x.State.Should().Be(EntityState.Deleted); + x.Entity.Should().BeOfType(); + }); + changeTracker.Entries().Select(x => x.Entity).Cast() + .Should().Satisfy( + x => x.Id == technicalUserId + ); + } + + #endregion + + private async Task<(TenantRepository sut, DimDbContext dbContext)> CreateSutWithContext() + { + var context = await _dbTestDbFixture.GetDbContext(); + var sut = new TenantRepository(context); + return (sut, context); + } + + private async Task CreateSut() + { + var context = await _dbTestDbFixture.GetDbContext(); + return new TenantRepository(context); + } +} diff --git a/tests/processes/DimProcess.Executor.Tests/DimProcessTypeExecutorTests.cs b/tests/processes/DimProcess.Executor.Tests/DimProcessTypeExecutorTests.cs index 56ae8b3..162d096 100644 --- a/tests/processes/DimProcess.Executor.Tests/DimProcessTypeExecutorTests.cs +++ b/tests/processes/DimProcess.Executor.Tests/DimProcessTypeExecutorTests.cs @@ -26,13 +26,13 @@ namespace DimProcess.Executor.Tests; -public class CredentialProcessTypeExecutorTests +public class DimProcessTypeExecutorTests { private readonly DimProcessTypeExecutor _sut; private readonly IDimProcessHandler _dimProcessHandler; private readonly ITenantRepository _tenantRepository; - public CredentialProcessTypeExecutorTests() + public DimProcessTypeExecutorTests() { var fixture = new Fixture().Customize(new AutoFakeItEasyCustomization { ConfigureMembers = true }); fixture.Behaviors.OfType().ToList() @@ -67,24 +67,11 @@ public void IsExecutableStepTypeId_WithValid_ReturnsExpected() public void GetExecutableStepTypeIds_ReturnsExpected() { // Assert - _sut.GetExecutableStepTypeIds().Should().HaveCount(18).And.Satisfy( - x => x == ProcessStepTypeId.CREATE_SUBACCOUNT, - x => x == ProcessStepTypeId.CREATE_SERVICEMANAGER_BINDINGS, - x => x == ProcessStepTypeId.ASSIGN_ENTITLEMENTS, - x => x == ProcessStepTypeId.CREATE_SERVICE_INSTANCE, - x => x == ProcessStepTypeId.CREATE_SERVICE_BINDING, - x => x == ProcessStepTypeId.SUBSCRIBE_APPLICATION, - x => x == ProcessStepTypeId.CREATE_CLOUD_FOUNDRY_ENVIRONMENT, - x => x == ProcessStepTypeId.CREATE_CLOUD_FOUNDRY_SPACE, - x => x == ProcessStepTypeId.ADD_SPACE_MANAGER_ROLE, - x => x == ProcessStepTypeId.ADD_SPACE_DEVELOPER_ROLE, - x => x == ProcessStepTypeId.CREATE_DIM_SERVICE_INSTANCE, - x => x == ProcessStepTypeId.CREATE_SERVICE_INSTANCE_BINDING, - x => x == ProcessStepTypeId.GET_DIM_DETAILS, - x => x == ProcessStepTypeId.CREATE_APPLICATION, - x => x == ProcessStepTypeId.CREATE_COMPANY_IDENTITY, - x => x == ProcessStepTypeId.CREATE_STATUS_LIST, - x => x == ProcessStepTypeId.ASSIGN_COMPANY_APPLICATION, + _sut.GetExecutableStepTypeIds().Should().HaveCount(5).And.Satisfy( + x => x == ProcessStepTypeId.CHECK_OPERATION, + x => x == ProcessStepTypeId.GET_COMPANY, + x => x == ProcessStepTypeId.GET_DID_DOCUMENT, + x => x == ProcessStepTypeId.CREATE_WALLET, x => x == ProcessStepTypeId.SEND_CALLBACK); } @@ -148,23 +135,10 @@ public async Task ExecuteProcessStep_WithoutRegistrationId_ThrowsUnexpectedCondi } [Theory] - [InlineData(ProcessStepTypeId.CREATE_SUBACCOUNT)] - [InlineData(ProcessStepTypeId.CREATE_SERVICEMANAGER_BINDINGS)] - [InlineData(ProcessStepTypeId.ASSIGN_ENTITLEMENTS)] - [InlineData(ProcessStepTypeId.CREATE_SERVICE_INSTANCE)] - [InlineData(ProcessStepTypeId.CREATE_SERVICE_BINDING)] - [InlineData(ProcessStepTypeId.SUBSCRIBE_APPLICATION)] - [InlineData(ProcessStepTypeId.CREATE_CLOUD_FOUNDRY_ENVIRONMENT)] - [InlineData(ProcessStepTypeId.CREATE_CLOUD_FOUNDRY_SPACE)] - [InlineData(ProcessStepTypeId.ADD_SPACE_MANAGER_ROLE)] - [InlineData(ProcessStepTypeId.ADD_SPACE_DEVELOPER_ROLE)] - [InlineData(ProcessStepTypeId.CREATE_DIM_SERVICE_INSTANCE)] - [InlineData(ProcessStepTypeId.CREATE_SERVICE_INSTANCE_BINDING)] - [InlineData(ProcessStepTypeId.GET_DIM_DETAILS)] - [InlineData(ProcessStepTypeId.CREATE_APPLICATION)] - [InlineData(ProcessStepTypeId.CREATE_COMPANY_IDENTITY)] - [InlineData(ProcessStepTypeId.CREATE_STATUS_LIST)] - [InlineData(ProcessStepTypeId.ASSIGN_COMPANY_APPLICATION)] + [InlineData(ProcessStepTypeId.CREATE_WALLET)] + [InlineData(ProcessStepTypeId.CHECK_OPERATION)] + [InlineData(ProcessStepTypeId.GET_COMPANY)] + [InlineData(ProcessStepTypeId.GET_DID_DOCUMENT)] [InlineData(ProcessStepTypeId.SEND_CALLBACK)] public async Task ExecuteProcessStep_WithValidData_CallsExpected(ProcessStepTypeId processStepTypeId) { @@ -182,7 +156,7 @@ public async Task ExecuteProcessStep_WithValidData_CallsExpected(ProcessStepType initializeResult.ScheduleStepTypeIds.Should().BeNull(); // Arrange - SetupMock(tenantId, "test1_test"); + SetupMock(tenantId, "test1test"); // Act var result = await _sut.ExecuteProcessStep(processStepTypeId, Enumerable.Empty(), CancellationToken.None); @@ -212,11 +186,11 @@ public async Task ExecuteProcessStep_WithRecoverableServiceException_ReturnsToDo initializeResult.ScheduleStepTypeIds.Should().BeNull(); // Arrange - A.CallTo(() => _dimProcessHandler.CreateSubaccount(tenantId, "test1_test", A._)) + A.CallTo(() => _dimProcessHandler.CreateWallet(tenantId, "test1test", A._)) .Throws(new ServiceException("this is a test", true)); // Act - var result = await _sut.ExecuteProcessStep(ProcessStepTypeId.CREATE_SUBACCOUNT, Enumerable.Empty(), CancellationToken.None); + var result = await _sut.ExecuteProcessStep(ProcessStepTypeId.CREATE_WALLET, Enumerable.Empty(), CancellationToken.None); // Assert result.Modified.Should().BeTrue(); @@ -243,11 +217,11 @@ public async Task ExecuteProcessStep_WithServiceException_ReturnsFailedAndRetrig initializeResult.ScheduleStepTypeIds.Should().BeNull(); // Arrange - A.CallTo(() => _dimProcessHandler.CreateSubaccount(tenantId, "test1_test", A._)) + A.CallTo(() => _dimProcessHandler.CreateWallet(tenantId, "test1test", A._)) .Throws(new ServiceException("this is a test")); // Act - var result = await _sut.ExecuteProcessStep(ProcessStepTypeId.CREATE_SUBACCOUNT, Enumerable.Empty(), CancellationToken.None); + var result = await _sut.ExecuteProcessStep(ProcessStepTypeId.CREATE_WALLET, Enumerable.Empty(), CancellationToken.None); // Assert result.Modified.Should().BeTrue(); @@ -263,63 +237,21 @@ public async Task ExecuteProcessStep_WithServiceException_ReturnsFailedAndRetrig private void SetupMock(Guid tenantId, string tenantName) { - A.CallTo(() => _dimProcessHandler.CreateSubaccount(tenantId, tenantName, A._)) + A.CallTo(() => _dimProcessHandler.CreateWallet(tenantId, tenantName, A._)) .Returns(new ValueTuple?, ProcessStepStatusId, bool, string?>(null, ProcessStepStatusId.DONE, false, null)); - A.CallTo(() => _dimProcessHandler.CreateServiceManagerBindings(tenantId, A._)) + A.CallTo(() => _dimProcessHandler.CheckOperation(tenantId, A._)) .Returns(new ValueTuple?, ProcessStepStatusId, bool, string?>(null, ProcessStepStatusId.DONE, false, null)); - A.CallTo(() => _dimProcessHandler.AssignEntitlements(tenantId, A._)) + A.CallTo(() => _dimProcessHandler.GetCompany(tenantId, tenantName, A._)) .Returns(new ValueTuple?, ProcessStepStatusId, bool, string?>(null, ProcessStepStatusId.DONE, false, null)); - A.CallTo(() => _dimProcessHandler.CreateServiceInstance(tenantId, A._)) - .Returns(new ValueTuple?, ProcessStepStatusId, bool, string?>(null, ProcessStepStatusId.DONE, false, null)); - - A.CallTo(() => _dimProcessHandler.CreateServiceBindings(tenantId, A._)) - .Returns(new ValueTuple?, ProcessStepStatusId, bool, string?>(null, ProcessStepStatusId.DONE, false, null)); - - A.CallTo(() => _dimProcessHandler.SubscribeApplication(tenantId, A._)) - .Returns(new ValueTuple?, ProcessStepStatusId, bool, string?>(null, ProcessStepStatusId.DONE, false, null)); - - A.CallTo(() => _dimProcessHandler.CreateCloudFoundryEnvironment(tenantId, tenantName, A._)) - .Returns(new ValueTuple?, ProcessStepStatusId, bool, string?>(null, ProcessStepStatusId.DONE, false, null)); - - A.CallTo(() => _dimProcessHandler.CreateCloudFoundrySpace(tenantId, tenantName, A._)) - .Returns(new ValueTuple?, ProcessStepStatusId, bool, string?>(null, ProcessStepStatusId.DONE, false, null)); - - A.CallTo(() => _dimProcessHandler.AddSpaceManagerRole(tenantId, A._)) - .Returns(new ValueTuple?, ProcessStepStatusId, bool, string?>(null, ProcessStepStatusId.DONE, false, null)); - - A.CallTo(() => _dimProcessHandler.AddSpaceDeveloperRole(tenantId, A._)) - .Returns(new ValueTuple?, ProcessStepStatusId, bool, string?>(null, ProcessStepStatusId.DONE, false, null)); - - A.CallTo(() => _dimProcessHandler.CreateSubaccount(tenantId, tenantName, A._)) - .Returns(new ValueTuple?, ProcessStepStatusId, bool, string?>(null, ProcessStepStatusId.DONE, false, null)); - - A.CallTo(() => _dimProcessHandler.CreateDimServiceInstance(tenantName, tenantId, A._)) - .Returns(new ValueTuple?, ProcessStepStatusId, bool, string?>(null, ProcessStepStatusId.DONE, false, null)); - - A.CallTo(() => _dimProcessHandler.CreateServiceInstanceBindings(tenantName, tenantId, A._)) - .Returns(new ValueTuple?, ProcessStepStatusId, bool, string?>(null, ProcessStepStatusId.DONE, false, null)); - - A.CallTo(() => _dimProcessHandler.GetDimDetails(tenantName, tenantId, A._)) - .Returns(new ValueTuple?, ProcessStepStatusId, bool, string?>(null, ProcessStepStatusId.DONE, false, null)); - - A.CallTo(() => _dimProcessHandler.CreateApplication(tenantName, tenantId, A._)) - .Returns(new ValueTuple?, ProcessStepStatusId, bool, string?>(null, ProcessStepStatusId.DONE, false, null)); - - A.CallTo(() => _dimProcessHandler.GetDimDetails(tenantName, tenantId, A._)) - .Returns(new ValueTuple?, ProcessStepStatusId, bool, string?>(null, ProcessStepStatusId.DONE, false, null)); - - A.CallTo(() => _dimProcessHandler.CreateCompanyIdentity(tenantId, A._)) + A.CallTo(() => _dimProcessHandler.GetDidDocument(tenantId, A._)) .Returns(new ValueTuple?, ProcessStepStatusId, bool, string?>(null, ProcessStepStatusId.DONE, false, null)); A.CallTo(() => _dimProcessHandler.CreateStatusList(tenantId, A._)) .Returns(new ValueTuple?, ProcessStepStatusId, bool, string?>(null, ProcessStepStatusId.DONE, false, null)); - A.CallTo(() => _dimProcessHandler.AssignCompanyApplication(tenantId, A._)) - .Returns(new ValueTuple?, ProcessStepStatusId, bool, string?>(null, ProcessStepStatusId.DONE, false, null)); - A.CallTo(() => _dimProcessHandler.SendCallback(tenantId, A._)) .Returns(new ValueTuple?, ProcessStepStatusId, bool, string?>(null, ProcessStepStatusId.DONE, false, null)); } diff --git a/tests/processes/DimProcess.Executor.Tests/TechnicalUserProcessTypeExecutorTests.cs b/tests/processes/DimProcess.Executor.Tests/TechnicalUserProcessTypeExecutorTests.cs new file mode 100644 index 0000000..6737a74 --- /dev/null +++ b/tests/processes/DimProcess.Executor.Tests/TechnicalUserProcessTypeExecutorTests.cs @@ -0,0 +1,257 @@ +/******************************************************************************** + * Copyright (c) 2024 BMW Group AG + * Copyright 2024 SAP SE or an SAP affiliate company and ssi-dim-middle-layer contributors. + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +using Dim.DbAccess; +using Dim.DbAccess.Repositories; +using Dim.Entities.Enums; +using DimProcess.Library; +using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; + +namespace DimProcess.Executor.Tests; + +public class TechnicalUserProcessTypeExecutorTests +{ + private readonly TechnicalUserProcessTypeExecutor _sut; + private readonly ITechnicalUserProcessHandler _technicalUserProcessHandler; + private readonly ITenantRepository _tenantRepository; + + public TechnicalUserProcessTypeExecutorTests() + { + var fixture = new Fixture().Customize(new AutoFakeItEasyCustomization { ConfigureMembers = true }); + fixture.Behaviors.OfType().ToList() + .ForEach(b => fixture.Behaviors.Remove(b)); + fixture.Behaviors.Add(new OmitOnRecursionBehavior()); + + var repositories = A.Fake(); + _technicalUserProcessHandler = A.Fake(); + + _tenantRepository = A.Fake(); + + A.CallTo(() => repositories.GetInstance()).Returns(_tenantRepository); + + _sut = new TechnicalUserProcessTypeExecutor(repositories, _technicalUserProcessHandler); + } + + [Fact] + public void GetProcessTypeId_ReturnsExpected() + { + // Assert + _sut.GetProcessTypeId().Should().Be(ProcessTypeId.TECHNICAL_USER); + } + + [Fact] + public void IsExecutableStepTypeId_WithValid_ReturnsExpected() + { + // Assert + _sut.IsExecutableStepTypeId(ProcessStepTypeId.SEND_TECHNICAL_USER_CREATION_CALLBACK).Should().BeTrue(); + } + + [Fact] + public void GetExecutableStepTypeIds_ReturnsExpected() + { + // Assert + _sut.GetExecutableStepTypeIds().Should().HaveCount(5).And.Satisfy( + x => x == ProcessStepTypeId.CREATE_TECHNICAL_USER, + x => x == ProcessStepTypeId.GET_TECHNICAL_USER_DATA, + x => x == ProcessStepTypeId.SEND_TECHNICAL_USER_CREATION_CALLBACK, + x => x == ProcessStepTypeId.DELETE_TECHNICAL_USER, + x => x == ProcessStepTypeId.SEND_TECHNICAL_USER_DELETION_CALLBACK); + } + + [Fact] + public async Task IsLockRequested_ReturnsExpected() + { + // Act + var result = await _sut.IsLockRequested(ProcessStepTypeId.CREATE_TECHNICAL_USER); + + // Assert + result.Should().BeFalse(); + } + + #region InitializeProcess + + [Fact] + public async Task InitializeProcess_WithExistingProcess_ReturnsExpected() + { + // Arrange + var validProcessId = Guid.NewGuid(); + A.CallTo(() => _tenantRepository.GetTenantDataForTechnicalUserProcessId(validProcessId)) + .Returns(new ValueTuple(true, Guid.NewGuid(), "test", "test1")); + + // Act + var result = await _sut.InitializeProcess(validProcessId, Enumerable.Empty()); + + // Assert + result.Modified.Should().BeFalse(); + result.ScheduleStepTypeIds.Should().BeNull(); + } + + [Fact] + public async Task InitializeProcess_WithNotExistingProcess_ThrowsNotFoundException() + { + // Arrange + var validProcessId = Guid.NewGuid(); + A.CallTo(() => _tenantRepository.GetTenantDataForTechnicalUserProcessId(validProcessId)) + .Returns(new ValueTuple(false, Guid.Empty, string.Empty, string.Empty)); + + // Act + async Task Act() => await _sut.InitializeProcess(validProcessId, Enumerable.Empty()).ConfigureAwait(false); + + // Assert + var ex = await Assert.ThrowsAsync(Act); + ex.Message.Should().Be($"process {validProcessId} does not exist or is not associated with an technical user"); + } + + #endregion + + #region ExecuteProcessStep + + [Fact] + public async Task ExecuteProcessStep_WithoutInitialization_ThrowsUnexpectedConditionException() + { + // Act + async Task Act() => await _sut.ExecuteProcessStep(ProcessStepTypeId.CREATE_TECHNICAL_USER, Enumerable.Empty(), CancellationToken.None).ConfigureAwait(false); + + // Assert + var ex = await Assert.ThrowsAsync(Act); + ex.Message.Should().Be("technicalUserId and tenantName should never be empty here"); + } + + [Theory] + [InlineData(ProcessStepTypeId.CREATE_TECHNICAL_USER)] + [InlineData(ProcessStepTypeId.GET_TECHNICAL_USER_DATA)] + [InlineData(ProcessStepTypeId.SEND_TECHNICAL_USER_CREATION_CALLBACK)] + [InlineData(ProcessStepTypeId.DELETE_TECHNICAL_USER)] + [InlineData(ProcessStepTypeId.SEND_TECHNICAL_USER_DELETION_CALLBACK)] + public async Task ExecuteProcessStep_WithValidData_CallsExpected(ProcessStepTypeId processStepTypeId) + { + // Arrange InitializeProcess + var validProcessId = Guid.NewGuid(); + var tenantId = Guid.NewGuid(); + A.CallTo(() => _tenantRepository.GetTenantDataForTechnicalUserProcessId(validProcessId)) + .Returns(new ValueTuple(true, tenantId, "test", "test1")); + + // Act InitializeProcess + var initializeResult = await _sut.InitializeProcess(validProcessId, Enumerable.Empty()); + + // Assert InitializeProcess + initializeResult.Modified.Should().BeFalse(); + initializeResult.ScheduleStepTypeIds.Should().BeNull(); + + // Arrange + SetupMock(tenantId, "test1test"); + + // Act + var result = await _sut.ExecuteProcessStep(processStepTypeId, Enumerable.Empty(), CancellationToken.None); + + // Assert + result.Modified.Should().BeFalse(); + result.ScheduleStepTypeIds.Should().BeNull(); + result.ProcessStepStatusId.Should().Be(ProcessStepStatusId.DONE); + result.ProcessMessage.Should().BeNull(); + result.SkipStepTypeIds.Should().BeNull(); + } + + [Fact] + public async Task ExecuteProcessStep_WithRecoverableServiceException_ReturnsToDo() + { + // Arrange InitializeProcess + var validProcessId = Guid.NewGuid(); + var tenantId = Guid.NewGuid(); + A.CallTo(() => _tenantRepository.GetTenantDataForTechnicalUserProcessId(validProcessId)) + .Returns(new ValueTuple(true, tenantId, "test", "test1")); + + // Act InitializeProcess + var initializeResult = await _sut.InitializeProcess(validProcessId, Enumerable.Empty()); + + // Assert InitializeProcess + initializeResult.Modified.Should().BeFalse(); + initializeResult.ScheduleStepTypeIds.Should().BeNull(); + + // Arrange + A.CallTo(() => _technicalUserProcessHandler.CreateServiceInstanceBindings("test1test", tenantId, A._)) + .Throws(new ServiceException("this is a test", true)); + + // Act + var result = await _sut.ExecuteProcessStep(ProcessStepTypeId.CREATE_TECHNICAL_USER, Enumerable.Empty(), CancellationToken.None); + + // Assert + result.Modified.Should().BeTrue(); + result.ScheduleStepTypeIds.Should().BeNull(); + result.ProcessStepStatusId.Should().Be(ProcessStepStatusId.TODO); + result.ProcessMessage.Should().Be("this is a test"); + result.SkipStepTypeIds.Should().BeNull(); + } + + [Fact] + public async Task ExecuteProcessStep_WithServiceException_ReturnsFailedAndRetriggerStep() + { + // Arrange InitializeProcess + var validProcessId = Guid.NewGuid(); + var tenantId = Guid.NewGuid(); + A.CallTo(() => _tenantRepository.GetTenantDataForTechnicalUserProcessId(validProcessId)) + .Returns(new ValueTuple(true, tenantId, "test", "test1")); + + // Act InitializeProcess + var initializeResult = await _sut.InitializeProcess(validProcessId, Enumerable.Empty()); + + // Assert InitializeProcess + initializeResult.Modified.Should().BeFalse(); + initializeResult.ScheduleStepTypeIds.Should().BeNull(); + + // Arrange + A.CallTo(() => _technicalUserProcessHandler.CreateServiceInstanceBindings("test1test", tenantId, A._)) + .Throws(new ServiceException("this is a test")); + + // Act + var result = await _sut.ExecuteProcessStep(ProcessStepTypeId.CREATE_TECHNICAL_USER, Enumerable.Empty(), CancellationToken.None); + + // Assert + result.Modified.Should().BeTrue(); + result.ScheduleStepTypeIds.Should().BeNull(); + result.ProcessStepStatusId.Should().Be(ProcessStepStatusId.FAILED); + result.ProcessMessage.Should().Be("this is a test"); + result.SkipStepTypeIds.Should().BeNull(); + } + + #endregion + + #region Setup + + private void SetupMock(Guid tenantId, string tenantName) + { + A.CallTo(() => _technicalUserProcessHandler.CreateServiceInstanceBindings(tenantName, tenantId, A._)) + .Returns(new ValueTuple?, ProcessStepStatusId, bool, string?>(null, ProcessStepStatusId.DONE, false, null)); + + A.CallTo(() => _technicalUserProcessHandler.GetTechnicalUserData(tenantName, tenantId, A._)) + .Returns(new ValueTuple?, ProcessStepStatusId, bool, string?>(null, ProcessStepStatusId.DONE, false, null)); + + A.CallTo(() => _technicalUserProcessHandler.SendCreateCallback(tenantId, A._)) + .Returns(new ValueTuple?, ProcessStepStatusId, bool, string?>(null, ProcessStepStatusId.DONE, false, null)); + + A.CallTo(() => _technicalUserProcessHandler.DeleteServiceInstanceBindings(tenantName, tenantId, A._)) + .Returns(new ValueTuple?, ProcessStepStatusId, bool, string?>(null, ProcessStepStatusId.DONE, false, null)); + + A.CallTo(() => _technicalUserProcessHandler.SendDeleteCallback(tenantId, A._)) + .Returns(new ValueTuple?, ProcessStepStatusId, bool, string?>(null, ProcessStepStatusId.DONE, false, null)); + } + + #endregion +} diff --git a/tests/processes/DimProcess.Library.Tests/CallbackServiceTests.cs b/tests/processes/DimProcess.Library.Tests/CallbackServiceTests.cs index 0ec0098..fa8dde7 100644 --- a/tests/processes/DimProcess.Library.Tests/CallbackServiceTests.cs +++ b/tests/processes/DimProcess.Library.Tests/CallbackServiceTests.cs @@ -18,7 +18,7 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -using Dim.Clients.Api.Cf; +using Dim.Clients.Api.Div.Models; using Dim.Tests.Shared; using DimProcess.Library.Callback; using DimProcess.Library.Callback.DependencyInjection; @@ -76,7 +76,7 @@ public async Task SendCallback_WithValidData_DoesNotThrowException() var sut = new CallbackService(_tokenService, _options); // Act - await sut.SendCallback("BPNL00001TEST", _fixture.Create(), _fixture.Create(), "did:web:test123", CancellationToken.None); + await sut.SendCallback("BPNL00001TEST", _fixture.Create(), _fixture.Create(), "did:web:test123", CancellationToken.None); // Assert httpMessageHandlerMock.RequestMessage.Should().Match(x => @@ -97,7 +97,7 @@ public async Task SendCallback_WithInvalidData_ThrowsServiceException() var sut = new CallbackService(_tokenService, _options); // Act - async Task Act() => await sut.SendCallback("BPNL00001TEST", _fixture.Create(), _fixture.Create(), "did:web:test123", CancellationToken.None); + async Task Act() => await sut.SendCallback("BPNL00001TEST", _fixture.Create(), _fixture.Create(), "did:web:test123", CancellationToken.None); // Assert var ex = await Assert.ThrowsAsync(Act); diff --git a/tests/processes/DimProcess.Library.Tests/DimProcessHandlerTests.cs b/tests/processes/DimProcess.Library.Tests/DimProcessHandlerTests.cs index e3df309..dc9bd8c 100644 --- a/tests/processes/DimProcess.Library.Tests/DimProcessHandlerTests.cs +++ b/tests/processes/DimProcess.Library.Tests/DimProcessHandlerTests.cs @@ -18,15 +18,13 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -using Dim.Clients.Api.Cf; using Dim.Clients.Api.Dim; -using Dim.Clients.Api.Entitlements; -using Dim.Clients.Api.Provisioning; -using Dim.Clients.Api.Services; -using Dim.Clients.Api.SubAccounts; -using Dim.Clients.Api.Subscriptions; +using Dim.Clients.Api.Dim.Models; +using Dim.Clients.Api.Div; +using Dim.Clients.Api.Div.Models; using Dim.Clients.Token; using Dim.DbAccess; +using Dim.DbAccess.Models; using Dim.DbAccess.Repositories; using Dim.Entities.Entities; using Dim.Entities.Enums; @@ -34,6 +32,9 @@ using DimProcess.Library.DependencyInjection; using Microsoft.Extensions.Options; using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; +using Org.Eclipse.TractusX.Portal.Backend.Framework.Models.Configuration; +using System.Net; +using System.Security.Cryptography; using System.Text.Json; namespace DimProcess.Library.Tests; @@ -43,23 +44,16 @@ public class DimProcessHandlerTests private readonly Guid _tenantId = Guid.NewGuid(); private readonly Guid _processId = Guid.NewGuid(); private readonly Guid _operatorId = Guid.NewGuid(); - private readonly string _tenantName = "testCorp"; - private readonly Guid _rootDirectoryId = Guid.NewGuid(); + private const string TenantName = "testCorp"; - private readonly IDimRepositories _repositories; private readonly ITenantRepository _tenantRepositories; - private readonly ISubAccountClient _subAccountClient; - private readonly IServiceClient _serviceClient; - private readonly ISubscriptionClient _subscriptionClient; - private readonly IEntitlementClient _entitlementClient; private readonly IProvisioningClient _provisioningClient; - private readonly ICfClient _cfClient; private readonly IDimClient _dimClient; private readonly ICallbackService _callbackService; - private readonly IOptions _options; private readonly DimProcessHandler _sut; private readonly IFixture _fixture; + private readonly DimHandlerSettings _settings; public DimProcessHandlerTests() { @@ -68,405 +62,64 @@ public DimProcessHandlerTests() .ForEach(b => _fixture.Behaviors.Remove(b)); _fixture.Behaviors.Add(new OmitOnRecursionBehavior()); - _repositories = A.Fake(); + var repositories = A.Fake(); _tenantRepositories = A.Fake(); - A.CallTo(() => _repositories.GetInstance()).Returns(_tenantRepositories); + A.CallTo(() => repositories.GetInstance()).Returns(_tenantRepositories); - _subAccountClient = A.Fake(); - _serviceClient = A.Fake(); - _subscriptionClient = A.Fake(); - _entitlementClient = A.Fake(); _provisioningClient = A.Fake(); - _cfClient = A.Fake(); _dimClient = A.Fake(); _callbackService = A.Fake(); - _options = Options.Create(new DimHandlerSettings + _settings = new DimHandlerSettings { - AdminMail = "test@example.org", - AuthUrl = "https://example.org/auth", - ClientidCisCentral = "test123", - ClientsecretCisCentral = "test654", - EncryptionKey = "test123", - RootDirectoryId = _rootDirectoryId - }); - - _sut = new DimProcessHandler(_repositories, _subAccountClient, _serviceClient, _subscriptionClient, - _entitlementClient, _provisioningClient, _cfClient, _dimClient, _callbackService, _options); - } - - #region CreateSubaccount - - [Fact] - public async Task CreateSubaccount_WithValidData_ReturnsExpected() - { - // Arrange - var subAccountId = Guid.NewGuid(); - var tenant = new Tenant(_tenantId, "test", "Corp", "https://example.org/did", false, _processId, _operatorId); - A.CallTo(() => _tenantRepositories.AttachAndModifyTenant(_tenantId, A>._, A>._)) - .Invokes((Guid _, Action? initialize, Action modify) => - { - initialize?.Invoke(tenant); - modify(tenant); - }); - A.CallTo(() => _subAccountClient.CreateSubaccount(A._, A._, _tenantName, A._, A._, A._)) - .Returns(subAccountId); - - // Act - var result = await _sut.CreateSubaccount(_tenantId, _tenantName, CancellationToken.None); - - // Assert - result.modified.Should().BeFalse(); - result.processMessage.Should().BeNull(); - result.stepStatusId.Should().Be(ProcessStepStatusId.DONE); - result.nextStepTypeIds.Should().ContainSingle().Which.Should().Be(ProcessStepTypeId.CREATE_SERVICEMANAGER_BINDINGS); - tenant.SubAccountId.Should().Be(subAccountId); - } - - #endregion - - #region CreateServiceManagerBindings - - [Fact] - public async Task CreateServiceManagerBindings_WithNotExisting_ReturnsExpected() - { - // Arrange - A.CallTo(() => _tenantRepositories.GetSubAccountIdByTenantId(_tenantId)) - .Returns((Guid?)null); - async Task Act() => await _sut.CreateServiceManagerBindings(_tenantId, CancellationToken.None).ConfigureAwait(false); - - // Act - var ex = await Assert.ThrowsAsync(Act); - - // Assert - ex.Message.Should().Be("SubAccountId must not be null."); - } - - [Fact] - public async Task CreateServiceManagerBindings_WithValidData_ReturnsExpected() - { - // Arrange - var subAccountId = Guid.NewGuid(); - A.CallTo(() => _tenantRepositories.GetSubAccountIdByTenantId(_tenantId)) - .Returns(subAccountId); - - // Act - var result = await _sut.CreateServiceManagerBindings(_tenantId, CancellationToken.None); - - // Assert - A.CallTo(() => _subAccountClient.CreateServiceManagerBindings(A._, subAccountId, A._)) - .MustHaveHappenedOnceExactly(); - - result.modified.Should().BeFalse(); - result.processMessage.Should().BeNull(); - result.stepStatusId.Should().Be(ProcessStepStatusId.DONE); - result.nextStepTypeIds.Should().ContainSingle().Which.Should().Be(ProcessStepTypeId.ASSIGN_ENTITLEMENTS); - } - - #endregion - - #region AssignEntitlements - - [Fact] - public async Task AssignEntitlements_WithNotExisting_ReturnsExpected() - { - // Arrange - A.CallTo(() => _tenantRepositories.GetSubAccountIdByTenantId(_tenantId)) - .Returns((Guid?)null); - async Task Act() => await _sut.AssignEntitlements(_tenantId, CancellationToken.None).ConfigureAwait(false); - - // Act - var ex = await Assert.ThrowsAsync(Act); - - // Assert - ex.Message.Should().Be("SubAccountId must not be null."); - } - - [Fact] - public async Task AssignEntitlements_WithValidData_ReturnsExpected() - { - // Arrange - var subAccountId = Guid.NewGuid(); - A.CallTo(() => _tenantRepositories.GetSubAccountIdByTenantId(_tenantId)) - .Returns(subAccountId); - - // Act - var result = await _sut.AssignEntitlements(_tenantId, CancellationToken.None); - - // Assert - A.CallTo(() => _entitlementClient.AssignEntitlements(A._, subAccountId, A._)) - .MustHaveHappenedOnceExactly(); - - result.modified.Should().BeFalse(); - result.processMessage.Should().BeNull(); - result.stepStatusId.Should().Be(ProcessStepStatusId.DONE); - result.nextStepTypeIds.Should().ContainSingle().Which.Should().Be(ProcessStepTypeId.CREATE_SERVICE_INSTANCE); - } - - #endregion - - #region CreateServiceInstance - - [Fact] - public async Task CreateServiceInstance_WithNotExisting_ReturnsExpected() - { - // Arrange - A.CallTo(() => _tenantRepositories.GetSubAccountIdByTenantId(_tenantId)) - .Returns((Guid?)null); - async Task Act() => await _sut.CreateServiceInstance(_tenantId, CancellationToken.None).ConfigureAwait(false); - - // Act - var ex = await Assert.ThrowsAsync(Act); - - // Assert - ex.Message.Should().Be("SubAccountId must not be null."); - } - - [Fact] - public async Task CreateServiceInstance_WithValidData_ReturnsExpected() - { - // Arrange - var subAccountId = Guid.NewGuid(); - var serviceInstance = new CreateServiceInstanceResponse(Guid.NewGuid().ToString(), "test"); - var tenant = new Tenant(_tenantId, "test", "Corp", "https://example.org/did", false, _processId, _operatorId); - A.CallTo(() => _tenantRepositories.GetSubAccountIdByTenantId(_tenantId)) - .Returns(subAccountId); - A.CallTo(() => _subAccountClient.GetServiceManagerBindings(A._, subAccountId, A._)) - .Returns(new ServiceManagementBindingItem("test", "test123", "https://example.org/sm", "https://example.org")); - A.CallTo(() => _tenantRepositories.AttachAndModifyTenant(_tenantId, A>._, A>._)) - .Invokes((Guid _, Action? initialize, Action modify) => - { - initialize?.Invoke(tenant); - modify(tenant); - }); - A.CallTo(() => _serviceClient.CreateServiceInstance(A._, A._)) - .Returns(serviceInstance); - - // Act - var result = await _sut.CreateServiceInstance(_tenantId, CancellationToken.None); - - // Assert - A.CallTo(() => _serviceClient.CreateServiceInstance(A._, A._)) - .MustHaveHappenedOnceExactly(); - - result.modified.Should().BeFalse(); - result.processMessage.Should().BeNull(); - result.stepStatusId.Should().Be(ProcessStepStatusId.DONE); - result.nextStepTypeIds.Should().ContainSingle().Which.Should().Be(ProcessStepTypeId.CREATE_SERVICE_BINDING); - tenant.ServiceInstanceId.Should().Be(serviceInstance.Id); - } - - #endregion - - #region CreateServiceBindings - - [Fact] - public async Task CreateServiceBindings_WithNotExistingSubAccount_ReturnsExpected() - { - // Arrange - A.CallTo(() => _tenantRepositories.GetSubAccountAndServiceInstanceIdsByTenantId(_tenantId)) - .Returns((null, null)); - async Task Act() => await _sut.CreateServiceBindings(_tenantId, CancellationToken.None).ConfigureAwait(false); - - // Act - var ex = await Assert.ThrowsAsync(Act); - - // Assert - ex.Message.Should().Be("SubAccountId must not be null."); - } - - [Fact] - public async Task CreateServiceBindings_WithNotExistingServiceInstance_ReturnsExpected() - { - // Arrange - A.CallTo(() => _tenantRepositories.GetSubAccountAndServiceInstanceIdsByTenantId(_tenantId)) - .Returns((Guid.NewGuid(), null)); - async Task Act() => await _sut.CreateServiceBindings(_tenantId, CancellationToken.None).ConfigureAwait(false); - - // Act - var ex = await Assert.ThrowsAsync(Act); - - // Assert - ex.Message.Should().Be("ServiceInstanceId must not be null."); - } - - [Fact] - public async Task CreateServiceBindings_WithValidData_ReturnsExpected() - { - // Arrange - var subAccountId = Guid.NewGuid(); - var serviceInstanceId = Guid.NewGuid().ToString(); - var binding = new ServiceManagementBindingItem("cl1", "s1", "https://example.org/sm", "https://example.org"); - var tenant = new Tenant(_tenantId, "test", "Corp", "https://example.org/did", false, _processId, _operatorId); - A.CallTo(() => _tenantRepositories.GetSubAccountAndServiceInstanceIdsByTenantId(_tenantId)) - .Returns((subAccountId, serviceInstanceId)); - A.CallTo(() => _subAccountClient.GetServiceManagerBindings(A._, subAccountId, A._)) - .Returns(new ServiceManagementBindingItem("test", "test123", "https://example.org/sm", "https://example.org")); - A.CallTo(() => _tenantRepositories.AttachAndModifyTenant(_tenantId, A>._, A>._)) - .Invokes((Guid _, Action? initialize, Action modify) => + ApplicationName = "catena-x-portal", + EncryptionConfigIndex = 0, + EncryptionConfigs = new[] { - initialize?.Invoke(tenant); - modify(tenant); - }); - A.CallTo(() => _subAccountClient.GetServiceManagerBindings(A._, subAccountId, A._)) - .Returns(binding); - A.CallTo(() => _serviceClient.CreateServiceBinding(binding, serviceInstanceId, A._)) - .Returns(new CreateServiceBindingResponse(Guid.NewGuid().ToString(), "expectedName")); - - // Act - var result = await _sut.CreateServiceBindings(_tenantId, CancellationToken.None); - - // Assert - A.CallTo(() => _serviceClient.CreateServiceBinding(binding, serviceInstanceId, A._)) - .MustHaveHappenedOnceExactly(); - - result.modified.Should().BeFalse(); - result.processMessage.Should().BeNull(); - result.stepStatusId.Should().Be(ProcessStepStatusId.DONE); - result.nextStepTypeIds.Should().ContainSingle().Which.Should().Be(ProcessStepTypeId.SUBSCRIBE_APPLICATION); - tenant.ServiceBindingName.Should().Be("expectedName"); - } - - #endregion - - #region SubscribeApplication - - [Fact] - public async Task SubscribeApplication_WithNotExistingSubAccount_ReturnsExpected() - { - // Arrange - A.CallTo(() => _tenantRepositories.GetSubAccountAndServiceInstanceIdsByTenantId(_tenantId)) - .Returns((null, null)); - async Task Act() => await _sut.SubscribeApplication(_tenantId, CancellationToken.None).ConfigureAwait(false); - - // Act - var ex = await Assert.ThrowsAsync(Act); - - // Assert - ex.Message.Should().Be("SubAccountId must not be null."); - } - - [Fact] - public async Task SubscribeApplication_WithNotExistingServiceBindingName_ReturnsExpected() - { - // Arrange - A.CallTo(() => _tenantRepositories.GetSubAccountAndServiceInstanceIdsByTenantId(_tenantId)) - .Returns((Guid.NewGuid(), null)); - async Task Act() => await _sut.SubscribeApplication(_tenantId, CancellationToken.None).ConfigureAwait(false); - - // Act - var ex = await Assert.ThrowsAsync(Act); - - // Assert - ex.Message.Should().Be("SubAccountId must not be null."); - } - - [Fact] - public async Task SubscribeApplication_WithValidData_ReturnsExpected() - { - // Arrange - var subAccountId = Guid.NewGuid(); - var serviceInstanceId = Guid.NewGuid(); - var serviceBindingName = Guid.NewGuid().ToString(); - var serviceManagementBinding = new ServiceManagementBindingItem("c1", "cs1", "https://example.org/sm", "https://example.org/"); - var binding = new BindingItem("binding1", serviceInstanceId, _fixture.Create()); - A.CallTo(() => _tenantRepositories.GetSubAccountIdAndServiceBindingNameByTenantId(_tenantId)) - .Returns((subAccountId, serviceBindingName)); - A.CallTo(() => _subAccountClient.GetServiceManagerBindings(A._, subAccountId, A._)) - .Returns(new ServiceManagementBindingItem("test", "test123", "https://example.org/sm", "https://example.org")); - A.CallTo(() => _subAccountClient.GetServiceManagerBindings(A._, subAccountId, A._)) - .Returns(serviceManagementBinding); - A.CallTo(() => _serviceClient.GetServiceBinding(A._, serviceBindingName, A._)) - .Returns(binding); - A.CallTo(() => _serviceClient.CreateServiceBinding(serviceManagementBinding, serviceBindingName, A._)) - .Returns(new CreateServiceBindingResponse(Guid.NewGuid().ToString(), "expectedName")); - - // Act - var result = await _sut.SubscribeApplication(_tenantId, CancellationToken.None); - - // Assert - A.CallTo(() => _subscriptionClient.SubscribeApplication(A._, binding, "decentralized-identity-management-app", "standard", A._)) - .MustHaveHappenedOnceExactly(); - - result.modified.Should().BeFalse(); - result.processMessage.Should().BeNull(); - result.stepStatusId.Should().Be(ProcessStepStatusId.DONE); - result.nextStepTypeIds.Should().ContainSingle().Which.Should().Be(ProcessStepTypeId.CREATE_CLOUD_FOUNDRY_ENVIRONMENT); - } - - #endregion + new EncryptionModeConfig + { + Index = 0, + CipherMode = CipherMode.CBC, + PaddingMode = PaddingMode.PKCS7, + EncryptionKey = "2c68516f23467028602524534824437e417e253c29546c563c2f5e3d485e7667" + } + } + }; - #region CreateCloudFoundryEnvironment - - [Fact] - public async Task CreateCloudFoundryEnvironment_WithNotExistingSubAccount_ReturnsExpected() - { - // Arrange - A.CallTo(() => _tenantRepositories.GetSubAccountIdAndServiceBindingNameByTenantId(_tenantId)) - .Returns((null, null)); - async Task Act() => await _sut.CreateCloudFoundryEnvironment(_tenantId, _tenantName, CancellationToken.None).ConfigureAwait(false); - - // Act - var ex = await Assert.ThrowsAsync(Act); + var messageHandler = A.Fake(); + A.CallTo(messageHandler) + .Where(x => x.Method.Name == "SendAsync") + .WithReturnType>() + .Returns(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent("{\"id\": \"did:web:example:org:123TEST\"}") }); + var httpClient = new HttpClient(messageHandler) { BaseAddress = new Uri("http://localhost") }; - // Assert - ex.Message.Should().Be("SubAccountId must not be null."); + var httpClientFactory = _fixture.Freeze>(); + A.CallTo(() => httpClientFactory.FakedObject.CreateClient("didDocumentDownload")).Returns(httpClient); + _sut = new DimProcessHandler(repositories, _provisioningClient, _dimClient, _callbackService, httpClientFactory.FakedObject, Options.Create(_settings)); } - [Fact] - public async Task CreateCloudFoundryEnvironment_WithNotExistingServiceInstance_ReturnsExpected() - { - // Arrange - A.CallTo(() => _tenantRepositories.GetSubAccountIdAndServiceBindingNameByTenantId(_tenantId)) - .Returns((Guid.NewGuid(), null)); - async Task Act() => await _sut.CreateCloudFoundryEnvironment(_tenantId, _tenantName, CancellationToken.None).ConfigureAwait(false); - - // Act - var ex = await Assert.ThrowsAsync(Act); - - // Assert - ex.Message.Should().Be("ServiceBindingName must not be null."); - } + #region CreateOperation [Fact] - public async Task CreateCloudFoundryEnvironment_WithValidData_ReturnsExpected() + public async Task CreateOperation_WithDidLocationNull_ThrowsUnexpectedConditionException() { // Arrange - var subAccountId = Guid.NewGuid(); - var serviceInstanceId = Guid.NewGuid(); - var serviceManagementBinding = new ServiceManagementBindingItem("c1", "cs1", "https://example.org/sm", "https://example.org/"); - var binding = new BindingItem("binding1", serviceInstanceId, _fixture.Create()); - A.CallTo(() => _tenantRepositories.GetSubAccountIdAndServiceBindingNameByTenantId(_tenantId)) - .Returns((subAccountId, binding.Name)); - A.CallTo(() => _subAccountClient.GetServiceManagerBindings(A._, subAccountId, A._)) - .Returns(new ServiceManagementBindingItem("test", "test123", "https://example.org/sm", "https://example.org")); - A.CallTo(() => _subAccountClient.GetServiceManagerBindings(A._, subAccountId, A._)) - .Returns(serviceManagementBinding); - A.CallTo(() => _serviceClient.GetServiceBinding(serviceManagementBinding, binding.Name, A._)) - .Returns(binding); + A.CallTo(() => _tenantRepositories.GetHostingUrlAndIsIssuer(_tenantId)) + .Returns((true, (string?)null)); + Task Act() => _sut.CreateWallet(_tenantId, TenantName, CancellationToken.None); // Act - var result = await _sut.CreateCloudFoundryEnvironment(_tenantId, _tenantName, CancellationToken.None); + var ex = await Assert.ThrowsAsync(Act); // Assert - A.CallTo(() => _provisioningClient.CreateCloudFoundryEnvironment(A._, binding, _tenantName, A._, A._)) - .MustHaveHappenedOnceExactly(); - - result.modified.Should().BeFalse(); - result.processMessage.Should().BeNull(); - result.stepStatusId.Should().Be(ProcessStepStatusId.DONE); - result.nextStepTypeIds.Should().ContainSingle().Which.Should().Be(ProcessStepTypeId.CREATE_CLOUD_FOUNDRY_SPACE); + ex.Message.Should().Be("DidDocumentLocation must always be set"); } - #endregion - - #region CreateCloudFoundrySpace - [Fact] - public async Task CreateCloudFoundrySpace_WithValidData_ReturnsExpected() + public async Task CreateOperation_WithValidData_ReturnsExpected() { // Arrange - var spaceId = Guid.NewGuid(); + var operationId = Guid.NewGuid(); var tenant = new Tenant(_tenantId, "test", "Corp", "https://example.org/did", false, _processId, _operatorId); A.CallTo(() => _tenantRepositories.AttachAndModifyTenant(_tenantId, A>._, A>._)) .Invokes((Guid _, Action? initialize, Action modify) => @@ -474,420 +127,210 @@ public async Task CreateCloudFoundrySpace_WithValidData_ReturnsExpected() initialize?.Invoke(tenant); modify(tenant); }); - A.CallTo(() => _cfClient.CreateCloudFoundrySpace(_tenantName, A._)) - .Returns(spaceId); + A.CallTo(() => _provisioningClient.CreateOperation(A._, TenantName, A._, A._, A._, A._, A._)) + .Returns(operationId); // Act - var result = await _sut.CreateCloudFoundrySpace(_tenantId, _tenantName, CancellationToken.None); + var result = await _sut.CreateWallet(_tenantId, TenantName, CancellationToken.None); // Assert result.modified.Should().BeFalse(); result.processMessage.Should().BeNull(); result.stepStatusId.Should().Be(ProcessStepStatusId.DONE); - result.nextStepTypeIds.Should().ContainSingle().Which.Should().Be(ProcessStepTypeId.ADD_SPACE_MANAGER_ROLE); - tenant.SpaceId.Should().Be(spaceId); + result.nextStepTypeIds.Should().ContainSingle().Which.Should().Be(ProcessStepTypeId.CHECK_OPERATION); + tenant.OperationId.Should().Be(operationId); } #endregion - #region AddSpaceManagerRole - - [Fact] - public async Task AddSpaceManagerRole_WithNotExisting_ReturnsExpected() - { - // Arrange - A.CallTo(() => _tenantRepositories.GetSpaceId(_tenantId)) - .Returns((Guid?)null); - async Task Act() => await _sut.AddSpaceManagerRole(_tenantId, CancellationToken.None).ConfigureAwait(false); - - // Act - var ex = await Assert.ThrowsAsync(Act); - - // Assert - ex.Message.Should().Be("SpaceId must not be null."); - } + #region CheckOperation [Fact] - public async Task AddSpaceManagerRole_WithValidData_ReturnsExpected() + public async Task CheckOperation_WithoutOperationId_ThrowsUnexpectedException() { // Arrange - var spaceId = Guid.NewGuid(); - A.CallTo(() => _tenantRepositories.GetSpaceId(_tenantId)) - .Returns(spaceId); + A.CallTo(() => _tenantRepositories.GetOperationId(_tenantId)) + .Returns(null); + Task Act() => _sut.CheckOperation(_tenantId, CancellationToken.None); // Act - var result = await _sut.AddSpaceManagerRole(_tenantId, CancellationToken.None); + var ex = await Assert.ThrowsAsync(Act); // Assert - A.CallTo(() => _cfClient.AddSpaceRoleToUser("space_manager", A._, spaceId, A._)) - .MustHaveHappenedOnceExactly(); - - result.modified.Should().BeFalse(); - result.processMessage.Should().BeNull(); - result.stepStatusId.Should().Be(ProcessStepStatusId.DONE); - result.nextStepTypeIds.Should().ContainSingle().Which.Should().Be(ProcessStepTypeId.ADD_SPACE_DEVELOPER_ROLE); + ex.Message.Should().Be("OperationId must always be set"); } - #endregion - - #region AddSpaceManagerRole - - [Fact] - public async Task AddSpaceDeveloperRole_WithNotExisting_ReturnsExpected() - { - // Arrange - A.CallTo(() => _tenantRepositories.GetSpaceId(_tenantId)) - .Returns((Guid?)null); - async Task Act() => await _sut.AddSpaceDeveloperRole(_tenantId, CancellationToken.None).ConfigureAwait(false); - - // Act - var ex = await Assert.ThrowsAsync(Act); - - // Assert - ex.Message.Should().Be("SpaceId must not be null."); - } - - [Fact] - public async Task AddSpaceDeveloperRole_WithValidData_ReturnsExpected() - { - // Arrange - var spaceId = Guid.NewGuid(); - A.CallTo(() => _tenantRepositories.GetSpaceId(_tenantId)) - .Returns(spaceId); - - // Act - var result = await _sut.AddSpaceDeveloperRole(_tenantId, CancellationToken.None); - - // Assert - A.CallTo(() => _cfClient.AddSpaceRoleToUser("space_developer", A._, spaceId, A._)) - .MustHaveHappenedOnceExactly(); - - result.modified.Should().BeFalse(); - result.processMessage.Should().BeNull(); - result.stepStatusId.Should().Be(ProcessStepStatusId.DONE); - result.nextStepTypeIds.Should().ContainSingle().Which.Should().Be(ProcessStepTypeId.CREATE_DIM_SERVICE_INSTANCE); - } - - #endregion - - #region CreateDimServiceInstance - - [Fact] - public async Task CreateDimServiceInstance_WithValidData_ReturnsExpected() - { - // Arrange - var spaceId = Guid.NewGuid(); - var servicePlanId = Guid.NewGuid(); - A.CallTo(() => _cfClient.GetSpace(_tenantName, A._)) - .Returns(spaceId); - A.CallTo(() => _cfClient.GetServicePlan("decentralized-identity-management", "standard", A._)) - .Returns(servicePlanId); - - // Act - var result = await _sut.CreateDimServiceInstance(_tenantName, _tenantId, CancellationToken.None); - - // Assert - A.CallTo(() => _cfClient.CreateDimServiceInstance(_tenantName, spaceId, servicePlanId, A._)) - .MustHaveHappenedOnceExactly(); - - result.modified.Should().BeFalse(); - result.processMessage.Should().BeNull(); - result.stepStatusId.Should().Be(ProcessStepStatusId.DONE); - result.nextStepTypeIds.Should().ContainSingle().Which.Should().Be(ProcessStepTypeId.CREATE_SERVICE_INSTANCE_BINDING); - } - - #endregion - - #region CreateServiceInstanceBindings - [Fact] - public async Task CreateServiceInstanceBindings_WithNotExisting_ReturnsExpected() + public async Task CheckOperation_WithCompletedAndDataNull_ThrowsUnexpectedException() { // Arrange - A.CallTo(() => _tenantRepositories.GetSpaceId(_tenantId)) - .Returns((Guid?)null); - async Task Act() => await _sut.CreateServiceInstanceBindings(_tenantName, _tenantId, CancellationToken.None).ConfigureAwait(false); + var operationId = Guid.NewGuid(); + A.CallTo(() => _tenantRepositories.GetOperationId(_tenantId)) + .Returns(operationId); + A.CallTo(() => _provisioningClient.GetOperation(A._, A._)) + .Returns(new OperationResponse(operationId, OperationResponseStatus.completed, null, null, null)); + Task Act() => _sut.CheckOperation(_tenantId, CancellationToken.None); // Act - var ex = await Assert.ThrowsAsync(Act); + var ex = await Assert.ThrowsAsync(Act); // Assert - ex.Message.Should().Be("SpaceId must not be null."); + ex.Message.Should().Be($"Data should never be null when in status {OperationResponseStatus.completed}"); } [Fact] - public async Task CreateServiceInstanceBindings_WithValidData_ReturnsExpected() + public async Task CheckOperation_WithPending_StaysInTodo() { // Arrange - var spaceId = Guid.NewGuid(); - var servicePlanId = Guid.NewGuid(); - A.CallTo(() => _tenantRepositories.GetSpaceId(_tenantId)) - .Returns(spaceId); - A.CallTo(() => _cfClient.GetServicePlan("decentralized-identity-management", "standard", A._)) - .Returns(servicePlanId); + var operationId = Guid.NewGuid(); + A.CallTo(() => _tenantRepositories.GetOperationId(_tenantId)) + .Returns(operationId); + A.CallTo(() => _provisioningClient.GetOperation(A._, A._)) + .Returns(new OperationResponse(operationId, OperationResponseStatus.pending, null, null, null)); // Act - var result = await _sut.CreateServiceInstanceBindings(_tenantName, _tenantId, CancellationToken.None); + var result = await _sut.CheckOperation(_tenantId, CancellationToken.None); // Assert - A.CallTo(() => _cfClient.CreateServiceInstanceBindings(_tenantName, null, spaceId, A._)) - .MustHaveHappenedOnceExactly(); - result.modified.Should().BeFalse(); result.processMessage.Should().BeNull(); - result.stepStatusId.Should().Be(ProcessStepStatusId.DONE); - result.nextStepTypeIds.Should().ContainSingle().Which.Should().Be(ProcessStepTypeId.GET_DIM_DETAILS); - } - - #endregion - - #region GetDimDetails - - [Fact] - public async Task GetDimDetails_WithNotExisting_ReturnsExpected() - { - // Arrange - A.CallTo(() => _tenantRepositories.GetSpaceId(_tenantId)) - .Returns((Guid?)null); - async Task Act() => await _sut.GetDimDetails(_tenantName, _tenantId, CancellationToken.None).ConfigureAwait(false); - - // Act - var ex = await Assert.ThrowsAsync(Act); - - // Assert - ex.Message.Should().Be("SpaceId must not be null."); + result.stepStatusId.Should().Be(ProcessStepStatusId.TODO); + result.nextStepTypeIds.Should().BeNull(); } [Fact] - public async Task GetDimDetails_WithValidData_ReturnsExpected() + public async Task CheckOperation_WithValid_UpdatesTenant() { // Arrange - var spaceId = Guid.NewGuid(); - var dimInstanceId = Guid.NewGuid(); + var operationId = Guid.NewGuid(); + var responseData = new OperationResponseData( + Guid.NewGuid(), + Guid.NewGuid().ToString(), + "test name", + new ServiceKey( + new ServiceUaa("https://example.org/api", "https://example.org", "cl1", "test123"), + "https://example.org/test", + "test")); var tenant = new Tenant(_tenantId, "test", "Corp", "https://example.org/did", false, _processId, _operatorId); - A.CallTo(() => _tenantRepositories.GetSpaceId(_tenantId)) - .Returns(spaceId); - A.CallTo(() => _cfClient.GetServiceBinding(_tenantName, spaceId, $"{_tenantName}-dim-key01", A._)) - .Returns(dimInstanceId); A.CallTo(() => _tenantRepositories.AttachAndModifyTenant(_tenantId, A>._, A>._)) .Invokes((Guid _, Action? initialize, Action modify) => { initialize?.Invoke(tenant); modify(tenant); }); + A.CallTo(() => _tenantRepositories.GetOperationId(_tenantId)) + .Returns(operationId); + A.CallTo(() => _provisioningClient.GetOperation(A._, A._)) + .Returns(new OperationResponse(operationId, OperationResponseStatus.completed, null, null, responseData)); // Act - var result = await _sut.GetDimDetails(_tenantName, _tenantId, CancellationToken.None); + var result = await _sut.CheckOperation(_tenantId, CancellationToken.None); // Assert result.modified.Should().BeFalse(); result.processMessage.Should().BeNull(); result.stepStatusId.Should().Be(ProcessStepStatusId.DONE); - result.nextStepTypeIds.Should().ContainSingle().Which.Should().Be(ProcessStepTypeId.CREATE_APPLICATION); - tenant.DimInstanceId.Should().Be(dimInstanceId); + result.nextStepTypeIds.Should().ContainSingle().And.Satisfy(x => x == ProcessStepTypeId.GET_COMPANY); + tenant.BaseUrl.Should().Be(responseData.ServiceKey.Url); + tenant.TokenAddress.Should().Be(responseData.ServiceKey.Uaa.Url); + tenant.ClientId.Should().Be(responseData.ServiceKey.Uaa.ClientId); } #endregion - #region CreateApplication + #region GetCompany [Fact] - public async Task CreateApplication_WithNotExisting_ReturnsExpected() + public async Task GetCompany_WithDidLocationNull_ThrowsUnexpectedConditionException() { // Arrange - A.CallTo(() => _tenantRepositories.GetDimInstanceIdAndHostingUrl(_tenantId)) - .Returns(((Guid?)null, string.Empty, false)); - async Task Act() => await _sut.CreateApplication(_tenantName, _tenantId, CancellationToken.None).ConfigureAwait(false); + A.CallTo(() => _tenantRepositories.GetCompanyRequestData(_tenantId)) + .Returns(((string?)null, GetWalletData())); + Task Act() => _sut.GetCompany(_tenantId, TenantName, CancellationToken.None); // Act - var ex = await Assert.ThrowsAsync(Act); + var ex = await Assert.ThrowsAsync(Act); // Assert - ex.Message.Should().Be("DimInstanceId must not be null."); + ex.Message.Should().Be("BaseAddress must not be null"); } [Fact] - public async Task CreateApplication_WithValidData_ReturnsExpected() + public async Task GetCompany_WithValidData_ReturnsExpected() { // Arrange - var serviceCrenentialBinding = _fixture.Create(); - var dimInstanceId = Guid.NewGuid(); - var applicationId = Guid.NewGuid().ToString(); + var companyId = Guid.NewGuid(); var tenant = new Tenant(_tenantId, "test", "Corp", "https://example.org/did", false, _processId, _operatorId); - A.CallTo(() => _tenantRepositories.GetDimInstanceIdAndHostingUrl(_tenantId)) - .Returns((dimInstanceId, string.Empty, false)); - A.CallTo(() => _cfClient.GetServiceBindingDetails(dimInstanceId, A._)) - .Returns(serviceCrenentialBinding); + var baseUrl = "https://example.org/base"; + var downloadUrl = "https://example.org/download"; + A.CallTo(() => _tenantRepositories.GetCompanyRequestData(_tenantId)) + .Returns((baseUrl, GetWalletData())); A.CallTo(() => _tenantRepositories.AttachAndModifyTenant(_tenantId, A>._, A>._)) .Invokes((Guid _, Action? initialize, Action modify) => { initialize?.Invoke(tenant); modify(tenant); }); - A.CallTo(() => _dimClient.CreateApplication(A._, A._, _tenantName, A._)) - .Returns(applicationId); + A.CallTo(() => _dimClient.GetCompanyData(A._, baseUrl, TenantName, _settings.ApplicationName, A._)) + .Returns(new CompanyData(companyId, downloadUrl)); // Act - var result = await _sut.CreateApplication(_tenantName, _tenantId, CancellationToken.None); + var result = await _sut.GetCompany(_tenantId, TenantName, CancellationToken.None); // Assert - A.CallTo(() => _dimClient.CreateApplication(A._, A._, _tenantName, A._)) - .MustHaveHappenedOnceExactly(); - result.modified.Should().BeFalse(); result.processMessage.Should().BeNull(); result.stepStatusId.Should().Be(ProcessStepStatusId.DONE); - result.nextStepTypeIds.Should().ContainSingle().Which.Should().Be(ProcessStepTypeId.CREATE_COMPANY_IDENTITY); - tenant.ApplicationId.Should().Be(applicationId); + result.nextStepTypeIds.Should().ContainSingle().Which.Should().Be(ProcessStepTypeId.GET_DID_DOCUMENT); + tenant.CompanyId.Should().Be(companyId); + tenant.DidDownloadUrl.Should().Be(downloadUrl); } #endregion - #region CreateCompanyIdentity + #region GetDidDocument [Fact] - public async Task CreateCompanyIdentity_WithNotExisting_ReturnsExpected() + public async Task GetDidDocument_WithDownloadUrlNull_ThrowsUnexpectedConditionException() { // Arrange - A.CallTo(() => _tenantRepositories.GetDimInstanceIdAndHostingUrl(_tenantId)) - .Returns(((Guid?)null, string.Empty, false)); - async Task Act() => await _sut.CreateCompanyIdentity(_tenantId, CancellationToken.None).ConfigureAwait(false); + A.CallTo(() => _tenantRepositories.GetDownloadUrlAndIsIssuer(_tenantId)) + .Returns(((string?)null, false)); + Task Act() => _sut.GetDidDocument(_tenantId, CancellationToken.None); // Act - var ex = await Assert.ThrowsAsync(Act); + var ex = await Assert.ThrowsAsync(Act); // Assert - ex.Message.Should().Be("DimInstanceId must not be null."); + ex.Message.Should().Be("DownloadUrl must not be null"); } [Theory] [InlineData(true)] [InlineData(false)] - public async Task CreateCompanyIdentity_WithValidData_ReturnsExpected(bool isIssuer) + public async Task GetDidDocument_WithValidData_ReturnsExpected(bool isIssuer) { // Arrange - var serviceCrenentialBinding = _fixture.Create(); - var identityResponse = _fixture.Create(); - var dimInstanceId = Guid.NewGuid(); - var tenant = new Tenant(_tenantId, "test", "Corp", "https://example.org/did", isIssuer, _processId, _operatorId); - A.CallTo(() => _tenantRepositories.GetDimInstanceIdAndHostingUrl(_tenantId)) - .Returns((dimInstanceId, "https://example.org/hosting", tenant.IsIssuer)); - A.CallTo(() => _cfClient.GetServiceBindingDetails(dimInstanceId, A._)) - .Returns(serviceCrenentialBinding); - A.CallTo(() => _tenantRepositories.AttachAndModifyTenant(_tenantId, A>._, A>._)) - .Invokes((Guid _, Action? initialize, Action modify) => - { - initialize?.Invoke(tenant); - modify(tenant); - }); - A.CallTo(() => _dimClient.CreateCompanyIdentity(A._, _tenantId, "https://example.org/hosting", A._, tenant.IsIssuer, A._)) - .Returns(identityResponse); - - // Act - var result = await _sut.CreateCompanyIdentity(_tenantId, CancellationToken.None); - - // Assert - A.CallTo(() => _dimClient.CreateCompanyIdentity(A._, A._, A._, A._, tenant.IsIssuer, A._)) - .MustHaveHappenedOnceExactly(); - - result.modified.Should().BeFalse(); - result.processMessage.Should().BeNull(); - result.stepStatusId.Should().Be(ProcessStepStatusId.DONE); - result.nextStepTypeIds.Should().ContainSingle().Which.Should().Be(ProcessStepTypeId.ASSIGN_COMPANY_APPLICATION); - tenant.Did.Should().Be(identityResponse.Did); - tenant.DidDownloadUrl.Should().Be(identityResponse.DownloadUrl); - tenant.CompanyId.Should().Be(identityResponse.CompanyId); - } - - #endregion - - #region AssignCompanyApplication - - [Fact] - public async Task AssignCompanyApplication_WithNotExisting_ReturnsExpected() - { - // Arrange - A.CallTo(() => _tenantRepositories.GetApplicationAndCompanyId(_tenantId)) - .Returns(((string?)null, (Guid?)null, (Guid?)null, false)); - async Task Act() => await _sut.AssignCompanyApplication(_tenantId, CancellationToken.None).ConfigureAwait(false); - - // Act - var ex = await Assert.ThrowsAsync(Act); - - // Assert - ex.Message.Should().Be("ApplicationId must always be set here"); - } - - [Fact] - public async Task AssignCompanyApplication_WithNoCompanyId_ReturnsExpected() - { - // Arrange - A.CallTo(() => _tenantRepositories.GetApplicationAndCompanyId(_tenantId)) - .Returns((Guid.NewGuid().ToString(), (Guid?)null, (Guid?)null, false)); - async Task Act() => await _sut.AssignCompanyApplication(_tenantId, CancellationToken.None).ConfigureAwait(false); - - // Act - var ex = await Assert.ThrowsAsync(Act); - - // Assert - ex.Message.Should().Be("CompanyId must always be set here"); - } - - [Fact] - public async Task AssignCompanyApplication_WithNoDimInstanceId_ReturnsExpected() - { - // Arrange - A.CallTo(() => _tenantRepositories.GetApplicationAndCompanyId(_tenantId)) - .Returns((Guid.NewGuid().ToString(), Guid.NewGuid(), null, false)); - async Task Act() => await _sut.AssignCompanyApplication(_tenantId, CancellationToken.None).ConfigureAwait(false); - - // Act - var ex = await Assert.ThrowsAsync(Act); - - // Assert - ex.Message.Should().Be("DimInstanceId must not be null."); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public async Task AssignCompanyApplication_WithValidData_ReturnsExpected(bool isIssuer) - { - // Arrange - var serviceCrenentialBinding = _fixture.Create(); - var identityResponse = _fixture.Create(); - var applicationId = Guid.NewGuid().ToString(); - var applicationKey = Guid.NewGuid().ToString(); - var companyId = Guid.NewGuid(); - var dimInstanceId = Guid.NewGuid(); var tenant = new Tenant(_tenantId, "test", "Corp", "https://example.org/did", false, _processId, _operatorId); - A.CallTo(() => _tenantRepositories.GetApplicationAndCompanyId(_tenantId)) - .Returns((applicationId, companyId, dimInstanceId, isIssuer)); - A.CallTo(() => _cfClient.GetServiceBindingDetails(dimInstanceId, A._)) - .Returns(serviceCrenentialBinding); - A.CallTo(() => _dimClient.GetApplication(A._, A._, applicationId, A._)) - .Returns(applicationKey); + const string DownloadUrl = "https://example.org/download"; + A.CallTo(() => _tenantRepositories.GetDownloadUrlAndIsIssuer(_tenantId)) + .Returns((DownloadUrl, IsIssuer: isIssuer)); A.CallTo(() => _tenantRepositories.AttachAndModifyTenant(_tenantId, A>._, A>._)) .Invokes((Guid _, Action? initialize, Action modify) => { initialize?.Invoke(tenant); modify(tenant); }); - A.CallTo(() => _dimClient.CreateCompanyIdentity(A._, _tenantId, "https://example.org/hosting", A._, false, A._)) - .Returns(identityResponse); // Act - var result = await _sut.AssignCompanyApplication(_tenantId, CancellationToken.None); + var result = await _sut.GetDidDocument(_tenantId, CancellationToken.None); // Assert - A.CallTo(() => _dimClient.AssignApplicationToCompany(A._, A._, applicationKey, companyId, A._)) - .MustHaveHappenedOnceExactly(); - result.modified.Should().BeFalse(); result.processMessage.Should().BeNull(); result.stepStatusId.Should().Be(ProcessStepStatusId.DONE); result.nextStepTypeIds.Should().ContainSingle().Which.Should().Be(isIssuer ? ProcessStepTypeId.CREATE_STATUS_LIST : ProcessStepTypeId.SEND_CALLBACK); - tenant.ApplicationKey.Should().Be(applicationKey); + tenant.Did.Should().Be("did:web:example:org:123TEST"); } #endregion @@ -895,53 +338,50 @@ public async Task AssignCompanyApplication_WithValidData_ReturnsExpected(bool is #region CreateStatusList [Fact] - public async Task CreateStatusList_WithNoCompanyId_ReturnsExpected() + public async Task CreateStatusList_WithCompanyIdNull_ThrowsUnexpectedConditionException() { // Arrange - A.CallTo(() => _tenantRepositories.GetApplicationAndCompanyId(_tenantId)) - .Returns((Guid.NewGuid().ToString(), (Guid?)null, (Guid?)null, false)); - async Task Act() => await _sut.CreateStatusList(_tenantId, CancellationToken.None).ConfigureAwait(false); + A.CallTo(() => _tenantRepositories.GetStatusListCreationData(_tenantId)) + .Returns(((Guid?)null, (string?)null, GetWalletData())); + Task Act() => _sut.CreateStatusList(_tenantId, CancellationToken.None); // Act - var ex = await Assert.ThrowsAsync(Act); + var ex = await Assert.ThrowsAsync(Act); // Assert - ex.Message.Should().Be("CompanyId must always be set here"); + ex.Message.Should().Be("CompanyId must not be null"); } [Fact] - public async Task CreateStatusList_WithNoDimInstanceId_ReturnsExpected() + public async Task CreateStatusList_WithBaseUrlNull_ThrowsUnexpectedConditionException() { // Arrange - A.CallTo(() => _tenantRepositories.GetApplicationAndCompanyId(_tenantId)) - .Returns((Guid.NewGuid().ToString(), Guid.NewGuid(), null, false)); - async Task Act() => await _sut.CreateStatusList(_tenantId, CancellationToken.None).ConfigureAwait(false); + var companyId = Guid.NewGuid(); + A.CallTo(() => _tenantRepositories.GetStatusListCreationData(_tenantId)) + .Returns((companyId, null, GetWalletData())); + Task Act() => _sut.CreateStatusList(_tenantId, CancellationToken.None); // Act - var ex = await Assert.ThrowsAsync(Act); + var ex = await Assert.ThrowsAsync(Act); // Assert - ex.Message.Should().Be("DimInstanceId must not be null."); + ex.Message.Should().Be("BaseUrl must not be null"); } [Fact] public async Task CreateStatusList_WithValidData_ReturnsExpected() { // Arrange - var serviceCrenentialBinding = _fixture.Create(); - var applicationId = Guid.NewGuid().ToString(); var companyId = Guid.NewGuid(); - var dimInstanceId = Guid.NewGuid(); - A.CallTo(() => _tenantRepositories.GetApplicationAndCompanyId(_tenantId)) - .Returns((applicationId, companyId, dimInstanceId, false)); - A.CallTo(() => _cfClient.GetServiceBindingDetails(dimInstanceId, A._)) - .Returns(serviceCrenentialBinding); + const string BaseUrl = "https://example.org"; + A.CallTo(() => _tenantRepositories.GetStatusListCreationData(_tenantId)) + .Returns((companyId, BaseUrl, GetWalletData())); // Act var result = await _sut.CreateStatusList(_tenantId, CancellationToken.None); // Assert - A.CallTo(() => _dimClient.CreateStatusList(A._, A._, companyId, A._)) + A.CallTo(() => _dimClient.CreateStatusList(A._, BaseUrl, companyId, A._)) .MustHaveHappenedOnceExactly(); result.modified.Should().BeFalse(); @@ -955,79 +395,73 @@ public async Task CreateStatusList_WithValidData_ReturnsExpected() #region SendCallback [Fact] - public async Task SendCallback_WithNotExisting_ReturnsExpected() + public async Task SendCallback_WithoutBaseUrl_ReturnsExpected() { // Arrange A.CallTo(() => _tenantRepositories.GetCallbackData(_tenantId)) - .Returns(("bpn123", (string?)null, (string?)null, (Guid?)null)); + .Returns(("bpn123", null, _fixture.Create(), null, null)); async Task Act() => await _sut.SendCallback(_tenantId, CancellationToken.None).ConfigureAwait(false); // Act - var ex = await Assert.ThrowsAsync(Act); + var ex = await Assert.ThrowsAsync(Act); // Assert - ex.Message.Should().Be("DownloadUrl must not be null."); + ex.Message.Should().Be("BaseUrl must always be set"); } [Fact] - public async Task SendCallback_WithNoCompanyId_ReturnsExpected() + public async Task SendCallback_WithoutDid_ReturnsExpected() { // Arrange A.CallTo(() => _tenantRepositories.GetCallbackData(_tenantId)) - .Returns(("bpn123", "https://example.org/did", (string?)null, (Guid?)null)); + .Returns(("bpn123", "https://example.org/base", _fixture.Create(), null, null)); async Task Act() => await _sut.SendCallback(_tenantId, CancellationToken.None).ConfigureAwait(false); // Act - var ex = await Assert.ThrowsAsync(Act); + var ex = await Assert.ThrowsAsync(Act); // Assert - ex.Message.Should().Be("Did must not be null."); + ex.Message.Should().Be("Did must always be set"); } [Fact] - public async Task SendCallback_WithNoDimInstanceId_ReturnsExpected() + public async Task SendCallback_WithoutDownloadUrl_ReturnsExpected() { // Arrange A.CallTo(() => _tenantRepositories.GetCallbackData(_tenantId)) - .Returns(("bpn123", "https://example.org/did", Guid.NewGuid().ToString(), (Guid?)null)); + .Returns(("bpn123", "https://example.org/base", _fixture.Create(), "did:web:example:org:base", null)); async Task Act() => await _sut.SendCallback(_tenantId, CancellationToken.None).ConfigureAwait(false); // Act - var ex = await Assert.ThrowsAsync(Act); + var ex = await Assert.ThrowsAsync(Act); // Assert - ex.Message.Should().Be("DimInstanceId must not be null."); + ex.Message.Should().Be("DownloadUrl must always be set"); } [Fact] public async Task SendCallback_WithValidData_ReturnsExpected() { // Arrange - var serviceCrenentialBinding = _fixture.Create(); - var identityResponse = _fixture.Create(); - var dimInstanceId = Guid.NewGuid(); - var tenant = new Tenant(_tenantId, "test", "Corp", "https://example.org/did", false, _processId, _operatorId); - var did = Guid.NewGuid().ToString(); + var tenant = new Tenant(_tenantId, "test", "Corp", "https://example.org/did", false, _processId, _operatorId) + { + Did = "did:web:example:org:base", + DidDownloadUrl = "https://example.org/download", + }; + + var cryptoHelper = _settings.EncryptionConfigs.GetCryptoHelper(_settings.EncryptionConfigIndex); + var (encryptSecret, initializationVector) = cryptoHelper.Encrypt("test123"); + var walletData = new WalletData("https://example.org/token", "cl1", encryptSecret, initializationVector, _settings.EncryptionConfigIndex); A.CallTo(() => _tenantRepositories.GetCallbackData(_tenantId)) - .Returns(("bpn123", "https://example.org/did", did, dimInstanceId)); - A.CallTo(() => _cfClient.GetServiceBindingDetails(dimInstanceId, A._)) - .Returns(serviceCrenentialBinding); - A.CallTo(() => _dimClient.GetDidDocument(A._, A._)) - .Returns(JsonDocument.Parse("{}")); - A.CallTo(() => _tenantRepositories.AttachAndModifyTenant(_tenantId, A>._, A>._)) - .Invokes((Guid _, Action? initialize, Action modify) => - { - initialize?.Invoke(tenant); - modify(tenant); - }); - A.CallTo(() => _dimClient.CreateCompanyIdentity(A._, _tenantId, "https://example.org/hosting", A._, false, A._)) - .Returns(identityResponse); + .Returns(("bpn123", "https://example.org/base", walletData, tenant.Did, tenant.DidDownloadUrl)); // Act var result = await _sut.SendCallback(_tenantId, CancellationToken.None); // Assert - A.CallTo(() => _callbackService.SendCallback("bpn123", A._, A._, did, A._)) + A.CallTo(() => _callbackService.SendCallback(A._, A._, A._, A._, A._)) + .MustHaveHappenedOnceExactly(); + A.CallTo(() => _callbackService.SendCallback("bpn123", A._, A._, "did:web:example:org:base", A._)) .MustHaveHappenedOnceExactly(); result.modified.Should().BeFalse(); @@ -1037,4 +471,12 @@ public async Task SendCallback_WithValidData_ReturnsExpected() } #endregion + + private WalletData GetWalletData() + { + var cryptoHelper = _settings.EncryptionConfigs.GetCryptoHelper(_settings.EncryptionConfigIndex); + var (secret, initializationVector) = cryptoHelper.Encrypt("test123"); + + return new WalletData("https://example.org/token", "cl1", secret, initializationVector, _settings.EncryptionConfigIndex); + } } diff --git a/tests/processes/DimProcess.Library.Tests/TechnicalUserProcessHandlerTests.cs b/tests/processes/DimProcess.Library.Tests/TechnicalUserProcessHandlerTests.cs index 88cb19e..52f4130 100644 --- a/tests/processes/DimProcess.Library.Tests/TechnicalUserProcessHandlerTests.cs +++ b/tests/processes/DimProcess.Library.Tests/TechnicalUserProcessHandlerTests.cs @@ -18,8 +18,8 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -using Dim.Clients.Api.Cf; using Dim.DbAccess; +using Dim.DbAccess.Models; using Dim.DbAccess.Repositories; using Dim.Entities.Entities; using Dim.Entities.Enums; @@ -35,9 +35,9 @@ namespace DimProcess.Library.Tests; public class TechnicalUserProcessHandlerTests { private readonly ITenantRepository _tenantRepositories; - private readonly ICfClient _cfClient; private readonly ICallbackService _callbackService; private readonly TechnicalUserProcessHandler _sut; + private readonly TechnicalUserSettings _settings; public TechnicalUserProcessHandlerTests() { @@ -51,9 +51,8 @@ public TechnicalUserProcessHandlerTests() A.CallTo(() => repositories.GetInstance()).Returns(_tenantRepositories); - _cfClient = A.Fake(); _callbackService = A.Fake(); - var options = Options.Create(new TechnicalUserSettings + _settings = new TechnicalUserSettings { EncryptionConfigIndex = 0, EncryptionConfigs = new[] @@ -66,91 +65,92 @@ public TechnicalUserProcessHandlerTests() EncryptionKey = "2c68516f23467028602524534824437e417e253c29546c563c2f5e3d485e7667" } } - }); + }; + var options = Options.Create(_settings); - _sut = new TechnicalUserProcessHandler(repositories, _cfClient, _callbackService, options); + _sut = new TechnicalUserProcessHandler(repositories, _callbackService, options); } - #region CreateSubaccount + #region CreateServiceInstanceBindings [Fact] - public async Task CreateSubaccount_WithValidData_ReturnsExpected() + public async Task CreateServiceInstanceBindings_WithValidData_ReturnsExpected() { // Arrange var technicalUserId = Guid.NewGuid(); - var serviceBindingId = Guid.NewGuid(); - var technicalUser = new TechnicalUser(Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid(), "test", Guid.NewGuid()); - A.CallTo(() => _tenantRepositories.GetSpaceIdAndTechnicalUserName(technicalUserId)) - .Returns(new ValueTuple(Guid.NewGuid(), "test")); - A.CallTo(() => _cfClient.GetServiceBinding("test", A._, A._, A._)) - .Returns(serviceBindingId); - A.CallTo(() => _cfClient.GetServiceBindingDetails(serviceBindingId, A._)) - .Returns(new ServiceCredentialBindingDetailResponse(new Credentials("https://example.org", new Uaa("cl1", "test123", "https://example.org/test", "https://example.org/api")))); - A.CallTo(() => _tenantRepositories.AttachAndModifyTechnicalUser(A._, A>._, A>._)) - .Invokes((Guid _, Action? initialize, Action modify) => - { - initialize?.Invoke(technicalUser); - modify(technicalUser); - }); + // Act + var result = await _sut.CreateServiceInstanceBindings("test", technicalUserId, CancellationToken.None); + + // Assert + result.modified.Should().BeFalse(); + result.processMessage.Should().Be("Technical User Creation is currently not supported"); + result.stepStatusId.Should().Be(ProcessStepStatusId.FAILED); + result.nextStepTypeIds.Should().BeNull(); + } + + #endregion + + #region GetTechnicalUserData + + [Fact] + public async Task GetTechnicalUserData_WithValidData_ReturnsExpected() + { + // Arrange + var technicalUserId = Guid.NewGuid(); // Act var result = await _sut.GetTechnicalUserData("test", technicalUserId, CancellationToken.None); // Assert result.modified.Should().BeFalse(); - result.processMessage.Should().BeNull(); - result.stepStatusId.Should().Be(ProcessStepStatusId.DONE); - result.nextStepTypeIds.Should().ContainSingle().Which.Should().Be(ProcessStepTypeId.SEND_TECHNICAL_USER_CREATION_CALLBACK); - technicalUser.EncryptionMode.Should().NotBeNull().And.Be(0); - technicalUser.ClientId.Should().Be("cl1"); + result.processMessage.Should().Be("Technical User Creation is currently not supported"); + result.stepStatusId.Should().Be(ProcessStepStatusId.FAILED); + result.nextStepTypeIds.Should().BeNull(); } #endregion - #region DeleteServiceInstanceBindings + #region SendCreateCallback [Fact] - public async Task DeleteServiceInstanceBindings_WithValidData_ReturnsExpected() + public async Task SendCreateCallback_WithValidData_ReturnsExpected() { // Arrange var technicalUserId = Guid.NewGuid(); - var serviceBindingId = Guid.NewGuid(); - var spaceId = Guid.NewGuid(); - A.CallTo(() => _tenantRepositories.GetSpaceIdAndTechnicalUserName(technicalUserId)) - .Returns(new ValueTuple(spaceId, "test")); - A.CallTo(() => _cfClient.GetServiceBinding("test", spaceId, A._, A._)) - .Returns(serviceBindingId); + A.CallTo(() => _tenantRepositories.GetTechnicalUserCallbackData(technicalUserId)) + .Returns((Guid.NewGuid(), GetWalletData())); // Act - var result = await _sut.DeleteServiceInstanceBindings("test", technicalUserId, CancellationToken.None); + var result = await _sut.SendCreateCallback(technicalUserId, CancellationToken.None); // Assert + A.CallTo(() => _callbackService.SendTechnicalUserCallback(A._, A._, A._, A._, A._)) + .MustHaveHappenedOnceExactly(); + result.modified.Should().BeFalse(); result.processMessage.Should().BeNull(); result.stepStatusId.Should().Be(ProcessStepStatusId.DONE); - result.nextStepTypeIds.Should().ContainSingle().Which.Should().Be(ProcessStepTypeId.SEND_TECHNICAL_USER_DELETION_CALLBACK); - A.CallTo(() => _cfClient.DeleteServiceInstanceBindings(A._, A._)) - .MustHaveHappenedOnceExactly(); - A.CallTo(() => _cfClient.DeleteServiceInstanceBindings(serviceBindingId, A._)) - .MustHaveHappenedOnceExactly(); + result.nextStepTypeIds.Should().BeNull(); } + #endregion + + #region DeleteServiceInstanceBindings + [Fact] - public async Task DeleteServiceInstanceBindings_WithoutSpaceId_ThrowsConflictException() + public async Task DeleteServiceInstanceBindings_WithValidData_ReturnsExpected() { // Arrange var technicalUserId = Guid.NewGuid(); - A.CallTo(() => _tenantRepositories.GetSpaceIdAndTechnicalUserName(technicalUserId)) - .Returns(new ValueTuple(null, "test")); - async Task Act() => await _sut.DeleteServiceInstanceBindings("test", technicalUserId, CancellationToken.None); // Act - var ex = await Assert.ThrowsAsync(Act); + var result = await _sut.DeleteServiceInstanceBindings("test", technicalUserId, CancellationToken.None); // Assert - ex.Message.Should().Be("SpaceId must not be null."); - A.CallTo(() => _cfClient.DeleteServiceInstanceBindings(A._, A._)) - .MustNotHaveHappened(); + result.modified.Should().BeFalse(); + result.processMessage.Should().Be("Technical User deletion is currently not supported"); + result.stepStatusId.Should().Be(ProcessStepStatusId.FAILED); + result.nextStepTypeIds.Should().BeNull(); } #endregion @@ -192,4 +192,12 @@ public async Task SendCallback_WithValidData_ReturnsExpected() } #endregion + + private WalletData GetWalletData() + { + var cryptoHelper = _settings.EncryptionConfigs.GetCryptoHelper(_settings.EncryptionConfigIndex); + var (secret, initializationVector) = cryptoHelper.Encrypt("test123"); + + return new WalletData("https://example.org/token", "cl1", secret, initializationVector, _settings.EncryptionConfigIndex); + } } diff --git a/tests/processes/Processes.Worker.Library.Tests/ProcessExecutionServiceTests.cs b/tests/processes/Processes.Worker.Library.Tests/ProcessExecutionServiceTests.cs index 62b0bb0..d731e8e 100644 --- a/tests/processes/Processes.Worker.Library.Tests/ProcessExecutionServiceTests.cs +++ b/tests/processes/Processes.Worker.Library.Tests/ProcessExecutionServiceTests.cs @@ -27,7 +27,6 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Org.Eclipse.TractusX.Portal.Backend.Framework.DateTimeProvider; -using Org.Eclipse.TractusX.Portal.Backend.Processes.Worker.Library; using System.Collections.Immutable; namespace Processes.Worker.Library.Tests; diff --git a/tests/processes/Processes.Worker.Library.Tests/ProcessExecutorTests.cs b/tests/processes/Processes.Worker.Library.Tests/ProcessExecutorTests.cs index 222fcf9..48ebdd9 100644 --- a/tests/processes/Processes.Worker.Library.Tests/ProcessExecutorTests.cs +++ b/tests/processes/Processes.Worker.Library.Tests/ProcessExecutorTests.cs @@ -24,7 +24,6 @@ using Dim.Entities.Enums; using Microsoft.Extensions.Logging; using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; -using Org.Eclipse.TractusX.Portal.Backend.Processes.Worker.Library; using System.Collections.Immutable; using ProcessTypeId = Dim.Entities.Enums.ProcessTypeId; diff --git a/tests/web/Dim.Web.Tests/DimBusinessLogicTests.cs b/tests/web/Dim.Web.Tests/DimBusinessLogicTests.cs index 9d32d81..9493e28 100644 --- a/tests/web/Dim.Web.Tests/DimBusinessLogicTests.cs +++ b/tests/web/Dim.Web.Tests/DimBusinessLogicTests.cs @@ -18,15 +18,20 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -using Dim.Clients.Api.Cf; using Dim.Clients.Api.Dim; +using Dim.Clients.Token; using Dim.DbAccess; +using Dim.DbAccess.Models; using Dim.DbAccess.Repositories; using Dim.Entities.Entities; using Dim.Entities.Enums; using Dim.Web.BusinessLogic; +using Dim.Web.ErrorHandling; +using Dim.Web.Models; using Microsoft.Extensions.Options; using Org.Eclipse.TractusX.Portal.Backend.Framework.ErrorHandling; +using Org.Eclipse.TractusX.Portal.Backend.Framework.Models.Configuration; +using System.Security.Cryptography; namespace Dim.Web.Tests; @@ -34,21 +39,21 @@ public class DimBusinessLogicTests { private static readonly Guid OperatorId = Guid.NewGuid(); private readonly IDimBusinessLogic _sut; - private readonly ICfClient _cfClient; private readonly IDimClient _dimClient; private readonly ITenantRepository _tenantRepository; private readonly IProcessStepRepository _processStepRepository; + private readonly DimSettings _settings; + private readonly IFixture _fixture; public DimBusinessLogicTests() { - var fixture = new Fixture().Customize(new AutoFakeItEasyCustomization { ConfigureMembers = true }); - fixture.Behaviors.OfType().ToList() - .ForEach(b => fixture.Behaviors.Remove(b)); - fixture.Behaviors.Add(new OmitOnRecursionBehavior()); + _fixture = new Fixture().Customize(new AutoFakeItEasyCustomization { ConfigureMembers = true }); + _fixture.Behaviors.OfType().ToList() + .ForEach(b => _fixture.Behaviors.Remove(b)); + _fixture.Behaviors.Add(new OmitOnRecursionBehavior()); var repositories = A.Fake(); _dimClient = A.Fake(); - _cfClient = A.Fake(); _tenantRepository = A.Fake(); _processStepRepository = A.Fake(); @@ -56,12 +61,25 @@ public DimBusinessLogicTests() A.CallTo(() => repositories.GetInstance()).Returns(_tenantRepository); A.CallTo(() => repositories.GetInstance()).Returns(_processStepRepository); - _sut = new DimBusinessLogic(repositories, _cfClient, _dimClient, Options.Create(new DimSettings + _settings = new DimSettings { - OperatorId = OperatorId - })); + OperatorId = OperatorId, + EncryptionConfigs = new[] + { + new EncryptionModeConfig + { + Index = 0, + CipherMode = CipherMode.CBC, + PaddingMode = PaddingMode.PKCS7, + EncryptionKey = "2c68516f23467028602524534824437e417e253c29546c563c2f5e3d485e7667" + } + } + }; + _sut = new DimBusinessLogic(repositories, _dimClient, Options.Create(_settings)); } + #region StartSetupDim + [Fact] public async Task StartSetupDim_WithExisting_ThrowsConflictException() { @@ -74,7 +92,7 @@ public async Task StartSetupDim_WithExisting_ThrowsConflictException() var result = await Assert.ThrowsAsync(Act); // Assert - result.Message.Should().Be($"Tenant testCompany with Bpn BPNL00000001TEST already exists"); + result.Message.Should().Be(DimErrors.TENANT_ALREADY_EXISTS.ToString()); } [Theory] @@ -97,11 +115,12 @@ public async Task StartSetupDim_WithNewData_CreatesExpected(string companyName, .Invokes((ProcessTypeId processTypeId) => { processes.Add(new Process(processId, processTypeId, Guid.NewGuid())); - }); + }) + .Returns(new Process(processId, ProcessTypeId.TECHNICAL_USER, Guid.NewGuid())); A.CallTo(() => _processStepRepository.CreateProcessStep(A._, A._, A._)) .Invokes((ProcessStepTypeId processStepTypeId, ProcessStepStatusId processStepStatusId, Guid pId) => { - processSteps.Add(new ProcessStep(Guid.NewGuid(), processStepTypeId, processStepStatusId, processId, DateTimeOffset.UtcNow)); + processSteps.Add(new ProcessStep(Guid.NewGuid(), processStepTypeId, processStepStatusId, pId, DateTimeOffset.UtcNow)); }); A.CallTo(() => _tenantRepository.CreateTenant(A._, A._, A._, A._, A._, A._)) @@ -118,8 +137,276 @@ public async Task StartSetupDim_WithNewData_CreatesExpected(string companyName, processes.Should().ContainSingle() .Which.ProcessTypeId.Should().Be(ProcessTypeId.SETUP_DIM); processSteps.Should().ContainSingle() - .And.Satisfy(x => x.ProcessId == processId && x.ProcessStepTypeId == ProcessStepTypeId.CREATE_SUBACCOUNT); + .And.Satisfy(x => x.ProcessId == processId && x.ProcessStepTypeId == ProcessStepTypeId.CREATE_WALLET); tenants.Should().ContainSingle() .And.Satisfy(x => x.CompanyName == expectedCompanyName && x.Bpn == "BPNL00000001TEST"); } + + #endregion + + #region GetStatusList + + [Fact] + public async Task GetStatusList_WithNotExisting_ThrowsNotFoundException() + { + // Arrange + var bpn = "BPNL00000001TEST"; + A.CallTo(() => _tenantRepository.GetCompanyAndWalletDataForBpn(bpn)) + .Returns((false, null, null, GetWalletData())); + Task Act() => _sut.GetStatusList(bpn, CancellationToken.None); + + // Act + var result = await Assert.ThrowsAsync(Act); + + // Assert + result.Message.Should().Be(DimErrors.NO_COMPANY_FOR_BPN.ToString()); + } + + [Fact] + public async Task GetStatusList_WithoutCompanyId_ThrowsConflictException() + { + // Arrange + const string Bpn = "BPNL00000001TEST"; + A.CallTo(() => _tenantRepository.GetCompanyAndWalletDataForBpn(Bpn)) + .Returns((true, null, null, GetWalletData())); + Task Act() => _sut.GetStatusList(Bpn, CancellationToken.None); + + // Act + var result = await Assert.ThrowsAsync(Act); + + // Assert + result.Message.Should().Be(DimErrors.NO_COMPANY_ID_SET.ToString()); + } + + [Fact] + public async Task GetStatusList_WithBaseUrlNotSet_ThrowsConflictException() + { + // Arrange + const string Bpn = "BPNL00000001TEST"; + var companyId = Guid.NewGuid(); + A.CallTo(() => _tenantRepository.GetCompanyAndWalletDataForBpn(Bpn)) + .Returns((true, companyId, null, GetWalletData())); + Task Act() => _sut.GetStatusList(Bpn, CancellationToken.None); + + // Act + var result = await Assert.ThrowsAsync(Act); + + // Assert + result.Message.Should().Be(DimErrors.NO_BASE_URL_SET.ToString()); + } + + [Fact] + public async Task GetStatusList_WithValid_ReturnsExpected() + { + // Arrange + const string Bpn = "BPNL00000001TEST"; + const string BaseUrl = "https://example.org/base"; + var companyId = Guid.NewGuid(); + A.CallTo(() => _tenantRepository.GetCompanyAndWalletDataForBpn(Bpn)) + .Returns((true, companyId, BaseUrl, GetWalletData())); + A.CallTo(() => _dimClient.GetStatusList(A._, BaseUrl, companyId, A._)) + .Returns("https://example.org/statuslist"); + + // Act + var result = await _sut.GetStatusList(Bpn, CancellationToken.None); + + // Assert + result.Should().Be("https://example.org/statuslist"); + } + + #endregion + + #region CreateStatusList + + [Fact] + public async Task CreateStatusList_WithNotExisting_ThrowsNotFoundException() + { + // Arrange + var bpn = "BPNL00000001TEST"; + A.CallTo(() => _tenantRepository.GetCompanyAndWalletDataForBpn(bpn)) + .Returns((false, null, null, GetWalletData())); + Task Act() => _sut.CreateStatusList(bpn, CancellationToken.None); + + // Act + var result = await Assert.ThrowsAsync(Act); + + // Assert + result.Message.Should().Be(DimErrors.NO_COMPANY_FOR_BPN.ToString()); + } + + [Fact] + public async Task CreateStatusList_WithoutCompanyId_ThrowsConflictException() + { + // Arrange + const string Bpn = "BPNL00000001TEST"; + A.CallTo(() => _tenantRepository.GetCompanyAndWalletDataForBpn(Bpn)) + .Returns((true, null, null, GetWalletData())); + Task Act() => _sut.CreateStatusList(Bpn, CancellationToken.None); + + // Act + var result = await Assert.ThrowsAsync(Act); + + // Assert + result.Message.Should().Be(DimErrors.NO_COMPANY_ID_SET.ToString()); + } + + [Fact] + public async Task CreateStatusList_WithBaseUrlNotSet_ThrowsConflictException() + { + // Arrange + const string Bpn = "BPNL00000001TEST"; + var companyId = Guid.NewGuid(); + A.CallTo(() => _tenantRepository.GetCompanyAndWalletDataForBpn(Bpn)) + .Returns((true, companyId, null, GetWalletData())); + Task Act() => _sut.CreateStatusList(Bpn, CancellationToken.None); + + // Act + var result = await Assert.ThrowsAsync(Act); + + // Assert + result.Message.Should().Be(DimErrors.NO_BASE_URL_SET.ToString()); + } + + [Fact] + public async Task CreateStatusList_WithValid_ReturnsExpected() + { + // Arrange + const string Bpn = "BPNL00000001TEST"; + const string BaseUrl = "https://example.org/base"; + var companyId = Guid.NewGuid(); + A.CallTo(() => _tenantRepository.GetCompanyAndWalletDataForBpn(Bpn)) + .Returns((true, companyId, BaseUrl, GetWalletData())); + A.CallTo(() => _dimClient.CreateStatusList(A._, BaseUrl, companyId, A._)) + .Returns("https://example.org/statuslist"); + + // Act + var result = await _sut.CreateStatusList(Bpn, CancellationToken.None); + + // Assert + result.Should().Be("https://example.org/statuslist"); + } + + #endregion + + #region StartSetupDim + + [Fact] + public async Task CreateTechnicalUser_WithExisting_ThrowsNotFoundException() + { + // Arrange + const string Bpn = "BPNL00000001TEST"; + A.CallTo(() => _tenantRepository.GetTenantForBpn(Bpn)) + .Returns((false, Guid.NewGuid())); + async Task Act() => await _sut.CreateTechnicalUser(Bpn, _fixture.Create()); + + // Act + var result = await Assert.ThrowsAsync(Act); + + // Assert + result.Message.Should().Be(DimErrors.NO_COMPANY_FOR_BPN.ToString()); + } + + [Fact] + public async Task CreateTechnicalUser_WithNewData_CreatesExpected() + { + // Arrange + const string Bpn = "BPNL00001Test"; + var processId = Guid.NewGuid(); + var processes = new List(); + var processSteps = new List(); + var technicalUsers = new List(); + A.CallTo(() => _tenantRepository.GetTenantForBpn(Bpn)) + .Returns((true, Guid.NewGuid())); + A.CallTo(() => _processStepRepository.CreateProcess(A._)) + .Invokes((ProcessTypeId processTypeId) => + { + processes.Add(new Process(processId, processTypeId, Guid.NewGuid())); + }) + .Returns(new Process(processId, ProcessTypeId.TECHNICAL_USER, Guid.NewGuid())); + A.CallTo(() => _processStepRepository.CreateProcessStep(A._, A._, A._)) + .Invokes((ProcessStepTypeId processStepTypeId, ProcessStepStatusId processStepStatusId, Guid pId) => + { + processSteps.Add(new ProcessStep(Guid.NewGuid(), processStepTypeId, processStepStatusId, pId, DateTimeOffset.UtcNow)); + }); + A.CallTo(() => + _tenantRepository.CreateTenantTechnicalUser(A._, A._, A._, A._)) + .Invokes((Guid tenantId, string name, Guid externalId, Guid pId) => + { + technicalUsers.Add(new TechnicalUser(Guid.NewGuid(), tenantId, externalId, name, pId)); + }); + + // Act + await _sut.CreateTechnicalUser(Bpn, _fixture.Create()); + + // Assert + processes.Should().ContainSingle() + .Which.ProcessTypeId.Should().Be(ProcessTypeId.TECHNICAL_USER); + processSteps.Should().ContainSingle() + .And.Satisfy(x => x.ProcessId == processId && x.ProcessStepTypeId == ProcessStepTypeId.CREATE_TECHNICAL_USER); + technicalUsers.Should().ContainSingle(); + } + + #endregion + + #region StartSetupDim + + [Fact] + public async Task DeleteTechnicalUser_WithExisting_ThrowsNotFoundException() + { + // Arrange + const string Bpn = "BPNL00000001TEST"; + var technicalUserData = new TechnicalUserData(Guid.NewGuid(), "test"); + A.CallTo(() => _tenantRepository.GetTechnicalUserForBpn(Bpn, technicalUserData.Name)) + .Returns((false, Guid.NewGuid(), Guid.NewGuid())); + async Task Act() => await _sut.DeleteTechnicalUser(Bpn, technicalUserData); + + // Act + var result = await Assert.ThrowsAsync(Act); + + // Assert + result.Message.Should().Be(DimErrors.NO_TECHNICAL_USER_FOUND.ToString()); + } + + [Fact] + public async Task DeleteTechnicalUser_WithValid_DeletesExpected() + { + // Arrange + const string Bpn = "BPNL00001Test"; + var processId = Guid.NewGuid(); + var processSteps = new List(); + var technicalUserData = new TechnicalUserData(Guid.NewGuid(), "test"); + var technicalUser = new TechnicalUser(Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid(), "test", processId); + A.CallTo(() => _tenantRepository.GetTechnicalUserForBpn(Bpn, technicalUserData.Name)) + .Returns((true, technicalUser.Id, technicalUser.ProcessId)); + A.CallTo(() => _processStepRepository.CreateProcessStep(A._, A._, A._)) + .Invokes((ProcessStepTypeId processStepTypeId, ProcessStepStatusId processStepStatusId, Guid pId) => + { + processSteps.Add(new ProcessStep(Guid.NewGuid(), processStepTypeId, processStepStatusId, pId, DateTimeOffset.UtcNow)); + }); + A.CallTo(() => _tenantRepository.AttachAndModifyTechnicalUser(A._, A>._, A>._)) + .Invokes((Guid _, Action? initialize, Action modify) => + { + initialize?.Invoke(technicalUser); + modify(technicalUser); + }); + + // Act + await _sut.DeleteTechnicalUser(Bpn, technicalUserData); + + // Assert + processSteps.Should().ContainSingle() + .And.Satisfy(x => x.ProcessId == processId && x.ProcessStepTypeId == ProcessStepTypeId.DELETE_TECHNICAL_USER); + technicalUser.ProcessId.Should().Be(processId); + technicalUser.ExternalId.Should().Be(technicalUserData.ExternalId); + } + + #endregion + + private WalletData GetWalletData() + { + var cryptoHelper = _settings.EncryptionConfigs.GetCryptoHelper(0); + var (secret, initializationVector) = cryptoHelper.Encrypt("test123"); + + return new WalletData("https://example.org/token", "cl1", secret, initializationVector, 0); + } }