Installer workflow is done - Docker container is working fine

This commit is contained in:
2026-07-26 23:37:23 +05:30
parent 3cf5c83264
commit d684931bc5
1313 changed files with 778 additions and 408752 deletions

1
.gitignore vendored
View File

@@ -31,3 +31,4 @@
/cygnus-onprem-app/target /cygnus-onprem-app/target
/cygnus-cloud-client/target /cygnus-cloud-client/target
/cygnus-installer/src/test /cygnus-installer/src/test
/cygnus-cloud-client/target

13
.vscode/launch.json vendored
View File

@@ -29,7 +29,14 @@
"CYGNUS_ACCESS_TOKEN_PRIVATE_KEY": "file:${workspaceFolder}/config/keys/access-token-private.pem", "CYGNUS_ACCESS_TOKEN_PRIVATE_KEY": "file:${workspaceFolder}/config/keys/access-token-private.pem",
"CYGNUS_ACCESS_TOKEN_PUBLIC_KEY": "file:${workspaceFolder}/config/keys/access-token-public.pem", "CYGNUS_ACCESS_TOKEN_PUBLIC_KEY": "file:${workspaceFolder}/config/keys/access-token-public.pem",
"CYGNUS_LOGIN_KEY_ID": "cygnus-login-2026-01", "CYGNUS_LOGIN_KEY_ID": "cygnus-login-2026-01",
"CYGNUS_LOGIN_PRIVATE_KEY": "file:${workspaceFolder}/config/keys/login-private.pem" "CYGNUS_LOGIN_PRIVATE_KEY": "file:${workspaceFolder}/config/keys/login-private.pem",
"CYGNUS_MAIL_HOST": "smtp.gmail.com",
"CYGNUS_MAIL_PORT": "587",
"CYGNUS_MAIL_USERNAME": "technobeesolutions@gmail.com",
"CYGNUS_MAIL_PASSWORD": "lrideibfakickldg",
"CYGNUS_MAIL_SMTP_AUTH": "true",
"CYGNUS_MAIL_STARTTLS": "true",
"CYGNUS_REGISTRATION_EMAIL_FROM": "technobeesolutions@gmail.com"
}, },
"shortenCommandLine": "argfile" "shortenCommandLine": "argfile"
}, },
@@ -57,9 +64,9 @@
"CYGNUS_TOKEN_URL": "http://localhost:8090/oauth2/token", "CYGNUS_TOKEN_URL": "http://localhost:8090/oauth2/token",
"CYGNUS_CLIENT_ID": "matrix", "CYGNUS_CLIENT_ID": "matrix",
"CYGNUS_INSTALLATION_ID": "matrix-delhi-cygnus-01", "CYGNUS_INSTALLATION_ID": "matrix-delhi-cygnus-01",
"CYGNUS_CLIENT_ASSERTION": "file:${workspaceFolder}/config/clients/matrix/matrix-matrix-delhi-cygnus-01-assertion.jwt", "CYGNUS_CLIENT_ASSERTION": "file:${workspaceFolder}/matrix-installation/config/machine-assertion.jwt",
"CYGNUS_LOGIN_KEY_ID": "cygnus-login-2026-01", "CYGNUS_LOGIN_KEY_ID": "cygnus-login-2026-01",
"CYGNUS_LOGIN_PUBLIC_KEY": "file:${workspaceFolder}/config/keys/login-public.pem", "CYGNUS_LOGIN_PUBLIC_KEY": "file:${workspaceFolder}/matrix-installation/config/keys/login-public.pem",
"CYGNUS_CLOUD_REQUEST_TIMEOUT": "PT10S", "CYGNUS_CLOUD_REQUEST_TIMEOUT": "PT10S",
"CYGNUS_TOKEN_REFRESH_SKEW": "PT30S" "CYGNUS_TOKEN_REFRESH_SKEW": "PT30S"
}, },

View File

@@ -1,3 +0,0 @@
artifactId=cygnus-cloud-client
groupId=com.cygnus
version=1.0.0-SNAPSHOT

View File

@@ -1,14 +0,0 @@
com/cygnus/client/security/MachineTokenProvider.class
com/cygnus/client/security/OAuthMachineTokenProvider$TokenResponse.class
com/cygnus/client/provisioning/MachineAssertionGenerator.class
com/cygnus/client/CloudClientProperties.class
com/cygnus/client/security/LoginEnvelopeEncryptor.class
com/cygnus/client/model/LoginPayload.class
com/cygnus/client/model/CloudIdentitySession.class
com/cygnus/client/security/OAuthMachineTokenProvider.class
com/cygnus/client/security/OAuthMachineTokenProvider$CachedToken.class
com/cygnus/client/CloudClientFactory.class
com/cygnus/client/model/CloudMenuItem.class
com/cygnus/client/model/EncryptedLoginRequest.class
com/cygnus/client/security/CloudClientException.class
com/cygnus/client/CloudIdentityClient.class

View File

@@ -1,12 +0,0 @@
/Users/maddy/Projects/matrix/cygnus-cloud-client/src/main/java/com/cygnus/client/CloudClientFactory.java
/Users/maddy/Projects/matrix/cygnus-cloud-client/src/main/java/com/cygnus/client/CloudClientProperties.java
/Users/maddy/Projects/matrix/cygnus-cloud-client/src/main/java/com/cygnus/client/CloudIdentityClient.java
/Users/maddy/Projects/matrix/cygnus-cloud-client/src/main/java/com/cygnus/client/model/CloudIdentitySession.java
/Users/maddy/Projects/matrix/cygnus-cloud-client/src/main/java/com/cygnus/client/model/CloudMenuItem.java
/Users/maddy/Projects/matrix/cygnus-cloud-client/src/main/java/com/cygnus/client/model/EncryptedLoginRequest.java
/Users/maddy/Projects/matrix/cygnus-cloud-client/src/main/java/com/cygnus/client/model/LoginPayload.java
/Users/maddy/Projects/matrix/cygnus-cloud-client/src/main/java/com/cygnus/client/provisioning/MachineAssertionGenerator.java
/Users/maddy/Projects/matrix/cygnus-cloud-client/src/main/java/com/cygnus/client/security/CloudClientException.java
/Users/maddy/Projects/matrix/cygnus-cloud-client/src/main/java/com/cygnus/client/security/LoginEnvelopeEncryptor.java
/Users/maddy/Projects/matrix/cygnus-cloud-client/src/main/java/com/cygnus/client/security/MachineTokenProvider.java
/Users/maddy/Projects/matrix/cygnus-cloud-client/src/main/java/com/cygnus/client/security/OAuthMachineTokenProvider.java

View File

@@ -1 +0,0 @@
com/cygnus/client/CloudClientPropertiesTest.class

View File

@@ -1 +0,0 @@
/Users/maddy/Projects/matrix/cygnus-cloud-client/src/test/java/com/cygnus/client/CloudClientPropertiesTest.java

View File

@@ -1,63 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<testsuite xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://maven.apache.org/surefire/maven-surefire-plugin/xsd/surefire-test-report.xsd" version="3.0.2" name="com.cygnus.client.CloudClientPropertiesTest" time="0.016" tests="2" errors="0" skipped="0" failures="0">
<properties>
<property name="java.specification.version" value="21"/>
<property name="sun.jnu.encoding" value="UTF-8"/>
<property name="java.class.path" value="/Users/maddy/Projects/matrix/cygnus-cloud-client/target/test-classes:/Users/maddy/Projects/matrix/cygnus-cloud-client/target/classes:/Users/maddy/.m2/repository/org/springframework/spring-webflux/6.2.19/spring-webflux-6.2.19.jar:/Users/maddy/.m2/repository/org/springframework/spring-beans/6.2.19/spring-beans-6.2.19.jar:/Users/maddy/.m2/repository/org/springframework/spring-core/6.2.19/spring-core-6.2.19.jar:/Users/maddy/.m2/repository/org/springframework/spring-jcl/6.2.19/spring-jcl-6.2.19.jar:/Users/maddy/.m2/repository/org/springframework/spring-web/6.2.19/spring-web-6.2.19.jar:/Users/maddy/.m2/repository/io/micrometer/micrometer-observation/1.15.12/micrometer-observation-1.15.12.jar:/Users/maddy/.m2/repository/io/micrometer/micrometer-commons/1.15.12/micrometer-commons-1.15.12.jar:/Users/maddy/.m2/repository/io/projectreactor/reactor-core/3.7.19/reactor-core-3.7.19.jar:/Users/maddy/.m2/repository/org/reactivestreams/reactive-streams/1.0.4/reactive-streams-1.0.4.jar:/Users/maddy/.m2/repository/io/projectreactor/netty/reactor-netty-http/1.2.8/reactor-netty-http-1.2.8.jar:/Users/maddy/.m2/repository/io/netty/netty-codec-http/4.1.122.Final/netty-codec-http-4.1.122.Final.jar:/Users/maddy/.m2/repository/io/netty/netty-common/4.1.122.Final/netty-common-4.1.122.Final.jar:/Users/maddy/.m2/repository/io/netty/netty-buffer/4.1.122.Final/netty-buffer-4.1.122.Final.jar:/Users/maddy/.m2/repository/io/netty/netty-transport/4.1.122.Final/netty-transport-4.1.122.Final.jar:/Users/maddy/.m2/repository/io/netty/netty-codec/4.1.122.Final/netty-codec-4.1.122.Final.jar:/Users/maddy/.m2/repository/io/netty/netty-handler/4.1.122.Final/netty-handler-4.1.122.Final.jar:/Users/maddy/.m2/repository/io/netty/netty-codec-http2/4.1.122.Final/netty-codec-http2-4.1.122.Final.jar:/Users/maddy/.m2/repository/io/netty/netty-resolver-dns/4.1.122.Final/netty-resolver-dns-4.1.122.Final.jar:/Users/maddy/.m2/repository/io/netty/netty-resolver/4.1.122.Final/netty-resolver-4.1.122.Final.jar:/Users/maddy/.m2/repository/io/netty/netty-codec-dns/4.1.122.Final/netty-codec-dns-4.1.122.Final.jar:/Users/maddy/.m2/repository/io/netty/netty-resolver-dns-native-macos/4.1.122.Final/netty-resolver-dns-native-macos-4.1.122.Final-osx-x86_64.jar:/Users/maddy/.m2/repository/io/netty/netty-resolver-dns-classes-macos/4.1.122.Final/netty-resolver-dns-classes-macos-4.1.122.Final.jar:/Users/maddy/.m2/repository/io/netty/netty-transport-native-epoll/4.1.122.Final/netty-transport-native-epoll-4.1.122.Final-linux-x86_64.jar:/Users/maddy/.m2/repository/io/netty/netty-transport-native-unix-common/4.1.122.Final/netty-transport-native-unix-common-4.1.122.Final.jar:/Users/maddy/.m2/repository/io/netty/netty-transport-classes-epoll/4.1.122.Final/netty-transport-classes-epoll-4.1.122.Final.jar:/Users/maddy/.m2/repository/io/projectreactor/netty/reactor-netty-core/1.2.8/reactor-netty-core-1.2.8.jar:/Users/maddy/.m2/repository/io/netty/netty-handler-proxy/4.1.122.Final/netty-handler-proxy-4.1.122.Final.jar:/Users/maddy/.m2/repository/io/netty/netty-codec-socks/4.1.122.Final/netty-codec-socks-4.1.122.Final.jar:/Users/maddy/.m2/repository/com/fasterxml/jackson/core/jackson-databind/2.18.6/jackson-databind-2.18.6.jar:/Users/maddy/.m2/repository/com/fasterxml/jackson/core/jackson-annotations/2.18.6/jackson-annotations-2.18.6.jar:/Users/maddy/.m2/repository/com/fasterxml/jackson/core/jackson-core/2.18.6/jackson-core-2.18.6.jar:/Users/maddy/.m2/repository/com/nimbusds/nimbus-jose-jwt/10.4/nimbus-jose-jwt-10.4.jar:/Users/maddy/.m2/repository/org/junit/jupiter/junit-jupiter/5.12.2/junit-jupiter-5.12.2.jar:/Users/maddy/.m2/repository/org/junit/jupiter/junit-jupiter-api/5.12.2/junit-jupiter-api-5.12.2.jar:/Users/maddy/.m2/repository/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar:/Users/maddy/.m2/repository/org/junit/platform/junit-platform-commons/1.12.2/junit-platform-commons-1.12.2.jar:/Users/maddy/.m2/repository/org/apiguardian/apiguardian-api/1.1.2/apiguardian-api-1.1.2.jar:/Users/maddy/.m2/repository/org/junit/jupiter/junit-jupiter-params/5.12.2/junit-jupiter-params-5.12.2.jar:/Users/maddy/.m2/repository/org/junit/jupiter/junit-jupiter-engine/5.12.2/junit-jupiter-engine-5.12.2.jar:/Users/maddy/.m2/repository/org/junit/platform/junit-platform-engine/1.12.2/junit-platform-engine-1.12.2.jar:"/>
<property name="java.vm.vendor" value="Microsoft"/>
<property name="sun.arch.data.model" value="64"/>
<property name="java.vendor.url" value="https://www.microsoft.com"/>
<property name="os.name" value="Mac OS X"/>
<property name="java.vm.specification.version" value="21"/>
<property name="sun.java.launcher" value="SUN_STANDARD"/>
<property name="user.country" value="US"/>
<property name="sun.boot.library.path" value="/Users/maddy/Library/Java/JavaVirtualMachines/ms-21.0.8/Contents/Home/lib"/>
<property name="sun.java.command" value="/Users/maddy/Projects/matrix/cygnus-cloud-client/target/surefire/surefirebooter-20260726165419825_10.jar /Users/maddy/Projects/matrix/cygnus-cloud-client/target/surefire 2026-07-26T16-54-14_594-jvmRun1 surefire-20260726165419825_8tmp surefire_1-20260726165419825_9tmp"/>
<property name="http.nonProxyHosts" value="local|*.local|169.254/16|*.169.254/16"/>
<property name="jdk.debug" value="release"/>
<property name="surefire.test.class.path" value="/Users/maddy/Projects/matrix/cygnus-cloud-client/target/test-classes:/Users/maddy/Projects/matrix/cygnus-cloud-client/target/classes:/Users/maddy/.m2/repository/org/springframework/spring-webflux/6.2.19/spring-webflux-6.2.19.jar:/Users/maddy/.m2/repository/org/springframework/spring-beans/6.2.19/spring-beans-6.2.19.jar:/Users/maddy/.m2/repository/org/springframework/spring-core/6.2.19/spring-core-6.2.19.jar:/Users/maddy/.m2/repository/org/springframework/spring-jcl/6.2.19/spring-jcl-6.2.19.jar:/Users/maddy/.m2/repository/org/springframework/spring-web/6.2.19/spring-web-6.2.19.jar:/Users/maddy/.m2/repository/io/micrometer/micrometer-observation/1.15.12/micrometer-observation-1.15.12.jar:/Users/maddy/.m2/repository/io/micrometer/micrometer-commons/1.15.12/micrometer-commons-1.15.12.jar:/Users/maddy/.m2/repository/io/projectreactor/reactor-core/3.7.19/reactor-core-3.7.19.jar:/Users/maddy/.m2/repository/org/reactivestreams/reactive-streams/1.0.4/reactive-streams-1.0.4.jar:/Users/maddy/.m2/repository/io/projectreactor/netty/reactor-netty-http/1.2.8/reactor-netty-http-1.2.8.jar:/Users/maddy/.m2/repository/io/netty/netty-codec-http/4.1.122.Final/netty-codec-http-4.1.122.Final.jar:/Users/maddy/.m2/repository/io/netty/netty-common/4.1.122.Final/netty-common-4.1.122.Final.jar:/Users/maddy/.m2/repository/io/netty/netty-buffer/4.1.122.Final/netty-buffer-4.1.122.Final.jar:/Users/maddy/.m2/repository/io/netty/netty-transport/4.1.122.Final/netty-transport-4.1.122.Final.jar:/Users/maddy/.m2/repository/io/netty/netty-codec/4.1.122.Final/netty-codec-4.1.122.Final.jar:/Users/maddy/.m2/repository/io/netty/netty-handler/4.1.122.Final/netty-handler-4.1.122.Final.jar:/Users/maddy/.m2/repository/io/netty/netty-codec-http2/4.1.122.Final/netty-codec-http2-4.1.122.Final.jar:/Users/maddy/.m2/repository/io/netty/netty-resolver-dns/4.1.122.Final/netty-resolver-dns-4.1.122.Final.jar:/Users/maddy/.m2/repository/io/netty/netty-resolver/4.1.122.Final/netty-resolver-4.1.122.Final.jar:/Users/maddy/.m2/repository/io/netty/netty-codec-dns/4.1.122.Final/netty-codec-dns-4.1.122.Final.jar:/Users/maddy/.m2/repository/io/netty/netty-resolver-dns-native-macos/4.1.122.Final/netty-resolver-dns-native-macos-4.1.122.Final-osx-x86_64.jar:/Users/maddy/.m2/repository/io/netty/netty-resolver-dns-classes-macos/4.1.122.Final/netty-resolver-dns-classes-macos-4.1.122.Final.jar:/Users/maddy/.m2/repository/io/netty/netty-transport-native-epoll/4.1.122.Final/netty-transport-native-epoll-4.1.122.Final-linux-x86_64.jar:/Users/maddy/.m2/repository/io/netty/netty-transport-native-unix-common/4.1.122.Final/netty-transport-native-unix-common-4.1.122.Final.jar:/Users/maddy/.m2/repository/io/netty/netty-transport-classes-epoll/4.1.122.Final/netty-transport-classes-epoll-4.1.122.Final.jar:/Users/maddy/.m2/repository/io/projectreactor/netty/reactor-netty-core/1.2.8/reactor-netty-core-1.2.8.jar:/Users/maddy/.m2/repository/io/netty/netty-handler-proxy/4.1.122.Final/netty-handler-proxy-4.1.122.Final.jar:/Users/maddy/.m2/repository/io/netty/netty-codec-socks/4.1.122.Final/netty-codec-socks-4.1.122.Final.jar:/Users/maddy/.m2/repository/com/fasterxml/jackson/core/jackson-databind/2.18.6/jackson-databind-2.18.6.jar:/Users/maddy/.m2/repository/com/fasterxml/jackson/core/jackson-annotations/2.18.6/jackson-annotations-2.18.6.jar:/Users/maddy/.m2/repository/com/fasterxml/jackson/core/jackson-core/2.18.6/jackson-core-2.18.6.jar:/Users/maddy/.m2/repository/com/nimbusds/nimbus-jose-jwt/10.4/nimbus-jose-jwt-10.4.jar:/Users/maddy/.m2/repository/org/junit/jupiter/junit-jupiter/5.12.2/junit-jupiter-5.12.2.jar:/Users/maddy/.m2/repository/org/junit/jupiter/junit-jupiter-api/5.12.2/junit-jupiter-api-5.12.2.jar:/Users/maddy/.m2/repository/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar:/Users/maddy/.m2/repository/org/junit/platform/junit-platform-commons/1.12.2/junit-platform-commons-1.12.2.jar:/Users/maddy/.m2/repository/org/apiguardian/apiguardian-api/1.1.2/apiguardian-api-1.1.2.jar:/Users/maddy/.m2/repository/org/junit/jupiter/junit-jupiter-params/5.12.2/junit-jupiter-params-5.12.2.jar:/Users/maddy/.m2/repository/org/junit/jupiter/junit-jupiter-engine/5.12.2/junit-jupiter-engine-5.12.2.jar:/Users/maddy/.m2/repository/org/junit/platform/junit-platform-engine/1.12.2/junit-platform-engine-1.12.2.jar:"/>
<property name="sun.cpu.endian" value="little"/>
<property name="user.home" value="/Users/maddy"/>
<property name="user.language" value="en"/>
<property name="java.specification.vendor" value="Oracle Corporation"/>
<property name="java.version.date" value="2025-07-15"/>
<property name="java.home" value="/Users/maddy/Library/Java/JavaVirtualMachines/ms-21.0.8/Contents/Home"/>
<property name="file.separator" value="/"/>
<property name="basedir" value="/Users/maddy/Projects/matrix/cygnus-cloud-client"/>
<property name="java.vm.compressedOopsMode" value="Zero based"/>
<property name="line.separator" value="&#10;"/>
<property name="java.vm.specification.vendor" value="Oracle Corporation"/>
<property name="java.specification.name" value="Java Platform API Specification"/>
<property name="apple.awt.application.name" value="ForkedBooter"/>
<property name="surefire.real.class.path" value="/Users/maddy/Projects/matrix/cygnus-cloud-client/target/surefire/surefirebooter-20260726165419825_10.jar"/>
<property name="sun.management.compiler" value="HotSpot 64-Bit Tiered Compilers"/>
<property name="ftp.nonProxyHosts" value="local|*.local|169.254/16|*.169.254/16"/>
<property name="java.runtime.version" value="21.0.8+9-LTS"/>
<property name="user.name" value="maddy"/>
<property name="stdout.encoding" value="UTF-8"/>
<property name="path.separator" value=":"/>
<property name="os.version" value="26.5.2"/>
<property name="java.runtime.name" value="OpenJDK Runtime Environment"/>
<property name="file.encoding" value="UTF-8"/>
<property name="java.vm.name" value="OpenJDK 64-Bit Server VM"/>
<property name="java.vendor.version" value="Microsoft-11933201"/>
<property name="localRepository" value="/Users/maddy/.m2/repository"/>
<property name="java.vendor.url.bug" value="https://github.com/microsoft/openjdk/issues"/>
<property name="java.io.tmpdir" value="/var/folders/1l/36214rdn79755j30lcnmgsqh0000gn/T/"/>
<property name="java.version" value="21.0.8"/>
<property name="user.dir" value="/Users/maddy/Projects/matrix/cygnus-cloud-client"/>
<property name="os.arch" value="aarch64"/>
<property name="java.vm.specification.name" value="Java Virtual Machine Specification"/>
<property name="native.encoding" value="UTF-8"/>
<property name="java.library.path" value="/Users/maddy/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:."/>
<property name="java.vm.info" value="mixed mode, sharing"/>
<property name="stderr.encoding" value="UTF-8"/>
<property name="java.vendor" value="Microsoft"/>
<property name="java.vm.version" value="21.0.8+9-LTS"/>
<property name="sun.io.unicode.encoding" value="UnicodeBig"/>
<property name="socksNonProxyHosts" value="local|*.local|169.254/16|*.169.254/16"/>
<property name="java.class.version" value="65.0"/>
</properties>
<testcase name="configurationAcceptsCompleteMachineIdentity" classname="com.cygnus.client.CloudClientPropertiesTest" time="0.006"/>
<testcase name="configurationRequiresMachineCredentials" classname="com.cygnus.client.CloudClientPropertiesTest" time="0.001"/>
</testsuite>

View File

@@ -1,4 +0,0 @@
-------------------------------------------------------------------------------
Test set: com.cygnus.client.CloudClientPropertiesTest
-------------------------------------------------------------------------------
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.016 s -- in com.cygnus.client.CloudClientPropertiesTest

View File

@@ -2,6 +2,8 @@ package com.cygnus.cloud.identity.api;
import com.cygnus.cloud.identity.service.AuthenticationException; import com.cygnus.cloud.identity.service.AuthenticationException;
import java.util.Map; import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.ResponseStatus;
@@ -9,10 +11,12 @@ import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice @RestControllerAdvice
public class IdentityErrorHandler { public class IdentityErrorHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(IdentityErrorHandler.class);
@ExceptionHandler(AuthenticationException.class) @ExceptionHandler(AuthenticationException.class)
@ResponseStatus(HttpStatus.UNAUTHORIZED) @ResponseStatus(HttpStatus.UNAUTHORIZED)
Map<String, String> authenticationFailure() { Map<String, String> authenticationFailure(AuthenticationException exception) {
LOGGER.warn("Identity authentication rejected: {}", exception.getMessage());
return Map.of("code", "AUTHENTICATION_FAILED", "message", "Authentication failed"); return Map.of("code", "AUTHENTICATION_FAILED", "message", "Authentication failed");
} }
} }

View File

@@ -38,8 +38,8 @@ public class ClientAdministrationController {
@PostMapping("/tenants/{tenantId}/installations/{installationId}/decommission") @PostMapping("/tenants/{tenantId}/installations/{installationId}/decommission")
public Mono<?> decommission( public Mono<?> decommission(
@PathVariable UUID tenantId, @PathVariable("tenantId") UUID tenantId,
@PathVariable UUID installationId, @PathVariable("installationId") UUID installationId,
@Valid @RequestBody RetireInstallationRequest request, @Valid @RequestBody RetireInstallationRequest request,
Authentication authentication) { Authentication authentication) {
return installationLifecycleService.retire( return installationLifecycleService.retire(
@@ -52,8 +52,8 @@ public class ClientAdministrationController {
@PostMapping("/tenants/{tenantId}/installations/{installationId}/revoke") @PostMapping("/tenants/{tenantId}/installations/{installationId}/revoke")
public Mono<?> revoke( public Mono<?> revoke(
@PathVariable UUID tenantId, @PathVariable("tenantId") UUID tenantId,
@PathVariable UUID installationId, @PathVariable("installationId") UUID installationId,
@Valid @RequestBody RetireInstallationRequest request, @Valid @RequestBody RetireInstallationRequest request,
Authentication authentication) { Authentication authentication) {
return installationLifecycleService.retire( return installationLifecycleService.retire(
@@ -103,7 +103,7 @@ public class ClientAdministrationController {
@PostMapping("/client-registrations/{registrationId}/tenants") @PostMapping("/client-registrations/{registrationId}/tenants")
public Mono<Map<String, UUID>> tenant( public Mono<Map<String, UUID>> tenant(
@PathVariable UUID registrationId, @PathVariable("registrationId") UUID registrationId,
@Valid @RequestBody CreateTenantRequest request) { @Valid @RequestBody CreateTenantRequest request) {
UUID id = UUID.randomUUID(); UUID id = UUID.randomUUID();
return repository.createTenant( return repository.createTenant(
@@ -115,7 +115,7 @@ public class ClientAdministrationController {
@PostMapping("/tenants/{tenantId}/licenses") @PostMapping("/tenants/{tenantId}/licenses")
public Mono<Map<String, UUID>> license( public Mono<Map<String, UUID>> license(
@PathVariable UUID tenantId, @PathVariable("tenantId") UUID tenantId,
@Valid @RequestBody CreateLicenseRequest request) { @Valid @RequestBody CreateLicenseRequest request) {
if (!request.validUntil().isAfter(request.validFrom())) { if (!request.validUntil().isAfter(request.validFrom())) {
return Mono.error(new IllegalArgumentException( return Mono.error(new IllegalArgumentException(
@@ -138,8 +138,8 @@ public class ClientAdministrationController {
@PostMapping("/tenants/{tenantId}/licenses/{licenseId}/activation-key") @PostMapping("/tenants/{tenantId}/licenses/{licenseId}/activation-key")
public Mono<Map<String, String>> activationKey( public Mono<Map<String, String>> activationKey(
@PathVariable UUID tenantId, @PathVariable("tenantId") UUID tenantId,
@PathVariable UUID licenseId, @PathVariable("licenseId") UUID licenseId,
@Valid @RequestBody IssueLicenseKeyRequest request, @Valid @RequestBody IssueLicenseKeyRequest request,
Authentication authentication) { Authentication authentication) {
return licenseKeyService.issue( return licenseKeyService.issue(
@@ -149,7 +149,7 @@ public class ClientAdministrationController {
.switchIfEmpty(Mono.error( .switchIfEmpty(Mono.error(
new IllegalArgumentException("Tenant or license not found"))) new IllegalArgumentException("Tenant or license not found")))
.flatMap(details -> emailService.sendLicense( .flatMap(details -> emailService.sendLicense(
request.recipientEmail(), details.primaryContactEmail(),
details.clientCode(), details.clientCode(),
details.tenantSlug(), details.tenantSlug(),
details.packageCode(), details.packageCode(),

View File

@@ -1,6 +1,7 @@
package com.cygnus.cloud.tenant.api; package com.cygnus.cloud.tenant.api;
import com.cygnus.cloud.tenant.service.InstallationActivationException; import com.cygnus.cloud.tenant.service.InstallationActivationException;
import com.cygnus.cloud.tenant.service.InstallationCodeConflictException;
import com.cygnus.cloud.tenant.service.LicenseKeyException; import com.cygnus.cloud.tenant.service.LicenseKeyException;
import com.cygnus.cloud.tenant.service.ActivationRateLimitException; import com.cygnus.cloud.tenant.service.ActivationRateLimitException;
import java.util.Map; import java.util.Map;
@@ -12,6 +13,15 @@ import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice @RestControllerAdvice
public class InstallationActivationErrorHandler { public class InstallationActivationErrorHandler {
@ExceptionHandler(InstallationCodeConflictException.class)
@ResponseStatus(HttpStatus.CONFLICT)
Map<String, String> installationCodeConflict(
InstallationCodeConflictException exception) {
return Map.of(
"code", "INSTALLATION_CODE_ALREADY_EXISTS",
"message", exception.getMessage());
}
@ExceptionHandler({LicenseKeyException.class, InstallationActivationException.class}) @ExceptionHandler({LicenseKeyException.class, InstallationActivationException.class})
@ResponseStatus(HttpStatus.BAD_REQUEST) @ResponseStatus(HttpStatus.BAD_REQUEST)
Map<String, String> activationFailure() { Map<String, String> activationFailure() {

View File

@@ -1,11 +1,8 @@
package com.cygnus.cloud.tenant.api; package com.cygnus.cloud.tenant.api;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
public record IssueLicenseKeyRequest( public record IssueLicenseKeyRequest(
@NotNull OffsetDateTime expiresAt, @NotNull OffsetDateTime expiresAt) {
@NotBlank @Email String recipientEmail) {
} }

View File

@@ -4,4 +4,5 @@ public record LicenseDeliveryDetails(
String clientCode, String clientCode,
String tenantSlug, String tenantSlug,
String packageCode, String packageCode,
int maximumInstallations) {} int maximumInstallations,
String primaryContactEmail) {}

View File

@@ -46,6 +46,7 @@ public class ClientAdministrationRepository {
private static final String FIND_LICENSE_DELIVERY_DETAILS = """ private static final String FIND_LICENSE_DELIVERY_DETAILS = """
SELECT registration.client_code, SELECT registration.client_code,
registration.primary_contact_email,
account.client_slug, account.client_slug,
license.package_code, license.package_code,
license.max_installations license.max_installations
@@ -118,7 +119,8 @@ public class ClientAdministrationRepository {
row.getString("client_code"), row.getString("client_code"),
row.getString("client_slug"), row.getString("client_slug"),
row.getString("package_code"), row.getString("package_code"),
row.getInteger("max_installations"))); row.getInteger("max_installations"),
row.getString("primary_contact_email")));
}); });
} }
} }

View File

@@ -20,7 +20,8 @@ public class InstallationActivationRepository {
activation_session_id, activation_key_id, registration_id, activation_session_id, activation_key_id, registration_id,
tenant_id, license_id, installation_uuid, token_hash, tenant_id, license_id, installation_uuid, token_hash,
status, expires_at, source_ip, installer_version) status, expires_at, source_ip, installer_version)
VALUES ($1, $2, $3, $4, $5, $6, $7, 'PENDING', $8, $9::inet, $10) VALUES ($1, $2, $3, $4, $5, $6, $7, 'PENDING', $8,
CAST($9 AS text)::inet, $10)
"""; """;
private static final String LOCK_SESSION_AND_LICENSE = """ private static final String LOCK_SESSION_AND_LICENSE = """

View File

@@ -94,7 +94,10 @@ public class LicenseActivationRepository {
private static final String REVOKE_KEY = """ private static final String REVOKE_KEY = """
UPDATE identity.license_activation_key UPDATE identity.license_activation_key
SET status = 'REVOKED', SET status = 'REVOKED',
locked_until = NULL locked_until = NULL,
revoked_at = now(),
revoked_by = 'system',
revocation_reason = 'Activation-key delivery failed'
WHERE activation_key_id = $1 WHERE activation_key_id = $1
AND status IN ('ACTIVE', 'LOCKED') AND status IN ('ACTIVE', 'LOCKED')
"""; """;

View File

@@ -17,6 +17,7 @@ import java.time.ZoneOffset;
import java.util.Base64; import java.util.Base64;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import io.vertx.pgclient.PgException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
@@ -83,6 +84,11 @@ public class InstallationActivationService {
softwareVersion, softwareVersion,
environment, environment,
now) now)
.onErrorMap(
error -> error instanceof PgException pgException
&& "23505".equals(pgException.getSqlState()),
error -> new InstallationCodeConflictException(
installationCode))
.onErrorMap( .onErrorMap(
error -> !(error instanceof InstallationActivationException), error -> !(error instanceof InstallationActivationException),
error -> new InstallationActivationException( error -> new InstallationActivationException(

View File

@@ -0,0 +1,9 @@
package com.cygnus.cloud.tenant.service;
public class InstallationCodeConflictException
extends InstallationActivationException {
public InstallationCodeConflictException(String installationCode) {
super("Installation code already exists: " + installationCode);
}
}

View File

@@ -33,6 +33,10 @@ public class RegistrationEmailService {
OffsetDateTime validUntil, OffsetDateTime validUntil,
IssuedLicenseKey issued) { IssuedLicenseKey issued) {
return Mono.fromRunnable(() -> { return Mono.fromRunnable(() -> {
if (recipient == null || recipient.isBlank()) {
throw new IllegalStateException(
"The client registration has no primary contact email");
}
JavaMailSender mailSender = mailSenderProvider.getIfAvailable(); JavaMailSender mailSender = mailSenderProvider.getIfAvailable();
if (mailSender == null) { if (mailSender == null) {
throw new IllegalStateException( throw new IllegalStateException(

View File

@@ -0,0 +1,49 @@
package com.cygnus.cloud.tenant.repository;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.cygnus.cloud.database.ReactiveDatabaseClient;
import io.vertx.sqlclient.Tuple;
import java.time.OffsetDateTime;
import java.util.UUID;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import reactor.core.publisher.Mono;
class InstallationActivationRepositoryTest {
@Test
void sourceIpIsBoundAsTextBeforePostgresInetConversion() {
ReactiveDatabaseClient database = mock(ReactiveDatabaseClient.class);
when(database.preparedUpdate(anyString(), any(Tuple.class)))
.thenReturn(Mono.just(1));
InstallationActivationRepository repository =
new InstallationActivationRepository(database);
assertThat(repository.insertSession(
UUID.randomUUID(),
UUID.randomUUID(),
UUID.randomUUID(),
UUID.randomUUID(),
UUID.randomUUID(),
UUID.randomUUID(),
"token-hash",
OffsetDateTime.now().plusMinutes(10),
"127.0.0.1",
"1.0.0")
.block())
.isTrue();
ArgumentCaptor<String> sql = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<Tuple> parameters = ArgumentCaptor.forClass(Tuple.class);
verify(database).preparedUpdate(sql.capture(), parameters.capture());
assertThat(sql.getValue()).contains("CAST($9 AS text)::inet");
assertThat(parameters.getValue().getString(8)).isEqualTo("127.0.0.1");
}
}

View File

@@ -0,0 +1,37 @@
package com.cygnus.cloud.tenant.repository;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.cygnus.cloud.database.ReactiveDatabaseClient;
import io.vertx.sqlclient.Tuple;
import java.util.UUID;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import reactor.core.publisher.Mono;
class LicenseActivationRepositoryTest {
@Test
void revocationPopulatesRequiredAuditColumns() {
ReactiveDatabaseClient database = mock(ReactiveDatabaseClient.class);
when(database.preparedUpdate(anyString(), any(Tuple.class)))
.thenReturn(Mono.just(1));
LicenseActivationRepository repository =
new LicenseActivationRepository(database);
assertThat(repository.revoke(UUID.randomUUID()).block()).isTrue();
ArgumentCaptor<String> sql = ArgumentCaptor.forClass(String.class);
verify(database).preparedUpdate(sql.capture(), any(Tuple.class));
assertThat(sql.getValue())
.contains("status = 'REVOKED'")
.contains("revoked_at = now()")
.contains("revoked_by = 'system'")
.contains("revocation_reason = 'Activation-key delivery failed'");
}
}

View File

@@ -0,0 +1,72 @@
package com.cygnus.cloud.tenant.service;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.cygnus.cloud.tenant.model.IssuedLicenseKey;
import java.time.OffsetDateTime;
import java.util.UUID;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
class RegistrationEmailServiceTest {
@Test
void sendsActivationKeyToRegisteredPrimaryContact() {
JavaMailSender mailSender = mock(JavaMailSender.class);
@SuppressWarnings("unchecked")
ObjectProvider<JavaMailSender> provider = mock(ObjectProvider.class);
when(provider.getIfAvailable()).thenReturn(mailSender);
RegistrationEmailService service =
new RegistrationEmailService(provider, "noreply@technobee.test");
service.sendLicense(
"registered@example.com",
"MATRIX",
"matrix",
"STANDARD",
1,
OffsetDateTime.parse("2027-07-26T23:59:59+05:30"),
new IssuedLicenseKey(
UUID.randomUUID(),
"CYGNUS-TEST-LICENSE-KEY",
"ENSEKEY"))
.block();
ArgumentCaptor<SimpleMailMessage> message =
ArgumentCaptor.forClass(SimpleMailMessage.class);
verify(mailSender).send(message.capture());
assertThat(message.getValue().getTo()).containsExactly("registered@example.com");
assertThat(message.getValue().getText()).contains("CYGNUS-TEST-LICENSE-KEY");
}
@Test
void rejectsRegistrationWithoutPrimaryContactEmail() {
@SuppressWarnings("unchecked")
ObjectProvider<JavaMailSender> provider = mock(ObjectProvider.class);
RegistrationEmailService service =
new RegistrationEmailService(provider, "noreply@technobee.test");
assertThatThrownBy(() -> service.sendLicense(
" ",
"MATRIX",
"matrix",
"STANDARD",
1,
OffsetDateTime.parse("2027-07-26T23:59:59+05:30"),
new IssuedLicenseKey(
UUID.randomUUID(),
"CYGNUS-TEST-LICENSE-KEY",
"ENSEKEY"))
.block())
.isInstanceOf(IllegalStateException.class)
.hasMessage(
"The client registration has no primary contact email");
}
}

Some files were not shown because too many files have changed in this diff Show More