diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 80cf7eac3487ef51c800b092b202f377acdcc21b..2c93f8a1fab6ddd392cf8ff03049744e24528288 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -66,7 +66,7 @@ build_and_push_release_image: mend: stage: mend - image: eclipse-temurin:17-jre-alpine + image: openjdk:17-jdk-slim only: - /^release/ variables: @@ -76,5 +76,7 @@ mend: - | export PRODUCT_VERSION=$(echo $CI_COMMIT_BRANCH | cut -c 9-) export PROJECT_VERSION=$PRODUCT_VERSION + apt-get update && apt-get install -y curl + chmod +x ./gradlew curl -LJO https://github.com/whitesource/unified-agent-distribution/releases/latest/download/wss-unified-agent.jar - java -jar wss-unified-agent.jar -userKey ${MEND_USER_KEY} -apiKey ${MEND_API_KEY} -projectVersion ${PROJECT_VERSION} -project ${PROJECT_NAME} -productVersion ${PRODUCT_VERSION} -product ${PRODUCT_NAME} -c ws.config -d ../ \ No newline at end of file + java -jar wss-unified-agent.jar -userKey ${MEND_USER_KEY} -apiKey ${MEND_API_KEY} -projectVersion ${PROJECT_VERSION} -project ${PROJECT_NAME} -productVersion ${PRODUCT_VERSION} -product ${PRODUCT_NAME} -c ./ws/ws.config -d ./ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 4ca00dbd4ae0a658a70725626045222025694c0c..f2bf2bee365374eb5dabaced18ea6d6ba8a13dfd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,20 +4,41 @@ COPY . /build/ WORKDIR /build/ RUN chmod +x ./gradlew \ - && ./gradlew -Dorg.gradle.daemon=false build + && ./gradlew -Dorg.gradle.daemon=false build -x test FROM eclipse-temurin:17-jre-alpine LABEL maintainer=nmaas@lists.geant.org +ARG USERNAME=nmaas +ARG USER_UID=1000 +ARG USER_GID=1000 +# Note: Latest version of kubectl may be found at https://github.com/kubernetes/kubernetes/releases +ARG KUBE_LATEST_VERSION="v1.16.3" +# Note: Latest version of helm may be found at https://github.com/kubernetes/helm/releases +ARG HELM_VERSION="v3.9.3" + COPY --from=builder /build/build/libs/*.jar /nmaas/platform/ -COPY docker/run_platform.sh /nmaas/scripts/run_platform.sh +COPY docker/docker_entrypoint.sh /nmaas/scripts/docker_entrypoint.sh COPY docker/logback.xml /nmaas/platform/config/logback.xml COPY docker/do-ntp.sh /etc/periodic/hourly/do-ntp.sh -COPY docker/ssh-config /root/.ssh/config + +RUN addgroup -g $USER_GID $USERNAME \ + && adduser --disabled-password -u $USER_UID -G $USERNAME $USERNAME RUN apk --no-cache add gettext postgresql-client \ && mkdir /nmaas/files \ - && chmod +x /nmaas/scripts/run_platform.sh + && chmod +x /nmaas/scripts/docker_entrypoint.sh \ + && wget -q https://storage.googleapis.com/kubernetes-release/release/${KUBE_LATEST_VERSION}/bin/linux/amd64/kubectl -O /usr/local/bin/kubectl \ + && chmod +x /usr/local/bin/kubectl \ + && wget -q https://get.helm.sh/helm-${HELM_VERSION}-linux-amd64.tar.gz -O - | tar -xzO linux-amd64/helm > /usr/local/bin/helm \ + && chmod +x /usr/local/bin/helm + +RUN chown -R $USERNAME:$USERNAME /nmaas/files \ + && chown -R $USERNAME:$USERNAME /nmaas/platform + +USER $USERNAME + +ENTRYPOINT /nmaas/scripts/docker_entrypoint.sh -CMD /nmaas/scripts/run_platform.sh && tail -f /dev/null \ No newline at end of file +CMD ls /home \ No newline at end of file diff --git a/build.gradle b/build.gradle index c81a2eec882c69ef394b88f033ac122b149daf6e..b48ed1e2ad106b2ea0a8a2e49945ff12bce4f12a 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ plugins { id 'java' id 'idea' id 'jacoco' - id 'org.springframework.boot' version '3.4.3' + id 'org.springframework.boot' version '3.4.4' id 'io.spring.dependency-management' version '1.1.7' id 'com.gorylenko.gradle-git-properties' version '2.4.2' id 'org.sonarqube' version '6.0.1.5171' @@ -13,7 +13,7 @@ repositories { mavenCentral() } -version = '1.7.0-SNAPSHOT' +version = '1.8.0-SNAPSHOT' group = 'net.geant.nmaas' java { @@ -30,11 +30,11 @@ gitProperties { protobuf { protoc { - artifact = 'com.google.protobuf:protoc:4.29.3' + artifact = 'com.google.protobuf:protoc:4.30.2' } plugins { grpc { - artifact = 'io.grpc:protoc-gen-grpc-java:1.69.1' + artifact = 'io.grpc:protoc-gen-grpc-java:1.71.0' } } generateProtoTasks { @@ -125,21 +125,17 @@ dependencies { implementation 'ch.qos.logback:logback-core:1.5.17' - // SSH library - implementation('com.hierynomus:sshj:0.32.0') - implementation('com.fasterxml.jackson.datatype:jackson-datatype-jsr310') - // Kubernetes API client implementation('io.fabric8:kubernetes-client:6.13.5') - implementation('com.google.protobuf:protobuf-java:4.29.3') - implementation('io.grpc:grpc-netty-shaded:1.69.1') - implementation('io.grpc:grpc-protobuf:1.69.1') - implementation('io.grpc:grpc-stub:1.69.1') + implementation('com.google.protobuf:protobuf-java:4.30.2') + implementation('io.grpc:grpc-netty-shaded:1.71.0') + implementation('io.grpc:grpc-protobuf:1.71.0') + implementation('io.grpc:grpc-stub:1.71.0') implementation('com.opencsv:opencsv:5.9') - testImplementation('org.mockito:mockito-inline:5.2.0') + testImplementation('org.mockito:mockito-core:5.16.1') testImplementation('org.springframework.boot:spring-boot-starter-test') integrationTestImplementation('org.springframework.boot:spring-boot-starter-test') diff --git a/docker/docker_entrypoint.sh b/docker/docker_entrypoint.sh new file mode 100644 index 0000000000000000000000000000000000000000..1a353247a6e67fcfa1a06a16c8ada6d3b29a6c55 --- /dev/null +++ b/docker/docker_entrypoint.sh @@ -0,0 +1,39 @@ +#!/bin/sh + +export HELM_HOME=/home/nmaas/.helm + +mkdir -p $HELM_HOME +helm repo add nmaas https://artifactory.software.geant.org/artifactory/nmaas-helm +helm repo add influxdata https://helm.influxdata.com/ +helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx +helm repo add bitnami https://charts.bitnami.com/bitnami +helm repo add jenkins https://charts.jenkins.io + +cat >> /home/nmaas/.profile <<EOF +export HELM_HOME=$HELM_HOME +export HELM_HOST=$HELM_HOST +export TILLER_NAMESPACE=$TILLER_NAMESPACE +export KUBERNETES_SERVICE_HOST=kubernetes.default +export KUBERNETES_SERVICE_PORT=443 +EOF + +DIR=/nmaas/platform +FILE=$(ls $DIR | grep .jar) +cd $DIR + +mkdir -p /nmaas/files/upload +mkdir -p /nmaas/files/log + +until PGPASSWORD=${POSTGRESQL_PASSWORD} psql -h "${POSTGRESQL_HOST}" -p ${POSTGRESQL_PORT} -U "${POSTGRESQL_USERNAME}" -d "postgres" -c '\l'; do + sleep 1s +done + +if PGPASSWORD=${POSTGRESQL_PASSWORD} psql -h "${POSTGRESQL_HOST}" -p ${POSTGRESQL_PORT} -U "${POSTGRESQL_USERNAME}" -lqt | cut -d \| -f 1 | grep -qw ${POSTGRESQL_DBNAME}; then + echo "Database is already exists" +else + echo "Database needs to be created. Creating ..." + PGPASSWORD=${POSTGRESQL_PASSWORD} createdb ${POSTGRESQL_DBNAME} -h "${POSTGRESQL_HOST}" -p ${POSTGRESQL_PORT} -U "${POSTGRESQL_USERNAME}" ${POSTGRESQL_DBNAME} +fi + +echo "Running nmaas-platform ..." +java -Djava.security.egd=file:/dev/./urandom -Dlogging.config=/nmaas/platform/config/logback.xml -jar $FILE \ No newline at end of file diff --git a/docker/run_platform.sh b/docker/run_platform.sh deleted file mode 100644 index e457b6ce003279e848da7479c512f23d68f3f84b..0000000000000000000000000000000000000000 --- a/docker/run_platform.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/sh -DIR=/nmaas/platform -FILE=$(ls $DIR | grep .jar) -cd $DIR - -mkdir -p /root/.ssh -cp /nmaas/.ssh/id_rsa /root/.ssh/id_rsa -chmod 600 /root/.ssh/id_rsa -mkdir -p /nmaas/files/upload -mkdir -p /nmaas/files/log - -until PGPASSWORD=${POSTGRESQL_PASSWORD} psql -h "${POSTGRESQL_HOST}" -p ${POSTGRESQL_PORT} -U "${POSTGRESQL_USERNAME}" -d "postgres" -c '\l'; do - sleep 1s -done - -if PGPASSWORD=${POSTGRESQL_PASSWORD} psql -h "${POSTGRESQL_HOST}" -p ${POSTGRESQL_PORT} -U "${POSTGRESQL_USERNAME}" -lqt | cut -d \| -f 1 | grep -qw ${POSTGRESQL_DBNAME}; then - echo "Database is already setup" -else - echo "Database is being created..." - PGPASSWORD=${POSTGRESQL_PASSWORD} createdb ${POSTGRESQL_DBNAME} -h "${POSTGRESQL_HOST}" -p ${POSTGRESQL_PORT} -U "${POSTGRESQL_USERNAME}" ${POSTGRESQL_DBNAME} -fi - -java -Djava.security.egd=file:/dev/./urandom -Dlogging.config=/nmaas/platform/config/logback.xml -jar $FILE diff --git a/docker/ssh-config b/docker/ssh-config deleted file mode 100644 index 08b1f063c8a657530d351a063d6db3b96b0a42b3..0000000000000000000000000000000000000000 --- a/docker/ssh-config +++ /dev/null @@ -1,4 +0,0 @@ -Host nmaas-helm -Hostname nmaas-helm -IdentityFile ~/.ssh/id_rsa -User helm diff --git a/src/integrationTest/resources/application.properties b/src/integrationTest/resources/application.properties index e9ab8d25240a047fb8a2ac06d09da72b0c5cd621..bede8a8c35454dfa31bfaa167de66206150febe1 100644 --- a/src/integrationTest/resources/application.properties +++ b/src/integrationTest/resources/application.properties @@ -86,8 +86,6 @@ spring.security.oauth2.client.provider.keycloak.user-info-uri=http://localhost:8 helm.update.async.enabled=false helm.update.async.cron=0 * * * * ? -helm.address=10.134.241.6 -helm.username=nmaas helm.useLocalCharts=true helm.repositoryName=nmaas-test helm.repositoryUrl=https://nmaas-test.helm.repository diff --git a/src/main/java/net/geant/nmaas/SecurityConfig.java b/src/main/java/net/geant/nmaas/SecurityConfig.java index db380467dff062bb31c1f2bee3bd00fa197b3e82..5e322bc723e587c10f7db8ba637102f960305441 100644 --- a/src/main/java/net/geant/nmaas/SecurityConfig.java +++ b/src/main/java/net/geant/nmaas/SecurityConfig.java @@ -53,6 +53,7 @@ public class SecurityConfig { private static final String AUTH_OIDC_LOGIN_PAGE = "/api/oauth2/authorization/my-oidc"; private static final String AUTH_OIDC_LOGIN = "/api/auth/oidc/login"; private static final String AUTH_OIDC_SUCCESS = "/api/oidc/success"; + private static final String AUTH_OIDC_LINK = "/api/oidc/link"; private static final String AUTH_LOGOUT = "/api/oidc/logout/*"; private static final String AUTH_OIDC = "/api/oidc/**"; private static final String AUTH_CODE = "/api/login/oauth2/code"; @@ -146,6 +147,7 @@ public class SecurityConfig { .csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests(httpRequest -> httpRequest .requestMatchers(AUTH_WHITELIST).permitAll() + .requestMatchers(AUTH_OIDC_LINK).permitAll() .requestMatchers(AUTH_AUTHENTICATED_LIST).authenticated() ) .oauth2Login(oAuth2 -> oAuth2 diff --git a/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/HelmRepoUpdateScheduleConfig.java b/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/HelmRepoUpdateScheduleConfig.java index 672a58a08449f95c49c4600ff2f0a5d8b93f0634..cc6155441f991162c4a3105c17737fe800443bc9 100644 --- a/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/HelmRepoUpdateScheduleConfig.java +++ b/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/HelmRepoUpdateScheduleConfig.java @@ -1,11 +1,10 @@ package net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes; import com.google.common.base.Strings; -import lombok.extern.log4j.Log4j2; import lombok.extern.slf4j.Slf4j; import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.components.helm.HelmKServiceManager; import net.geant.nmaas.scheduling.ScheduleManager; -import net.geant.nmaas.utils.ssh.CommandExecutionException; +import net.geant.nmaas.utils.bash.CommandExecutionException; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; diff --git a/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/components/helm/HelmCommand.java b/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/components/helm/HelmCommand.java index c6d01946c4126b5ebf4b51caf9fd6cea8014846d..f4ba07022f7d0a78e95fe8f4dcf31ab0dec71644 100644 --- a/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/components/helm/HelmCommand.java +++ b/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/components/helm/HelmCommand.java @@ -1,6 +1,6 @@ package net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.components.helm; -import net.geant.nmaas.utils.ssh.Command; +import net.geant.nmaas.utils.bash.Command; public abstract class HelmCommand implements Command { @@ -24,7 +24,7 @@ public abstract class HelmCommand implements Command { } protected static void addTlsOptionIfRequired(String helmVersion, boolean enableTls, StringBuilder sb) { - if(HELM_VERSION_2.equals(helmVersion) && enableTls){ + if (HELM_VERSION_2.equals(helmVersion) && enableTls) { sb.append(SPACE).append(TLS); } } diff --git a/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/components/helm/HelmCommandExecutor.java b/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/components/helm/HelmCommandExecutor.java index b92061470b62a644dbcb0e6259e1cff49479880f..560c569c75876c8be909cd23a757e954e6af83d4 100644 --- a/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/components/helm/HelmCommandExecutor.java +++ b/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/components/helm/HelmCommandExecutor.java @@ -10,9 +10,9 @@ import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.co import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.components.helm.commands.HelmUpgradeCommand; import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.components.helm.commands.HelmVersionCommand; import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.entities.KubernetesTemplate; -import net.geant.nmaas.utils.ssh.CommandExecutionException; -import net.geant.nmaas.utils.ssh.SingleCommandExecutor; -import net.geant.nmaas.utils.ssh.SshConnectionException; +import net.geant.nmaas.utils.bash.CommandExecutionException; +import net.geant.nmaas.utils.bash.CommandExecutor; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @@ -25,15 +25,12 @@ import static net.geant.nmaas.nmservice.deployment.containerorchestrators.kubern @Component public class HelmCommandExecutor { + @Autowired + CommandExecutor commandExecutor; + @Value("${helm.version:v3}") String helmVersion; - @Value("${helm.address}") - String helmAddress; - - @Value("${helm.username}") - String helmUsername; - @Value("${helm.useLocalCharts}") Boolean useLocalCharts; @@ -73,9 +70,8 @@ public class HelmCommandExecutor { enableTls ); } - singleCommandExecutor().executeSingleCommand(command); - } catch (SshConnectionException - | CommandExecutionException e) { + commandExecutor.execute(command); + } catch (CommandExecutionException e) { throw new CommandExecutionException("Failed to execute helm install command -> " + e.getMessage()); } } @@ -105,9 +101,8 @@ public class HelmCommandExecutor { } else { throw new CommandExecutionException("Unknown Helm version in use: " + helmVersion); } - singleCommandExecutor().executeSingleCommand(command); - } catch (SshConnectionException - | CommandExecutionException e) { + commandExecutor.execute(command); + } catch (CommandExecutionException e) { throw new CommandExecutionException("Failed to execute helm delete command -> " + e.getMessage()); } } @@ -124,16 +119,15 @@ public class HelmCommandExecutor { releaseName, enableTls ); - String output = singleCommandExecutor().executeSingleCommandAndReturnOutput(command); + String output = commandExecutor.executeWithOutput(command); return parseStatus(output); - } catch (SshConnectionException - | CommandExecutionException e) { + } catch (CommandExecutionException e) { throw new CommandExecutionException("Failed to execute helm status command -> " + e.getMessage()); } } HelmPackageStatus parseStatus(String output) { - if(HELM_VERSION_2.equals(helmVersion) && output.contains("STATUS: DEPLOYED")) { + if (HELM_VERSION_2.equals(helmVersion) && output.contains("STATUS: DEPLOYED")) { return HelmPackageStatus.DEPLOYED; } else if (HelmCommand.HELM_VERSION_3.equals(helmVersion) && output.contains("STATUS: deployed")) { return HelmPackageStatus.DEPLOYED; @@ -149,10 +143,9 @@ public class HelmCommandExecutor { namespace, enableTls ); - String output = singleCommandExecutor().executeSingleCommandAndReturnOutput(command); + String output = commandExecutor.executeWithOutput(command); return Arrays.asList(output.split("\n")); - } catch (SshConnectionException - | CommandExecutionException e) { + } catch (CommandExecutionException e) { throw new CommandExecutionException("Failed to execute helm list command -> " + e.getMessage()); } } @@ -178,38 +171,32 @@ public class HelmCommandExecutor { enableTls ); } - singleCommandExecutor().executeSingleCommand(command); - } catch (SshConnectionException e) { + commandExecutor.execute(command); + } catch (CommandExecutionException e) { throw new CommandExecutionException("Failed to execute helm upgrade command -> " + e.getMessage()); } } void executeVersionCommand() { - try{ - singleCommandExecutor().executeSingleCommand( - HelmVersionCommand.command(helmVersion, enableTls) - ); - } catch (SshConnectionException e) { + try { + commandExecutor.execute(HelmVersionCommand.command(helmVersion, enableTls)); + } catch (CommandExecutionException e) { throw new CommandExecutionException("Failed to execute helm version command -> " + e.getMessage()); } } - private SingleCommandExecutor singleCommandExecutor() { - return SingleCommandExecutor.getExecutor(helmAddress, helmUsername); - } - void executeHelmRepoUpdateCommand() { - try{ - singleCommandExecutor().executeSingleCommand(HelmRepoUpdateCommand.command()); - } catch (SshConnectionException e) { + try { + commandExecutor.execute(HelmRepoUpdateCommand.command()); + } catch (CommandExecutionException e) { throw new CommandExecutionException("Failed to execute helm repository update command -> " + e.getMessage()); } } void executeHelmRepoAddCommand(String repoName, String repoUrl) { - try{ - singleCommandExecutor().executeSingleCommand(HelmRepoAddCommand.command(repoName, repoUrl)); - } catch (SshConnectionException e) { + try { + commandExecutor.execute(HelmRepoAddCommand.command(repoName, repoUrl)); + } catch (CommandExecutionException e) { throw new CommandExecutionException("Failed to execute helm repository add command -> " + e.getMessage()); } } diff --git a/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/components/helm/HelmKServiceManager.java b/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/components/helm/HelmKServiceManager.java index c43420fc9ed3262047e2200c0d1c7c1c99cde405..4bd99914caf9562e59eec2221dc7c4e65970cec9 100644 --- a/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/components/helm/HelmKServiceManager.java +++ b/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/components/helm/HelmKServiceManager.java @@ -18,7 +18,7 @@ import net.geant.nmaas.orchestration.Identifier; import net.geant.nmaas.orchestration.repositories.DomainTechDetailsRepository; import net.geant.nmaas.utils.logging.LogLevel; import net.geant.nmaas.utils.logging.Loggable; -import net.geant.nmaas.utils.ssh.CommandExecutionException; +import net.geant.nmaas.utils.bash.CommandExecutionException; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; diff --git a/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/components/helm/HelmMonitorService.java b/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/components/helm/HelmMonitorService.java index de8dc79db52a10f9bcad152e0e76a27d66b8261d..dd4cfa536812865bd10c2ab41f73f21a449eaee0 100644 --- a/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/components/helm/HelmMonitorService.java +++ b/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/components/helm/HelmMonitorService.java @@ -5,7 +5,7 @@ import lombok.extern.slf4j.Slf4j; import net.geant.nmaas.monitor.MonitorService; import net.geant.nmaas.monitor.MonitorStatus; import net.geant.nmaas.monitor.ServiceType; -import net.geant.nmaas.utils.ssh.CommandExecutionException; +import net.geant.nmaas.utils.bash.CommandExecutionException; import org.springframework.stereotype.Service; @Service diff --git a/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/components/helm/commands/HelmInstallCommand.java b/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/components/helm/commands/HelmInstallCommand.java index 6fbcbc35b831de480a978b0c1cdbfbb521a98d6a..5f8e03742b632e0585605f79650803627f74e738 100644 --- a/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/components/helm/commands/HelmInstallCommand.java +++ b/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/components/helm/commands/HelmInstallCommand.java @@ -16,13 +16,13 @@ public class HelmInstallCommand extends HelmCommand { /** * Creates {@link HelmInstallCommand} with provided custom input * - * @param helmVersion version of Helm in use - * @param namespace namespace to install the release into - * @param releaseName release name - * @param values a map of key - value pairs to customize the release installation - * @param chartName chart name for download from repository + * @param helmVersion version of Helm in use + * @param namespace namespace to install the release into + * @param releaseName release name + * @param values a map of key - value pairs to customize the release installation + * @param chartName chart name for download from repository * @param chartVersion chart version from download from repository - * @param enableTls flag indicating if tls option should be added + * @param enableTls flag indicating if tls option should be added * @return complete command object */ public static HelmInstallCommand commandWithRepo(String helmVersion, String namespace, String releaseName, Map<String, String> values, String chartName, String chartVersion, boolean enableTls) { @@ -41,10 +41,10 @@ public class HelmInstallCommand extends HelmCommand { /** * Creates {@link HelmInstallCommand} with provided custom input and local chart archive * - * @param helmVersion version of Helm in use - * @param namespace namespace to install the release into - * @param releaseName release name - * @param values a map of key - value pairs to customize the release installation + * @param helmVersion version of Helm in use + * @param namespace namespace to install the release into + * @param releaseName release name + * @param values a map of key - value pairs to customize the release installation * @param chartArchive complete path to the release chart archive * @return complete command object */ @@ -75,7 +75,7 @@ public class HelmInstallCommand extends HelmCommand { } public static String commaSeparatedValuesString(Map<String, String> values) { - values.entrySet().forEach(e -> log.info(e.getKey() + " " + e.getValue())); + values.forEach((key, value) -> log.info("{} {}", key, value)); return values.entrySet().stream() .map(entry -> { if (entry.getKey().contains(TEXT_TO_REPLACE_WITH_VALUE)) { diff --git a/src/main/java/net/geant/nmaas/portal/api/auth/OIDCAuthController.java b/src/main/java/net/geant/nmaas/portal/api/auth/OIDCAuthController.java index 1b006bc268eb592ba27891b52a88fed89a6dcb4f..16512e8291e034e9b39aeef5aed26967c123bef8 100644 --- a/src/main/java/net/geant/nmaas/portal/api/auth/OIDCAuthController.java +++ b/src/main/java/net/geant/nmaas/portal/api/auth/OIDCAuthController.java @@ -1,25 +1,38 @@ package net.geant.nmaas.portal.api.auth; +import com.google.common.collect.ImmutableSet; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; +import net.geant.nmaas.portal.api.exception.AuthenticationException; import net.geant.nmaas.portal.api.exception.ExternalUserCanNotBeLinked; import net.geant.nmaas.portal.api.exception.ExternalUserMatchException; +import net.geant.nmaas.portal.api.exception.SignupException; import net.geant.nmaas.portal.api.security.JWTTokenService; +import net.geant.nmaas.portal.exceptions.UndergoingMaintenanceException; +import net.geant.nmaas.portal.persistent.entity.Role; import net.geant.nmaas.portal.persistent.entity.User; +import net.geant.nmaas.portal.persistent.entity.UserRole; +import net.geant.nmaas.portal.service.ConfigurationManager; import net.geant.nmaas.portal.service.DomainService; import net.geant.nmaas.portal.service.OidcUserService; import net.geant.nmaas.portal.service.UserLoginRegisterService; +import net.geant.nmaas.portal.service.UserService; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpHeaders; import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.oauth2.core.oidc.user.OidcUser; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.view.RedirectView; +import static java.lang.String.format; + @RestController @RequiredArgsConstructor @@ -33,18 +46,83 @@ public class OIDCAuthController { private final UserLoginRegisterService loginRegisterService; + private final UserService userService; + + private final PasswordEncoder passwordEncoder; private final DomainService domains; + private final ConfigurationManager configurationManager; + + @Value("${portal.address}") private String portalAddress; @Value("${spring.security.oauth2.client.provider.my-oidc.issuer-uri:http://localhost:8080/realms/geant}") private String oidcAddress; + @PostMapping("api/oidc/link") + public UserOidcToken oidcLinkedSuccess(@RequestBody final OidcLogin oidcLogin, HttpServletRequest request) { + + User user = userService.findByEmail(oidcLogin.email()); + try { + validate( + oidcLogin.email(), + oidcLogin.password(), + user.getPassword(), + user.isEnabled()); + } catch (AuthenticationException ae) { + this.loginRegisterService.registerNewFailedLogin( + user, + request.getHeader(HttpHeaders.HOST), + request.getHeader(HttpHeaders.USER_AGENT), + BasicAuthController.getClientIpAddr(request) + ); + throw new AuthenticationException(ae.getMessage()); + } + checkUserApprovals(user); + if ( + configurationManager.getConfiguration().isMaintenance() + && user.getRoles().stream().noneMatch( + value -> value.getRole().equals(Role.ROLE_SYSTEM_ADMIN) + ) + ) { + throw new UndergoingMaintenanceException("Application is undergoing maintenance right now"); + } + this.loginRegisterService.registerNewSuccessfulLogin( + user, + request.getHeader(HttpHeaders.HOST), + request.getHeader(HttpHeaders.USER_AGENT), + BasicAuthController.getClientIpAddr(request) + ); + + User linkedUser = oidcUserService.linkUser( + oidcLogin.email(), + oidcLogin.uuid(), + oidcLogin.firstName(), + oidcLogin.lastName() + ); + + return new UserOidcToken( + jwtTokenService.getToken(linkedUser), + jwtTokenService.getRefreshToken(linkedUser), + oidcLogin.oidcToken() + ); + + + } + @GetMapping("/api/oidc/success") public RedirectView oidcLoginSuccess(@AuthenticationPrincipal OidcUser oidcUser, HttpServletRequest request) { + if (oidcUserService.externalUserRequiredLinking(oidcUser)) { + String linkingRedirectUrl = portalAddress + + "/login-linking?oidc_token=" + + oidcUser.getIdToken().getTokenValue(); + return new RedirectView(linkingRedirectUrl); + } + + try { User user = oidcUserService.checkUser(oidcUser); String redirectUrl = portalAddress @@ -72,7 +150,6 @@ public class OIDCAuthController { } } - @GetMapping("/api/oidc/logout/{oidcToken}") public RedirectView logout(@PathVariable String oidcToken) { @@ -80,6 +157,26 @@ public class OIDCAuthController { return new RedirectView(logoutUrl + "?id_token_hint=" + oidcToken); } -} + void validate(String email, String providedPassword, String actualPassword, boolean isEnabled) { + validateConditionAndLogMessage(email == null || providedPassword == null, + format("Login failed: missing credentials%s", email != null ? (format(" (email: %s)", email)) : "")); + validateConditionAndLogMessage(!isEnabled, format("Login failed: user [%s] is not active", email)); + validateConditionAndLogMessage(!passwordEncoder.matches(providedPassword, actualPassword), format("Login failed: user [%s] entered incorrect password", email)); + } + + void checkUserApprovals(User user) { + if (!user.isTermsOfUseAccepted() || !user.isPrivacyPolicyAccepted()) { + log.info(format("Check during login: Terms of Use or Privacy Policy were not accepted by user [%s]", user.getUsername())); + user.setNewRoles(ImmutableSet.of(new UserRole(user, domains.getGlobalDomain().orElseThrow(SignupException::new), Role.ROLE_NOT_ACCEPTED))); + } + } + + private void validateConditionAndLogMessage(boolean loginCondition, String errorMessage) { + if (loginCondition) { + log.info(errorMessage); + throw new AuthenticationException("Invalid Credentials"); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/geant/nmaas/portal/api/auth/OidcLogin.java b/src/main/java/net/geant/nmaas/portal/api/auth/OidcLogin.java new file mode 100644 index 0000000000000000000000000000000000000000..a145c3d84dba8aabd504d3e5fa7ca45c9d1c81b6 --- /dev/null +++ b/src/main/java/net/geant/nmaas/portal/api/auth/OidcLogin.java @@ -0,0 +1,9 @@ +package net.geant.nmaas.portal.api.auth; + +public record OidcLogin(String email, + String password, + String oidcToken, + String uuid, + String firstName, + String lastName) { +} diff --git a/src/main/java/net/geant/nmaas/portal/api/auth/UserOidcToken.java b/src/main/java/net/geant/nmaas/portal/api/auth/UserOidcToken.java new file mode 100644 index 0000000000000000000000000000000000000000..0d1871d15b4943f4095cd18e24082fcb512bd04e --- /dev/null +++ b/src/main/java/net/geant/nmaas/portal/api/auth/UserOidcToken.java @@ -0,0 +1,5 @@ +package net.geant.nmaas.portal.api.auth; + + +public record UserOidcToken(String token, String refreshToken, String oidcToken) { +} diff --git a/src/main/java/net/geant/nmaas/portal/persistent/repositories/UserApiTokenRepository.java b/src/main/java/net/geant/nmaas/portal/persistent/repositories/UserApiTokenRepository.java index 85239e0fcae1a54deb09101a00975ab09c256b23..950187b08c9497b88694c74e6b138f907d700a83 100644 --- a/src/main/java/net/geant/nmaas/portal/persistent/repositories/UserApiTokenRepository.java +++ b/src/main/java/net/geant/nmaas/portal/persistent/repositories/UserApiTokenRepository.java @@ -12,4 +12,6 @@ public interface UserApiTokenRepository extends JpaRepository<UserApiToken, Long List<UserApiToken> findAllByUserId(Long userId); List<UserApiToken> findAllByUserIdAndName(Long userId, String name); + + List<UserApiToken> findAllByValid(boolean valid); } diff --git a/src/main/java/net/geant/nmaas/portal/service/OidcUserService.java b/src/main/java/net/geant/nmaas/portal/service/OidcUserService.java index aad3204b87807cf859ba53e64620be33948adae0..7f94bf9f9a35d78a6769c0369ed4ec0638760887 100644 --- a/src/main/java/net/geant/nmaas/portal/service/OidcUserService.java +++ b/src/main/java/net/geant/nmaas/portal/service/OidcUserService.java @@ -9,5 +9,7 @@ public interface OidcUserService { User checkUser(OidcUser oidcUser); User register(OidcUser user, Domain globalDomain); User registerNewUser(OidcUser oidcUser); + boolean externalUserRequiredLinking(OidcUser oidcUser); + User linkUser(String email, String samlToken, String firstName, String lastName); } diff --git a/src/main/java/net/geant/nmaas/portal/service/TokenAuthenticationService.java b/src/main/java/net/geant/nmaas/portal/service/TokenAuthenticationService.java index a930f0a387fb40e9aae6af3fefb21c4b48293613..1e56d243b8901f8ed384bc26382df760fdf232ea 100644 --- a/src/main/java/net/geant/nmaas/portal/service/TokenAuthenticationService.java +++ b/src/main/java/net/geant/nmaas/portal/service/TokenAuthenticationService.java @@ -4,6 +4,9 @@ import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import net.geant.nmaas.portal.api.security.JWTTokenService; import net.geant.nmaas.portal.api.security.exceptions.AuthenticationMethodNotSupportedException; +import net.geant.nmaas.portal.persistent.entity.User; +import net.geant.nmaas.portal.persistent.repositories.UserApiTokenRepository; +import net.geant.nmaas.portal.service.impl.security.SecretPasswordService; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; @@ -14,6 +17,8 @@ import org.springframework.stereotype.Service; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.regex.Pattern; +import java.util.stream.Collectors; @Service @Slf4j @@ -21,12 +26,18 @@ public class TokenAuthenticationService { private static final String AUTH_HEADER = "Authorization"; private static final String AUTH_METHOD = "Bearer"; + private static final Pattern UUID_PATTERN = Pattern.compile("^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$", Pattern.CASE_INSENSITIVE); + private final JWTTokenService jwtTokenService; + private final UserApiTokenRepository userApiTokenRepository; + private final SecretPasswordService secretPasswordService; @Autowired - public TokenAuthenticationService(JWTTokenService jwtTokenService) { + public TokenAuthenticationService(JWTTokenService jwtTokenService, UserApiTokenRepository userApiTokenRepository, SecretPasswordService secretPasswordService) { this.jwtTokenService = jwtTokenService; + this.userApiTokenRepository = userApiTokenRepository; + this.secretPasswordService = secretPasswordService; } public Authentication getAuthentication(HttpServletRequest httpRequest) { @@ -35,26 +46,41 @@ public class TokenAuthenticationService { throw new AuthenticationMethodNotSupportedException(AUTH_HEADER + " contains unsupported method."); } String token = authHeader.substring(AUTH_METHOD.length() + 1); + if (isJWTToken(token)) { - log.trace("Jwt token auth service: {} {} ", jwtTokenService.getClaims(token).getSubject(), jwtTokenService.getClaims(token).get("roles")); - - String username = jwtTokenService.getClaims(token).getSubject(); - Object roles = jwtTokenService.getClaims(token).get("roles"); - Object globalRole = jwtTokenService.getClaims(token).get("global_role"); + log.trace("Jwt token auth service: {} {} ", jwtTokenService.getClaims(token).getSubject(), jwtTokenService.getClaims(token).get("roles")); - Set<SimpleGrantedAuthority> authorities = new HashSet<>(); - if (globalRole instanceof List<?>) { - for (Object role : (List<?>) globalRole) { - authorities.add(new SimpleGrantedAuthority(role.toString())); + String username = jwtTokenService.getClaims(token).getSubject(); + Object roles = jwtTokenService.getClaims(token).get("roles"); + Object globalRole = jwtTokenService.getClaims(token).get("global_role"); + Set<SimpleGrantedAuthority> authorities = new HashSet<>(); + if (globalRole instanceof List<?>) { + for (Object role : (List<?>) globalRole) { + authorities.add(new SimpleGrantedAuthority(role.toString())); + } } - } - if (roles instanceof List<?>) { - for (Object role : (List<?>) roles) { - authorities.add(new SimpleGrantedAuthority(role.toString())); + if (roles instanceof List<?>) { + for (Object role : (List<?>) roles) { + authorities.add(new SimpleGrantedAuthority(role.toString())); + } } + return new UsernamePasswordAuthenticationToken(username, null, authorities); + } else if (isUUIDToken(token)) { + User user = secretPasswordService.findUserBasedOnToken(token, userApiTokenRepository.findAllByValid(true)); + Set<SimpleGrantedAuthority> authorities = user.getRoles().stream().filter(role -> role.getDomain().isActive()).map(role -> new SimpleGrantedAuthority(role.getRole().authority())).collect(Collectors.toSet()); + return new UsernamePasswordAuthenticationToken(user.getUsername(), null, authorities); + } else { + throw new AuthenticationMethodNotSupportedException("Not supported token type"); } + } + + private boolean isUUIDToken(String token) { + return UUID_PATTERN.matcher(token).matches(); + } - return new UsernamePasswordAuthenticationToken(username, null, authorities); + private boolean isJWTToken(String token) { + // JWT has three parts separated by dots + return token.split("\\.").length == 3; } } diff --git a/src/main/java/net/geant/nmaas/portal/service/impl/OidcUserServiceImpl.java b/src/main/java/net/geant/nmaas/portal/service/impl/OidcUserServiceImpl.java index 733624d31972fadcfc187c39693101a1860bf451..a9eebe460474560b07b08bf8aa7eef72b60f5250 100644 --- a/src/main/java/net/geant/nmaas/portal/service/impl/OidcUserServiceImpl.java +++ b/src/main/java/net/geant/nmaas/portal/service/impl/OidcUserServiceImpl.java @@ -71,16 +71,7 @@ public class OidcUserServiceImpl implements OidcUserService { + oidcUserPreferredUsername + " does not match internal user "); } - } else if (existUserByEmail) { - - if (allowedLinkingUsersByEmail) { - return linkUser(oidcUserEmail, oidcUserSub); - } else { - throw new ExternalUserCanNotBeLinked("External user " - + oidcUserPreferredUsername - + " cannot be linked to an internal user "); - } - } else { + } else { return registerNewUser(oidcUser); } } @@ -122,10 +113,32 @@ public class OidcUserServiceImpl implements OidcUserService { return newUser; } - private User linkUser(String email, String samlToken) { + @Override + public boolean externalUserRequiredLinking(OidcUser oidcUser) { + + String oidcUserSub = oidcUser.getAttribute("sub"); + String oidcUserEmail = oidcUser.getAttribute("email"); + String oidcUserPreferredUsername = oidcUser.getAttribute("preferred_username"); + + boolean existUserBySamlToken = userService + .existsBySamlToken(oidcUserSub); + boolean existUserByUsernameAsSamlToken = userService + .existsBySamlToken(oidcUserPreferredUsername); + boolean existUserByEmail = userService + .existsByEmail(oidcUserEmail); + + if(existUserBySamlToken || existUserByUsernameAsSamlToken) { + return false; + }else return existUserByEmail; + } + + @Override + public User linkUser(String email, String samlToken, String firstName, String lastName) { User user = userService.findByEmail(email); user.setSamlToken(samlToken); + user.setFirstname(firstName); + user.setLastname(lastName); userService.update(user); return user; diff --git a/src/main/java/net/geant/nmaas/portal/service/impl/security/SecretPasswordService.java b/src/main/java/net/geant/nmaas/portal/service/impl/security/SecretPasswordService.java index 6a1269e9653d03b8a4f65f357aa11e36ce37e203..a119267857bb6b9d8a336d11f74c6630811f9813 100644 --- a/src/main/java/net/geant/nmaas/portal/service/impl/security/SecretPasswordService.java +++ b/src/main/java/net/geant/nmaas/portal/service/impl/security/SecretPasswordService.java @@ -1,9 +1,14 @@ package net.geant.nmaas.portal.service.impl.security; import lombok.RequiredArgsConstructor; +import net.geant.nmaas.portal.api.security.exceptions.TokenAuthenticationException; +import net.geant.nmaas.portal.persistent.entity.User; +import net.geant.nmaas.portal.persistent.entity.UserApiToken; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; +import java.util.List; + @Service @RequiredArgsConstructor public class SecretPasswordService { @@ -18,4 +23,18 @@ public class SecretPasswordService { return passwordEncoder.matches(rawSecret, hashedSecret); } + /** + * Find user behind uuid token + * @param rawSecret + * @param tokens + * @return + */ + public User findUserBasedOnToken(String rawSecret, List<UserApiToken> tokens) { + for (UserApiToken token : tokens){ + if (verifySecret(rawSecret, token.getTokenValue())) + return token.getUser(); + } + throw new TokenAuthenticationException("This uuid token does not exist"); + } + } diff --git a/src/main/java/net/geant/nmaas/utils/ssh/Command.java b/src/main/java/net/geant/nmaas/utils/bash/Command.java similarity index 78% rename from src/main/java/net/geant/nmaas/utils/ssh/Command.java rename to src/main/java/net/geant/nmaas/utils/bash/Command.java index d9e254fd707cdc2b041d72df8fa50e944ad2979d..d4a46a3216e4c767dc51f4f66f8f263e2355bfad 100644 --- a/src/main/java/net/geant/nmaas/utils/ssh/Command.java +++ b/src/main/java/net/geant/nmaas/utils/bash/Command.java @@ -1,4 +1,4 @@ -package net.geant.nmaas.utils.ssh; +package net.geant.nmaas.utils.bash; import java.util.function.Predicate; diff --git a/src/main/java/net/geant/nmaas/utils/ssh/CommandExecutionException.java b/src/main/java/net/geant/nmaas/utils/bash/CommandExecutionException.java similarity index 88% rename from src/main/java/net/geant/nmaas/utils/ssh/CommandExecutionException.java rename to src/main/java/net/geant/nmaas/utils/bash/CommandExecutionException.java index 8a4de4da1abbc191e22b556b06c36261264d1882..bb4f450b75ef3632af9430491e0685692f9d7023 100644 --- a/src/main/java/net/geant/nmaas/utils/ssh/CommandExecutionException.java +++ b/src/main/java/net/geant/nmaas/utils/bash/CommandExecutionException.java @@ -1,4 +1,4 @@ -package net.geant.nmaas.utils.ssh; +package net.geant.nmaas.utils.bash; public class CommandExecutionException extends RuntimeException { diff --git a/src/main/java/net/geant/nmaas/utils/bash/CommandExecutor.java b/src/main/java/net/geant/nmaas/utils/bash/CommandExecutor.java new file mode 100644 index 0000000000000000000000000000000000000000..263974ccf57ab02a7da97843ca92745cfea13bc9 --- /dev/null +++ b/src/main/java/net/geant/nmaas/utils/bash/CommandExecutor.java @@ -0,0 +1,9 @@ +package net.geant.nmaas.utils.bash; + +public interface CommandExecutor { + + void execute(Command command) throws CommandExecutionException; + + String executeWithOutput(Command command) throws CommandExecutionException; + +} \ No newline at end of file diff --git a/src/main/java/net/geant/nmaas/utils/bash/DefaultCommandExecutor.java b/src/main/java/net/geant/nmaas/utils/bash/DefaultCommandExecutor.java new file mode 100644 index 0000000000000000000000000000000000000000..9deb800972745c4cbc3c2190ad276e774d462432 --- /dev/null +++ b/src/main/java/net/geant/nmaas/utils/bash/DefaultCommandExecutor.java @@ -0,0 +1,34 @@ +package net.geant.nmaas.utils.bash; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +@Slf4j +@Component +public class DefaultCommandExecutor implements CommandExecutor { + + @Override + public void execute(Command command) throws CommandExecutionException { + try { + new ProcessBuilder(command.asString()).start(); + } catch (IOException e) { + throw new CommandExecutionException(e.getMessage()); + } + } + + @Override + public String executeWithOutput(Command command) throws CommandExecutionException { + try { + Process process = new ProcessBuilder(command.asString()).start(); + final String output = new String(process.getInputStream().readAllBytes(), StandardCharsets.UTF_8); + log.debug("Output: {}", output); + return output; + } catch (IOException e) { + throw new CommandExecutionException(e.getMessage()); + } + } + +} diff --git a/src/main/java/net/geant/nmaas/utils/ssh/BasicCredentials.java b/src/main/java/net/geant/nmaas/utils/ssh/BasicCredentials.java deleted file mode 100644 index 956471c18c0750c24d35277efc60f1133a292606..0000000000000000000000000000000000000000 --- a/src/main/java/net/geant/nmaas/utils/ssh/BasicCredentials.java +++ /dev/null @@ -1,19 +0,0 @@ -package net.geant.nmaas.utils.ssh; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -@AllArgsConstructor -@Getter -public class BasicCredentials { - - private final String username; - - private final String password; - - public BasicCredentials(String username) { - this.username = username; - this.password = null; - } - -} diff --git a/src/main/java/net/geant/nmaas/utils/ssh/DummyHostKeyVerifier.java b/src/main/java/net/geant/nmaas/utils/ssh/DummyHostKeyVerifier.java deleted file mode 100644 index 8ef7708be72c9a9c1df484f0fcc0736f38584827..0000000000000000000000000000000000000000 --- a/src/main/java/net/geant/nmaas/utils/ssh/DummyHostKeyVerifier.java +++ /dev/null @@ -1,21 +0,0 @@ -package net.geant.nmaas.utils.ssh; - -import net.schmizz.sshj.transport.verification.HostKeyVerifier; - -import java.security.PublicKey; -import java.util.Collections; -import java.util.List; - -public class DummyHostKeyVerifier implements HostKeyVerifier { - - @Override - public boolean verify(String hostname, int port, PublicKey key) { - return true; - } - - @Override - public List<String> findExistingAlgorithms(String hostname, int port) { - return Collections.emptyList(); - } - -} diff --git a/src/main/java/net/geant/nmaas/utils/ssh/SingleCommandExecutor.java b/src/main/java/net/geant/nmaas/utils/ssh/SingleCommandExecutor.java deleted file mode 100644 index 9957a37b5af020af42840fc0259df3d6c3351e84..0000000000000000000000000000000000000000 --- a/src/main/java/net/geant/nmaas/utils/ssh/SingleCommandExecutor.java +++ /dev/null @@ -1,92 +0,0 @@ -package net.geant.nmaas.utils.ssh; - -import lombok.extern.log4j.Log4j2; -import lombok.extern.slf4j.Slf4j; - -import java.util.function.Predicate; - -@Slf4j -public class SingleCommandExecutor { - - private final String hostname; - - private final int port; - - private final BasicCredentials credentials; - - private SshConnector connector = null; - - private static SingleCommandExecutor defaultExecutor; - - public static SingleCommandExecutor getExecutor(String hostname, String username) { - if (defaultExecutor != null) { - return defaultExecutor; - } - return new SingleCommandExecutor(hostname, 22, new BasicCredentials(username, null)); - } - - public static SingleCommandExecutor getExecutor(String hostname, BasicCredentials credentials) { - if (defaultExecutor != null) { - return defaultExecutor; - } - return new SingleCommandExecutor(hostname, 22, credentials); - } - - public static SingleCommandExecutor getExecutor(String hostname, int port, BasicCredentials credentials) { - if (defaultExecutor != null) { - return defaultExecutor; - } - return new SingleCommandExecutor(hostname, port, credentials); - } - - public static void setDefaultExecutor(SingleCommandExecutor executor) { - defaultExecutor = executor; - } - - private SingleCommandExecutor(String hostname, int port, BasicCredentials credentials) { - this.hostname = hostname; - this.port = port; - this.credentials = credentials; - } - - public void executeSingleCommand(Command command) { - log.trace("command : {}", command.asString()); - executeCommand(command); - } - - public String executeSingleCommandAndReturnOutput(Command command) { - return executeCommand(command); - } - - private String executeCommand(Command command) { - connect(); - String output = execute(command); - validateOutput(output, command.isOutputCorrect()); - disconnect(); - return output; - } - - private void connect() { - log.trace("Connecting to {}", hostname); - connector = new SshConnector(hostname, port, credentials); - } - - private String execute(Command command) { - log.trace("Executing command: {}", command.asString()); - return connector.executeSingleCommand(command.asString()); - } - - void validateOutput(String output, Predicate<String> isOutputCorrect) { - if (isOutputCorrect.negate().test(output)) { - throw new CommandExecutionException("Identified problem with command execution based on output -> details: " + output + ")"); - } - } - - private void disconnect() { - if (connector != null) { - connector.close(); - connector = null; - } - } - -} diff --git a/src/main/java/net/geant/nmaas/utils/ssh/SshConnectionException.java b/src/main/java/net/geant/nmaas/utils/ssh/SshConnectionException.java deleted file mode 100644 index 5b609156a75788ae3081cb8ac3efb38d3467224b..0000000000000000000000000000000000000000 --- a/src/main/java/net/geant/nmaas/utils/ssh/SshConnectionException.java +++ /dev/null @@ -1,13 +0,0 @@ -package net.geant.nmaas.utils.ssh; - -public class SshConnectionException extends RuntimeException { - - public SshConnectionException(String message) { - super(message); - } - - public SshConnectionException(String message, Throwable cause) { - super(message, cause); - } - -} diff --git a/src/main/java/net/geant/nmaas/utils/ssh/SshConnector.java b/src/main/java/net/geant/nmaas/utils/ssh/SshConnector.java deleted file mode 100644 index 2ec94d6131c906e476f6ec0a7c51d216c43740a9..0000000000000000000000000000000000000000 --- a/src/main/java/net/geant/nmaas/utils/ssh/SshConnector.java +++ /dev/null @@ -1,89 +0,0 @@ -package net.geant.nmaas.utils.ssh; - -import lombok.NoArgsConstructor; -import lombok.extern.log4j.Log4j2; -import lombok.extern.slf4j.Slf4j; -import net.schmizz.sshj.SSHClient; -import net.schmizz.sshj.common.IOUtils; -import net.schmizz.sshj.connection.channel.direct.Session; - -import java.io.IOException; -import java.util.concurrent.TimeUnit; - -@NoArgsConstructor -@Slf4j -public class SshConnector { - - private SSHClient ssh; - - SshConnector(String hostname, int port, BasicCredentials credentials) { - connect(hostname, port); - if (isConnected()) { - authenticate(credentials); - } - } - - private void connect(String hostname, int port) { - try { - ssh = new SSHClient(); - ssh.addHostKeyVerifier(new DummyHostKeyVerifier()); - ssh.connect(hostname, port); - } catch (IOException ex) { - ssh = null; - throw new SshConnectionException("Unable to connect -> " + ex.getMessage()); - } - } - - private void authenticate(BasicCredentials credentials) { - if (ssh == null || !isConnected()) { - throw new SshConnectionException("Not connected."); - } - try { - ssh.authPublickey(credentials.getUsername()); - } catch(IOException ex) { - throw new SshConnectionException("Unable to authenticate due to some errors -> " + ex.getMessage()); - } - } - - String executeSingleCommand(String command) { - if(!isAuthenticated()) { - throw new SshConnectionException("Not authenticated connection to " + ssh.getRemoteAddress()); - } - try (Session session = ssh.startSession()) { - final Session.Command c = session.exec(command); - String error = IOUtils.readFully(c.getErrorStream()).toString(); - String output = IOUtils.readFully(c.getInputStream()).toString(); - c.join(5, TimeUnit.SECONDS); - if (exitStatusIndicatesThatSomethingWentWrong(c.getExitStatus())) { - throw new CommandExecutionException("Command execution failed (exit status: " + c.getExitStatus() + "; details: " + error + ")"); - } - return output; - } catch (IOException ex) { - throw new SshConnectionException("Unable to read command execution error message -> " + ex.getMessage()); - } - } - - void close() { - if (ssh != null) { - try { - ssh.disconnect(); - } catch (IOException e) { - log.warn(e.getMessage()); - } - ssh = null; - } - } - - private boolean exitStatusIndicatesThatSomethingWentWrong(int exitStatus) { - return exitStatus != 0; - } - - private boolean isConnected() { - return ssh.isConnected(); - } - - private boolean isAuthenticated() { - return (isConnected() && ssh.isAuthenticated()); - } - -} diff --git a/src/main/java/net/geant/nmaas/utils/ssh/SshSessionConnector.java b/src/main/java/net/geant/nmaas/utils/ssh/SshSessionConnector.java deleted file mode 100644 index 5ba7a7be2f3581db97ac6166d9afad6d9054eade..0000000000000000000000000000000000000000 --- a/src/main/java/net/geant/nmaas/utils/ssh/SshSessionConnector.java +++ /dev/null @@ -1,201 +0,0 @@ -package net.geant.nmaas.utils.ssh; - -import lombok.NoArgsConstructor; -import lombok.extern.log4j.Log4j2; -import lombok.extern.slf4j.Slf4j; -import net.geant.nmaas.kubernetes.AsyncConnector; -import net.schmizz.sshj.SSHClient; -import net.schmizz.sshj.common.IOUtils; -import net.schmizz.sshj.connection.ConnectionException; -import net.schmizz.sshj.connection.channel.direct.Session; -import net.schmizz.sshj.transport.TransportException; -import net.schmizz.sshj.userauth.keyprovider.KeyProvider; - -import java.io.IOException; -import java.io.InputStream; -import java.util.concurrent.TimeUnit; - -/** - * This is implementation of the SSH connector that allows executing ssh commands in scope of shell session - * How to use: - * 0. Prepare key pair - * 1. Create instance - * 2. Get Input Stream - * 3. Execute command in session - * 4. Read result from input stream - * 5. finally close connection - */ -@NoArgsConstructor -@Slf4j -public class SshSessionConnector implements AsyncConnector { - - private transient SSHClient client; - private transient Session session; - private transient Session.Shell shell; - - /** - * Creates SSH connection using public and private keys - * Connection->Authentication->Session->Shell - * @param hostname target - * @param port connection port - * @param credentials BasicCredentials (username is enough - this connector won't attempt password authentication) - * @param keyProvider private-public key pair - */ - public SshSessionConnector(String hostname, int port, BasicCredentials credentials, KeyProvider keyProvider) { - connect(hostname, port); - if (isConnected()) { - authenticate(credentials, keyProvider); - } - if (isAuthenticated()) { - openSession(); - } - } - - /** - * Test purpose constructor - * @param client - already connected (mocked) SSHClient instance - * @param credentials - * @param keyProvider - */ - public SshSessionConnector(SSHClient client, BasicCredentials credentials, KeyProvider keyProvider) { - this.client = client; - if(isConnected()) { - authenticate(credentials, keyProvider); - } - if(isAuthenticated()) { - openSession(); - } - } - - /** - * Use this input stream to obtain results from executed commands - * Returns entire shell appearance - * @return InputStream with shell output - */ - public InputStream getInputStream() { - return this.shell.getInputStream(); - } - - /** - * Returns stream of errors - * @return InputStream with errors - */ - public InputStream getErrorStream() { - return this.shell.getErrorStream(); - } - - private void connect(String hostname, int port) { - try { - client = new SSHClient(); - client.addHostKeyVerifier(new DummyHostKeyVerifier()); - client.connect(hostname, port); - } catch (IOException ex) { - client = null; - throw new SshConnectionException("Unable to connect -> " + ex.getMessage()); - } - } - - private void authenticate(BasicCredentials credentials, KeyProvider keyProvider) { - if (client == null || !isConnected()) { - throw new SshConnectionException("Not connected."); - } - try { - client.authPublickey(credentials.getUsername(), keyProvider); - } catch(IOException ex) { - throw new SshConnectionException("Unable to authenticate due to some errors -> " + ex.getMessage()); - } - } - - private void openSession() { - try { - this.session = client.startSession(); - this.session.allocateDefaultPTY(); - this.shell = this.session.startShell(); - } catch (ConnectionException | TransportException e) { - throw new SshConnectionException("Unable to start ssh session -> " + e.getMessage()); - } - } - - private void closeSession() { - try { - this.session.close(); - this.client.disconnect(); - } catch (IOException e) { - throw new SshConnectionException("Unable to stop ssh session -> " + e.getMessage()); - } - } - - /** - * utilizes shell OutputStream to execute commands during shell session - * the outcome of executed command will be available in resulting InputStream - * @param command command to be executed - */ - public void executeCommand(String command) { - if (!isSessionOpened()) { - throw new SshConnectionException("Session is not opened"); - } - try { - // write command to stream - // endline is essential for command to be executed - this.shell.getOutputStream().write((command + "\n").getBytes()); - this.shell.getOutputStream().flush(); - log.debug("Command:\t" + command + "\t written to shell stream"); - } catch (IOException e) { - throw new SshConnectionException("Unable to stop execute command in session -> " + e.getMessage()); - } - } - - /** - * Executes command SYNCHRONOUSLY in single scope - this should not affect shell session - * Result is available immediately as String - * @param command command to be executed - * @return outcome of executed command - */ - public String executeSingleCommand(String command) { - if (!isAuthenticated()) { - throw new SshConnectionException("Not authenticated connection to " + client.getRemoteAddress()); - } - try (Session ss = client.startSession()){ - final Session.Command c = ss.exec(command); - String error = IOUtils.readFully(c.getErrorStream()).toString(); - String output = IOUtils.readFully(c.getInputStream()).toString(); - c.join(5, TimeUnit.SECONDS); - if (exitStatusIndicatesThatSomethingWentWrong(c.getExitStatus())) { - return error; - } - return output; - } catch (IOException ex) { - throw new SshConnectionException("Unable to read command execution error message -> " + ex.getMessage()); - } - } - - /** - * closes session and disconnects client - * connection cannot be re-created after using this method using this instance - */ - public void close() { - if (client != null) { - if(isSessionOpened()) { - closeSession(); - } - client = null; - } - } - - private boolean exitStatusIndicatesThatSomethingWentWrong(int exitStatus) { - return exitStatus != 0; - } - - public boolean isConnected() { - return client != null && client.isConnected(); - } - - public boolean isAuthenticated() { - return isConnected() && client.isAuthenticated(); - } - - public boolean isSessionOpened() { - return isConnected() && isAuthenticated() && this.session.isOpen(); - } - -} diff --git a/src/main/java/net/geant/nmaas/utils/ssh/SshSessionConnectorDefaultData.java b/src/main/java/net/geant/nmaas/utils/ssh/SshSessionConnectorDefaultData.java deleted file mode 100644 index 0dc32821e5bf85c220f37e7e6955953d6c879665..0000000000000000000000000000000000000000 --- a/src/main/java/net/geant/nmaas/utils/ssh/SshSessionConnectorDefaultData.java +++ /dev/null @@ -1,115 +0,0 @@ -package net.geant.nmaas.utils.ssh; - -import net.schmizz.sshj.userauth.keyprovider.KeyPairWrapper; - -import java.security.*; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.PKCS8EncodedKeySpec; -import java.security.spec.X509EncodedKeySpec; -import java.util.Base64; - -/** - * hardcoded ssh keys - */ -public class SshSessionConnectorDefaultData { - - public static final String SSH_PUB_KEY_X509 = - "-----BEGIN PUBLIC KEY-----\n" + - "MIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0AMIIBCAKCAQEAo1lfdfK74mV5Xqr7sLYQ\n" + - "bp0kF3PZHXzQt6p+J3QuiOVMbe1XEVPZP7QiJqikrGEghIPklYvOSLQE9wcr2mA+\n" + - "NyIXWq3QI0zaGyW/C45dqr8o8bOjTos0i0CemYXQh58xKUl77bqytQvknv5oxOAV\n" + - "uJrdRNqt3UmSK2VW3cTAbUxXrsC/lYgGYz04VAHylV2VW5tqeWh83BXKIjp3ANkD\n" + - "OrfcO1WIUw9c/5yafBbBwkqlIlWO617exR6U/Ad178fTvJ4Shws7NvCpnunaow5Q\n" + - "pbk87LDb/R/Hzbo/I+98D94qMx+x9IVA7yvyegw3z6FHKx4RkvUgfk7cz7RuDh+m\n" + - "XwIBJQ==\n" + - "-----END PUBLIC KEY-----"; - public static final String SSH_PRIV_KEY = - "-----BEGIN PRIVATE KEY-----\n" + - "MIIEugIBADANBgkqhkiG9w0BAQEFAASCBKQwggSgAgEAAoIBAQCjWV918rviZXle\n" + - "qvuwthBunSQXc9kdfNC3qn4ndC6I5Uxt7VcRU9k/tCImqKSsYSCEg+SVi85ItAT3\n" + - "ByvaYD43IhdardAjTNobJb8Ljl2qvyjxs6NOizSLQJ6ZhdCHnzEpSXvturK1C+Se\n" + - "/mjE4BW4mt1E2q3dSZIrZVbdxMBtTFeuwL+ViAZjPThUAfKVXZVbm2p5aHzcFcoi\n" + - "OncA2QM6t9w7VYhTD1z/nJp8FsHCSqUiVY7rXt7FHpT8B3Xvx9O8nhKHCzs28Kme\n" + - "6dqjDlCluTzssNv9H8fNuj8j73wP3iozH7H0hUDvK/J6DDfPoUcrHhGS9SB+TtzP\n" + - "tG4OH6ZfAgElAoIBAAjUZgZgJdTi4/dHgzn8AONicKdSXsNS2tl+1mL/XHYaO3uQ\n" + - "SeVCzXkQp+Zp+xA8mflTPMnQNKn75Je7Ms2IqWrDko9HqmLF4kaoGCojXwJPhawz\n" + - "OUKEEK2Uyk182tbmAqhJKUse2To3/oUigjQnydKglla/tl79DtHpzVgYeRqqF+WZ\n" + - "ImNGQP4cqxWI7OJ7RHgfmh1zOmW4S2pf+a6zD/Z69vnqHKtkQ668jwwb91ySUGbG\n" + - "RiPL5h+YMTcebwmb9uYedxOz/t9fL2zARMwMcZrohLmRlCzZJHpI2ygL80zOV5u0\n" + - "rDITW51s6tQrPkSXrWeOw0cL+qzQ4BEvC0CaAg0CgYEA7ZE7Uw4BPl6vXstQ+PBG\n" + - "KdZeg2cKFfrYGwVdJQHKOluOVVrZe2YQJAsvZzZZe9YEJnJ2z9m1QJr3akqjgAvd\n" + - "xL1WiO+dUp9ZTwLJfR11hx6aiqzTJ5DS9VSqFQMPBQlAXce46KP18SMd8olM/iBW\n" + - "Wc3HEjqY7Lu7l32V9Q2B+qkCgYEAsAX0cFrzFad7fHpL20cu+Nb0Q/GKrodsO+zt\n" + - "xt8eIY9GrdEQ+opAVNPM3OW35buzr0KKrXttfOGnnDWw77Ikt14T7qrr/XM2bUA7\n" + - "ZMayk9VRGtbYfy6br6LMU7wDYfm0d6rYV7IpMOuvB5ufObpU5WGF4rX+lkV7FBQ4\n" + - "KNV8hccCgYBNDIHxbFNuLIvnZIj2ynee3b3JwI2mQ4RbymNl8r4gw791oOyWuVFX\n" + - "zEa5sMMTaAFYk9OBrmRMTe9gvkLda6He8Ux/cE6zAz+PPyyXR9MXENgs+cfxKA0R\n" + - "S+QifYhUqQ305tQvxnlHSajZCe91A5GglcQF6X20j+nQRGf8cxVmDQKBgByLWBI4\n" + - "QxhErD2y94tXpsCRj2T2vIsBNCVksO/RJ3sQUKaQmvgyGEUbbVRA7WOGONBP+tAG\n" + - "LW4ybjUByapUPU4q7nm62ikZmh+N4B4uDx7kUldhHDBMv0zp957gN+Zf2BNn0A44\n" + - "kQ7aHGIZPGo55EDfYdG8pdMt3Jt45oMpidCBAoGAY9PPPhnhawR7lU99qkAEp2q/\n" + - "hOIQuZVRXOLUS04EE+YaZoGZ7XbMWG48QYk++Kfr8LwbG3wWWHSywnx2kXrEUu7n\n" + - "we0cs9qikUog1n5kNUQYzQQwVFMOKG7deP3HQ0Mhwg7VLOyzodryr0sNEq5S+Xqf\n" + - "wWiKDSVL4RJDRKFJAdc=\n" + - "-----END PRIVATE KEY-----"; - - private static final String SSH_USERNAME = "nmaastest"; - private static final String SSH_HOST = "nmaastest-master1.qalab.geant.net"; - - private SshSessionConnectorDefaultData(){} - - /** - * transforms string public key to java format - * @param pubKey string public key - * @return Java formatted public key in X509 - * @throws InvalidKeySpecException - * @throws NoSuchAlgorithmException - */ - public static PublicKey getPublicKey(String pubKey) throws InvalidKeySpecException, NoSuchAlgorithmException { - - KeyFactory kf = KeyFactory.getInstance("RSA"); - - String publicKeyContent = pubKey - .replaceAll("\\n", "") - .replace("-----BEGIN PUBLIC KEY-----", "") - .replace("-----END PUBLIC KEY-----", ""); - - X509EncodedKeySpec keySpecX509 = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyContent)); - return kf.generatePublic(keySpecX509); - - } - - /** - * transforms string private key to java format - * @param privKey string private key - * @return Java formatted private key in PKCS8 - * @throws NoSuchAlgorithmException - * @throws InvalidKeySpecException - */ - public static PrivateKey getPrivateKey(String privKey) throws NoSuchAlgorithmException, InvalidKeySpecException { - - KeyFactory kf = KeyFactory.getInstance("RSA"); - - String privateKeyContent = privKey - .replaceAll("\\n", "") - .replace("-----BEGIN PRIVATE KEY-----", "") - .replace("-----END PRIVATE KEY-----", ""); - - PKCS8EncodedKeySpec keySpecPKCS8 = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyContent)); - return kf.generatePrivate(keySpecPKCS8); - - } - - public static SshSessionConnector getDefaultConnector() throws InvalidKeySpecException, NoSuchAlgorithmException { - PublicKey publicKey = getPublicKey(SshSessionConnectorDefaultData.SSH_PUB_KEY_X509); - PrivateKey privateKey = getPrivateKey(SshSessionConnectorDefaultData.SSH_PRIV_KEY); - KeyPair kp = new KeyPair(publicKey, privateKey); - - return new SshSessionConnector( - SSH_HOST, - 22, - new BasicCredentials(SSH_USERNAME), - new KeyPairWrapper(kp) - ); - } -} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 081aab25a4486b075914999a7f0206bc4a6ab21a..e1a7fec0bb607254fc5f13aa46d282c60d7e324a 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -92,8 +92,6 @@ spring.data.web.pageable.default-page-size=1000 # ------------------ # helm.update.async.enabled=${HELM_UPDATE_ASYNC_ENABLED:true} helm.update.async.cron=${HELM_UPDATE_ASYNC_CRON:0 0 * * * ?} -helm.address=${HELM_ADDRESS:10.134.241.6} -helm.username=${HELM_USERNAME:nmaas} helm.useLocalCharts=${HELM_USELOCALCHARTS:true} helm.repositoryName=${HELM_REPOSITORY:nmaas-test} helm.repositoryUrl=${HELM_REPOSITORY_URL:https://nmaas-test.helm.repository} diff --git a/src/main/resources/changelog.json b/src/main/resources/changelog.json index cdde8cebed51340bc652a4352057fb72a88f2f42..8c1c9229d5bc342893c7e579ae91803ec0e156f8 100644 --- a/src/main/resources/changelog.json +++ b/src/main/resources/changelog.json @@ -2,12 +2,32 @@ "versions" : [ { "verNo" : "1.7.0", - "date" : "(2025/-/-)", + "date" : "(2025/04/02)", "topic" : [ { - "title" : "--", + "title" : "Integration with OIDC-compliant IdP", + "tags" : "[New feature]", + "description" : "Moved away from the custom SAML-based IdP integration in favor of adding OIDC support" + }, + { + "title" : "Major dependencies upgrade", + "tags" : "[Enhancement]", + "description" : "Upgraded to Java 17 and Spring Boot to version 3.4" + }, + { + "title" : "Improved bulk application deployment views", + "tags" : "[Enhancement]", + "description" : "Added bulk deployment status and progress information on the bulk details view and summary information about all ongoing deployments" + }, + { + "title" : "User access token management", + "tags" : "[New feature]", + "description" : "Added possibility for users to generate personal access tokens to be used for interaction with the API bypassing the Portal" + }, + { + "title" : "Applied caching for improved performance", "tags" : "[Enhancement]", - "description" : "--" + "description" : "Implemented data caching mechanism to speed up application catalog view load time" } ] }, diff --git a/src/test/java/net/geant/nmaas/portal/api/auth/BasicAuthControllerTest.java b/src/test/java/net/geant/nmaas/portal/api/auth/BasicAuthControllerTest.java index 1e44a5d034dc3dbae43feaea323b2d4171d9928d..bc1865721203c6455ec2781927f82781c2bc4c47 100644 --- a/src/test/java/net/geant/nmaas/portal/api/auth/BasicAuthControllerTest.java +++ b/src/test/java/net/geant/nmaas/portal/api/auth/BasicAuthControllerTest.java @@ -21,55 +21,53 @@ import static org.mockito.Mockito.when; public class BasicAuthControllerTest { - private BasicAuthController basicAuthController; - - private DomainService domains = mock(DomainService.class); + private final DomainService domains = mock(DomainService.class); + private final PasswordEncoder passwordEncoder = mock(PasswordEncoder.class); + private final UserLoginRegisterService userLoginService = mock(UserLoginRegisterService.class); - private PasswordEncoder passwordEncoder = mock(PasswordEncoder.class); - - private UserLoginRegisterService userLoginService = mock(UserLoginRegisterService.class); + private BasicAuthController basicAuthController; @BeforeEach - public void setup(){ + void setup() { basicAuthController = new BasicAuthController(null, domains, passwordEncoder, null, null, userLoginService); when(passwordEncoder.matches(any(), any())).thenReturn(true); } @Test - public void testValidateWithValidUserNameAndPassword() throws AuthenticationException { + void testValidateWithValidUserNameAndPassword() throws AuthenticationException { basicAuthController.validate("TEST", "TEST", "TEST", true); } @Test - public void testValidateWithInvalidUserNameAndValidPassword() throws AuthenticationException { + void testValidateWithInvalidUserNameAndValidPassword() throws AuthenticationException { assertThrows(AuthenticationException.class, () -> { basicAuthController.validate(null, "TEST", "TEST", true); }); } @Test - public void testValidateWithValidUserNameAndInvalidPassword() throws AuthenticationException { + void testValidateWithValidUserNameAndInvalidPassword() throws AuthenticationException { assertThrows(AuthenticationException.class, () -> { basicAuthController.validate("TEST", null, "TEST", true); }); } @Test - public void testValidateWithInvalidUserNameAndInvalidPassword() throws AuthenticationException { + void testValidateWithInvalidUserNameAndInvalidPassword() throws AuthenticationException { assertThrows(AuthenticationException.class, () -> { basicAuthController.validate(null, null, "TEST", true); }); } @Test - public void testValidateWithValidUserNameAndValidPasswordAndUserNotEnabled() throws AuthenticationException { + void testValidateWithValidUserNameAndValidPasswordAndUserNotEnabled() throws AuthenticationException { assertThrows(AuthenticationException.class, () -> { basicAuthController.validate("TEST", "TEST", "TEST", false); }); } @Test - public void testValidateWithValidUserNameAndWrongPassword() throws AuthenticationException { + void testValidateWithValidUserNameAndWrongPassword() throws AuthenticationException { assertThrows(AuthenticationException.class, () -> { when(passwordEncoder.matches(any(), any())).thenReturn(false); basicAuthController.validate("TEST", "TEST", "TEST", true); @@ -77,7 +75,7 @@ public class BasicAuthControllerTest { } @Test - public void shouldNotChangeUserRoles() { + void shouldNotChangeUserRoles() { User user = new User("testUser"); user.setPrivacyPolicyAccepted(true); user.setTermsOfUseAccepted(true); @@ -86,7 +84,7 @@ public class BasicAuthControllerTest { } @Test - public void shouldAddIncompleteRoleToUser() { + void shouldAddIncompleteRoleToUser() { when(domains.getGlobalDomain()).thenReturn(Optional.of(new Domain("name", "codename"))); User user = new User("testUser"); user.setPrivacyPolicyAccepted(false); diff --git a/src/test/java/net/geant/nmaas/portal/api/auth/OIDCAuthControllerTest.java b/src/test/java/net/geant/nmaas/portal/api/auth/OIDCAuthControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..90b0cdce3b969d71985ae2f40f9266854f644f11 --- /dev/null +++ b/src/test/java/net/geant/nmaas/portal/api/auth/OIDCAuthControllerTest.java @@ -0,0 +1,158 @@ +package net.geant.nmaas.portal.api.auth; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.core.HttpHeaders; +import net.geant.nmaas.portal.api.configuration.ConfigurationView; +import net.geant.nmaas.portal.api.security.JWTTokenService; +import net.geant.nmaas.portal.persistent.entity.Domain; +import net.geant.nmaas.portal.persistent.entity.Role; +import net.geant.nmaas.portal.persistent.entity.User; +import net.geant.nmaas.portal.persistent.entity.UserRole; +import net.geant.nmaas.portal.service.ConfigurationManager; +import net.geant.nmaas.portal.service.DomainService; +import net.geant.nmaas.portal.service.OidcUserService; +import net.geant.nmaas.portal.service.UserLoginRegisterService; +import net.geant.nmaas.portal.service.UserService; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.oauth2.core.oidc.OidcIdToken; +import org.springframework.security.oauth2.core.oidc.user.OidcUser; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.web.servlet.view.RedirectView; + +import java.lang.reflect.Constructor; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class OIDCAuthControllerTest { + + @InjectMocks + private OIDCAuthController oidcAuthController; + + @Mock + private OidcUserService oidcUserService; + @Mock + private JWTTokenService jwtTokenService; + @Mock + private UserLoginRegisterService loginRegisterService; + @Mock + private UserService userService; + @Mock + private PasswordEncoder passwordEncoder; + @Mock + private DomainService domainService; + @Mock + private ConfigurationManager configurationManager; + @Mock + private HttpServletRequest request; + + @Test + void shouldReturnOidcTokenOnSuccessfulLinking() throws Exception { + // Given + OidcLogin oidcLogin = new OidcLogin("user@example.com", "pass123", "uuid-123", "John", "Doe", "oidc-token"); + + Constructor<User> userConstructor = User.class.getDeclaredConstructor(); + userConstructor.setAccessible(true); + User user = userConstructor.newInstance(); + + ReflectionTestUtils.setField(user, "password", "hashed-password"); + ReflectionTestUtils.setField(user, "enabled", true); + ReflectionTestUtils.setField(user, "termsOfUseAccepted", true); + ReflectionTestUtils.setField(user, "privacyPolicyAccepted", true); + + Constructor<Domain> domainConstructor = Domain.class.getDeclaredConstructor(); + domainConstructor.setAccessible(true); + Domain domain = domainConstructor.newInstance(); + ReflectionTestUtils.setField(domain, "name", "Global"); + + UserRole role = new UserRole(user, domain, Role.ROLE_USER); + ReflectionTestUtils.setField(user, "roles", List.of(role)); + + when(userService.findByEmail(any())).thenReturn(user); + when(passwordEncoder.matches(any(), any())).thenReturn(true); + + ConfigurationView config = mock(ConfigurationView.class); + when(config.isMaintenance()).thenReturn(false); + when(configurationManager.getConfiguration()).thenReturn(config); + + User linkedUser = userConstructor.newInstance(); + when(oidcUserService.linkUser(any(), any(), any(), any())).thenReturn(linkedUser); + when(jwtTokenService.getToken(any())).thenReturn("jwt-token"); + when(jwtTokenService.getRefreshToken(any())).thenReturn("refresh-token"); + + when(request.getHeader(HttpHeaders.HOST)).thenReturn("localhost"); + when(request.getHeader(HttpHeaders.USER_AGENT)).thenReturn("JUnit"); + + // When + UserOidcToken result = oidcAuthController.oidcLinkedSuccess(oidcLogin, request); + + // Then + assertEquals("jwt-token", result.token()); + assertEquals("refresh-token", result.refreshToken()); + assertEquals("uuid-123", result.oidcToken()); + + verify(loginRegisterService).registerNewSuccessfulLogin(eq(user), any(), any(), any()); + } + + @Test + void shouldRedirectToLoginSuccessIfOidcUserIsValid() throws Exception { + // given + OidcUser oidcUser = mock(OidcUser.class); + OidcIdToken idToken = mock(OidcIdToken.class); + when(idToken.getTokenValue()).thenReturn("oidc-token"); + when(oidcUser.getIdToken()).thenReturn(idToken); + + when(oidcUserService.externalUserRequiredLinking(any())).thenReturn(false); + + Constructor<User> userConstructor = User.class.getDeclaredConstructor(); + userConstructor.setAccessible(true); + User user = userConstructor.newInstance(); + + when(oidcUserService.checkUser(any())).thenReturn(user); + when(jwtTokenService.getToken(any())).thenReturn("jwt-token"); + when(jwtTokenService.getRefreshToken(any())).thenReturn("refresh-token"); + + when(request.getHeader(HttpHeaders.HOST)).thenReturn("localhost"); + when(request.getHeader(HttpHeaders.USER_AGENT)).thenReturn("JUnit"); + + // when + RedirectView result = oidcAuthController.oidcLoginSuccess(oidcUser, request); + + // then + assertTrue(result.getUrl().contains("login-success")); + assertTrue(result.getUrl().contains("token=jwt-token")); + assertTrue(result.getUrl().contains("refresh_token=refresh-token")); + assertTrue(result.getUrl().contains("oidc_token=oidc-token")); + + verify(loginRegisterService).registerNewSuccessfulLogin(eq(user), any(), any(), any()); + } + + @Test + void shouldRedirectToLinkingIfExternalUserRequiresLinking() { + // given + OidcUser oidcUser = mock(OidcUser.class); + OidcIdToken idToken = mock(OidcIdToken.class); + when(idToken.getTokenValue()).thenReturn("oidc-token"); + when(oidcUser.getIdToken()).thenReturn(idToken); + + when(oidcUserService.externalUserRequiredLinking(any())).thenReturn(true); + + // when + RedirectView result = oidcAuthController.oidcLoginSuccess(oidcUser, request); + + // then + assertTrue(result.getUrl().contains("/login-linking?oidc_token=oidc-token")); + } +} diff --git a/src/test/java/net/geant/nmaas/portal/api/info/ContentControllerTest.java b/src/test/java/net/geant/nmaas/portal/api/info/ContentControllerTest.java index 2f0c1361d6314c0a197b65e3c96f182e4d550449..ba5bc93d64aed5d8b77a4a1024ef097dc1a14c38 100644 --- a/src/test/java/net/geant/nmaas/portal/api/info/ContentControllerTest.java +++ b/src/test/java/net/geant/nmaas/portal/api/info/ContentControllerTest.java @@ -24,7 +24,7 @@ public class ContentControllerTest { private final ContentController contentController = new ContentController(repository, modelMapper); @Test - public void shouldGetContent(){ + void shouldGetContent(){ when(repository.findByName(CONTENT.getName())).thenReturn(Optional.of(CONTENT)); ContentView contentView = this.contentController.getContent(CONTENT.getName()); assertEquals(CONTENT.getName(), contentView.getName()); @@ -33,7 +33,7 @@ public class ContentControllerTest { } @Test - public void shouldThrowAnExceptionWhenContentNotFound(){ + void shouldThrowAnExceptionWhenContentNotFound(){ assertThrows(ProcessingException.class, () -> { when(repository.findByName(CONTENT.getName())).thenReturn(Optional.empty()); contentController.getContent(CONTENT.getName()); diff --git a/src/test/java/net/geant/nmaas/portal/api/kubernetes/connectors/KubernetesConnectorHelperTest.java b/src/test/java/net/geant/nmaas/portal/api/kubernetes/connectors/KubernetesConnectorHelperTest.java index bf18a7d3795a3073adb771973cc6dde3ad5685a2..5c49762cec31aa2e9377dda735faaf9145ff600f 100644 --- a/src/test/java/net/geant/nmaas/portal/api/kubernetes/connectors/KubernetesConnectorHelperTest.java +++ b/src/test/java/net/geant/nmaas/portal/api/kubernetes/connectors/KubernetesConnectorHelperTest.java @@ -32,9 +32,7 @@ import static org.mockito.Mockito.when; public class KubernetesConnectorHelperTest { private final AppDeploymentRepositoryManager appDeploymentRepositoryManager = mock(AppDeploymentRepositoryManager.class); - private final KubernetesClientConfigFactory configFactory = mock(KubernetesClientConfigFactory.class); - private final ApplicationInstanceService applicationInstanceService = mock(ApplicationInstanceService.class); private KubernetesConnectorHelper helper; diff --git a/src/test/java/net/geant/nmaas/portal/api/kubernetes/observable/SshConnectionShellSessionObservableTest.java b/src/test/java/net/geant/nmaas/portal/api/kubernetes/observable/SshConnectionShellSessionObservableTest.java deleted file mode 100644 index 99571be4643c59ab487a8ef4a7a8ca1b9dfbc9dc..0000000000000000000000000000000000000000 --- a/src/test/java/net/geant/nmaas/portal/api/kubernetes/observable/SshConnectionShellSessionObservableTest.java +++ /dev/null @@ -1,129 +0,0 @@ -package net.geant.nmaas.portal.api.kubernetes.observable; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; -import net.geant.nmaas.kubernetes.shell.observable.SshConnectionShellSessionObservable; -import net.geant.nmaas.portal.api.domain.K8sShellCommandRequest; -import net.geant.nmaas.utils.ssh.SshSessionConnector; -import net.geant.nmaas.utils.ssh.SshSessionConnectorDefaultData; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.io.PipedInputStream; -import java.io.PipedOutputStream; -import java.security.NoSuchAlgorithmException; -import java.security.spec.InvalidKeySpecException; -import java.util.ArrayList; -import java.util.List; -import java.util.Observable; -import java.util.Observer; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -@Slf4j -public class SshConnectionShellSessionObservableTest { - - public final String PUB_KEY = SshSessionConnectorDefaultData.SSH_PUB_KEY_X509; - public final String PRIV_KEY = SshSessionConnectorDefaultData.SSH_PRIV_KEY; - - @Test - public void testPublicKeyConversion() throws InvalidKeySpecException, NoSuchAlgorithmException { - assertNotNull(SshSessionConnectorDefaultData.getPublicKey(PUB_KEY)); - } - - @Test - public void testPrivateKeyConversion() throws InvalidKeySpecException, NoSuchAlgorithmException { - assertNotNull(SshSessionConnectorDefaultData.getPrivateKey(PRIV_KEY)); - } - - @Getter - @Slf4j - private static class TestObserver implements Observer { - - private final List<String> messages = new ArrayList<>(); - - @Override - public void update(Observable observable, Object o) { - log.debug("Test observer received:\t" + o.toString()); - this.messages.add(o.toString()); - } - } - - @Test - public void testSynchronousCommandExecution() throws IOException { - SshSessionConnector mockConnector = mock(SshSessionConnector.class); - - PipedInputStream inputStream = new PipedInputStream(); - PipedOutputStream outputStream = new PipedOutputStream(inputStream); - - when(mockConnector.getInputStream()).thenReturn(inputStream); - when(mockConnector.getErrorStream()).thenReturn(new PipedInputStream()); // do nothing - - when(mockConnector.executeSingleCommand(anyString())).thenReturn("result\nresult\nresult"); - - SshConnectionShellSessionObservable underTest = new SshConnectionShellSessionObservable("sessionId", mockConnector); - TestObserver to = new TestObserver(); - underTest.addObserver(to); - - String line1 = "some result line\r\n"; - underTest.executeCommand(new K8sShellCommandRequest(line1, "")); - String line2 = "host@localhost:~/ $ \n"; - underTest.executeCommand(new K8sShellCommandRequest(line2, "")); - String line3 = "continuation\r\n"; - underTest.executeCommand(new K8sShellCommandRequest(line3, "")); - String line4 = "some another line\r\n"; - underTest.executeCommand(new K8sShellCommandRequest(line4, "")); - - assertEquals(4 * 3, to.getMessages().size()); - - underTest.complete(); - } - - @Test - public void testAsynchronousCommandExecution() throws IOException, InterruptedException { - SshSessionConnector mockConnector = mock(SshSessionConnector.class); - - PipedInputStream inputStream = new PipedInputStream(); - PipedOutputStream outputStream = new PipedOutputStream(inputStream); - - doAnswer(invocation -> { - outputStream.write(invocation.getArgument(0).toString().getBytes()); - outputStream.flush(); - return null; - }).when(mockConnector).executeCommand(anyString()); - - when(mockConnector.getInputStream()).thenReturn(inputStream); - when(mockConnector.getErrorStream()).thenReturn(new PipedInputStream()); // do nothing - - SshConnectionShellSessionObservable underTest = new SshConnectionShellSessionObservable("sessionId", mockConnector); - TestObserver to = new TestObserver(); - underTest.addObserver(to); - - - String line1 = "some result line\r\n"; - underTest.executeCommandAsync(new K8sShellCommandRequest(line1, "")); - String line2 = "host@localhost:~/ $ \n"; - underTest.executeCommandAsync(new K8sShellCommandRequest(line2, "")); - String line3 = "continuation\r\n"; - underTest.executeCommandAsync(new K8sShellCommandRequest(line3, "")); - String line4 = "some another line\r\n"; - underTest.executeCommandAsync(new K8sShellCommandRequest(line4, "")); - - Thread.sleep(100); - - assertEquals(4, to.getMessages().size()); - assertTrue(to.messages.get(0).equalsIgnoreCase(line1.replace("\r", "<#>NEWLINE<#>").trim())); - assertEquals(to.messages.get(1).trim(), line2.trim()); - assertTrue(to.messages.get(2).equalsIgnoreCase(line3.replace("\r", "<#>NEWLINE<#>").trim())); - assertTrue(to.messages.get(3).equalsIgnoreCase(line4.replace("\r", "<#>NEWLINE<#>").trim())); - - underTest.complete(); - - } -} diff --git a/src/test/java/net/geant/nmaas/portal/service/TokenAuthenticationServiceUnitTest.java b/src/test/java/net/geant/nmaas/portal/service/TokenAuthenticationServiceUnitTest.java index 2110dd17f1b7451ae92a23efc19f53c7f2650952..66019ad01b7fb9c6db952fc6bfba04cdc0aa3501 100644 --- a/src/test/java/net/geant/nmaas/portal/service/TokenAuthenticationServiceUnitTest.java +++ b/src/test/java/net/geant/nmaas/portal/service/TokenAuthenticationServiceUnitTest.java @@ -75,7 +75,7 @@ class TokenAuthenticationServiceTest { @Test void shouldReturnAuthenticationWhenAuthHeaderIsValid() { // given - String token = "token123"; + String token = "example.jwt.token"; String validHeader = AUTH_METHOD + " " + token; String username = "testUser"; @@ -104,7 +104,7 @@ class TokenAuthenticationServiceTest { @Test void shouldReturnAuthenticationWithNoAuthoritiesWhenRolesAreNotList() { // given - String token = "token123"; + String token = "example.jwt.token"; String validHeader = AUTH_METHOD + " " + token; String username = "testUser"; diff --git a/src/test/java/net/geant/nmaas/portal/service/impl/OidcUserServiceImplTest.java b/src/test/java/net/geant/nmaas/portal/service/impl/OidcUserServiceImplTest.java index 17a88fd3e3d795e170014d2743e7954f3c2e8938..bfc13ee3cefd9cee2a5e06dda70384ab1d1e3246 100644 --- a/src/test/java/net/geant/nmaas/portal/service/impl/OidcUserServiceImplTest.java +++ b/src/test/java/net/geant/nmaas/portal/service/impl/OidcUserServiceImplTest.java @@ -88,13 +88,5 @@ class OidcUserServiceImplTest { assertThrows(ExternalUserMatchException.class, () -> oidcUserService.checkUser(oidcUser)); } - @Test - void shouldThrowExceptionWhenLinkingNotAllowed() { - - //when - when(userService.existsByEmail("test@example.com")).thenReturn(true); - //then - assertThrows(ExternalUserCanNotBeLinked.class, () -> oidcUserService.checkUser(oidcUser)); - } } \ No newline at end of file diff --git a/src/test/java/net/geant/nmaas/utils/bash/DefaultCommandExecutorTest.java b/src/test/java/net/geant/nmaas/utils/bash/DefaultCommandExecutorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..084966fd38b71f019a0ebe94a068e61bdc5556a4 --- /dev/null +++ b/src/test/java/net/geant/nmaas/utils/bash/DefaultCommandExecutorTest.java @@ -0,0 +1,30 @@ +package net.geant.nmaas.utils.bash; + +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.function.Predicate; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class DefaultCommandExecutorTest { + + private final CommandExecutor executor = new DefaultCommandExecutor(); + + @Test + void shouldRunHelmCommand() throws IOException { + String output = executor.executeWithOutput(new Command() { + @Override + public String asString() { + return "helm"; + } + + @Override + public Predicate<String> isOutputCorrect() { + return null; + } + }); + assertNotNull(output); + } + +} diff --git a/src/test/java/net/geant/nmaas/utils/ssh/SingleCommandExecutorTest.java b/src/test/java/net/geant/nmaas/utils/ssh/SingleCommandExecutorTest.java deleted file mode 100644 index 4e1c595e349d85ea2bb114f68bdb42fef0f26753..0000000000000000000000000000000000000000 --- a/src/test/java/net/geant/nmaas/utils/ssh/SingleCommandExecutorTest.java +++ /dev/null @@ -1,33 +0,0 @@ -package net.geant.nmaas.utils.ssh; - -import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.components.helm.commands.HelmListCommand; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class SingleCommandExecutorTest { - - private HelmListCommand command; - - private SingleCommandExecutor executor; - - @BeforeEach - public void setup() { - command = HelmListCommand.command("v2", "namespace", false); - executor = SingleCommandExecutor.getExecutor("", ""); - } - - @Test - public void shouldProperlyValidateCommandExecutionOutput() throws CommandExecutionException { - executor.validateOutput("No error string at the beginning ...", command.isOutputCorrect()); - } - - @Test - public void shouldProperlyValidateCommandExecutionOutputAndThrowExceptionOnError() throws CommandExecutionException { - assertThrows(CommandExecutionException.class, () -> { - executor.validateOutput("Error ...", command.isOutputCorrect()); - }); - } - -} diff --git a/src/test/java/net/geant/nmaas/utils/ssh/SshSessionConnectorTest.java b/src/test/java/net/geant/nmaas/utils/ssh/SshSessionConnectorTest.java deleted file mode 100644 index b10c48ac58e1e10295217762836ba9942704189e..0000000000000000000000000000000000000000 --- a/src/test/java/net/geant/nmaas/utils/ssh/SshSessionConnectorTest.java +++ /dev/null @@ -1,109 +0,0 @@ -package net.geant.nmaas.utils.ssh; - -import net.schmizz.sshj.SSHClient; -import net.schmizz.sshj.connection.channel.direct.Session; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.test.util.ReflectionTestUtils; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.PipedInputStream; -import java.io.PipedOutputStream; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class SshSessionConnectorTest { - - private SshSessionConnector connector; - private SSHClient mockedClient; - private Session mockedSession; - private Session.Shell mockedShell; - - private PipedInputStream inputStream; - private OutputStream outputStream; - - @BeforeEach - public void setup() throws NoSuchFieldException, IOException { - this.connector = new SshSessionConnector(); - this.mockedClient = mock(SSHClient.class); - this.mockedSession = mock(Session.class); - this.mockedShell = mock(Session.Shell.class); - - when(this.mockedClient.isConnected()).thenReturn(true); - when(this.mockedClient.isAuthenticated()).thenReturn(true); - when(this.mockedSession.isOpen()).thenReturn(true); - - // mocked connected pipes - this.inputStream = new PipedInputStream(); - this.outputStream = new PipedOutputStream(this.inputStream); - - when(this.mockedShell.getOutputStream()).thenReturn(this.outputStream); - when(this.mockedShell.getInputStream()).thenReturn(this.inputStream); - - // use reflection to set fields - ReflectionTestUtils.setField(connector, "client", this.mockedClient); - ReflectionTestUtils.setField(connector, "session", this.mockedSession); - ReflectionTestUtils.setField(connector, "shell", this.mockedShell); - } - - @Test - public void shouldNotBeConnectedWhenConnectionIsNotSet() { - SshSessionConnector underTest = new SshSessionConnector(); - assertFalse(underTest.isConnected()); - assertFalse(underTest.isAuthenticated()); - assertFalse(underTest.isSessionOpened()); - } - - @Test - public void shouldBeConnected() { - assertTrue(connector.isConnected()); - assertTrue(connector.isAuthenticated()); - assertTrue(connector.isSessionOpened()); - } - - @Test - public void shouldPassCommandThroughStreams() throws IOException { - InputStream is = connector.getInputStream(); - BufferedReader reader = new BufferedReader(new InputStreamReader(is)); - String result; - - String test1 = "first test"; - connector.executeCommand(test1); - - result = reader.readLine(); - assertTrue(test1.equalsIgnoreCase(result)); - - String test2 = "second test"; - String test3 = "third test"; - connector.executeCommand(test2); - connector.executeCommand(test3); - - result = reader.readLine(); - assertTrue(test2.equalsIgnoreCase(result)); - - result = reader.readLine(); - assertTrue(test3.equalsIgnoreCase(result)); - } - - @Test - public void shouldCloseConnection() throws IOException { - assertTrue(connector.isConnected()); - assertTrue(connector.isAuthenticated()); - assertTrue(connector.isSessionOpened()); - - connector.close(); - - verify(mockedSession, times(1)).close(); - verify(mockedClient, times(1)).disconnect(); - - } -} diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties index ad6b4107b31e6c10f988f7646ac71436f194bb57..c275520b3a6e59f8e119598fa33acd3dbaa19ce4 100644 --- a/src/test/resources/application.properties +++ b/src/test/resources/application.properties @@ -103,15 +103,11 @@ jwt.resetSigningKey=qCR0dochH55skij2aHltXE3Sh5IvgrY87wI4MACoa8aMYeFiCA0jWek9KYvE jwt.resetTokenValidFor=1800000 jwt.resetTokenRegistrationValid=86400000 - - # ------------------ # # Helm configuration # # ------------------ # helm.update.async.enabled=true helm.update.async.cron=0 * * * * ? -helm.address=10.134.241.6 -helm.username=nmaas helm.useLocalCharts=true helm.repositoryName=nmaas-test helm.repositoryUrl=https://nmaas-test.helm.repository diff --git a/src/test/shell/data/i18n/de.json b/src/test/shell/data/i18n/de.json index 4cce919a9cac8382e13b9c588fabc3776f579988..a9ffdf59f5909130267bb47ff61e6f5a796e5f59 100644 --- a/src/test/shell/data/i18n/de.json +++ b/src/test/shell/data/i18n/de.json @@ -93,6 +93,10 @@ "LOGIN_CAN_NOT_BE_PERFORMED_MESSAGE": "Systemkomponenten-Login kann nicht durchgeführt werden", "USER_DISABLED_MESSAGE": "User is disabled" }, + "ACCOUNT_LINKING": { + "HEADER": "Verknüpfung von Konten", + "INFO": "Sie versuchen, sich bei einem lokalen Konto anzumelden, das mit der von Ihnen angegebenen E-Mail-Adresse bereits in unserem System existiert. Bitte geben Sie Ihr Passwort ein, um Ihre Identität zu bestätigen und die Verknüpfung der Konten zu bestätigen." + }, "TEST_INSTANCE_MODAL": { "HEADER": "nmaas Testinstanz", "BODY": "Bitte beachten Sie, dass dies eine Test-nmaas-Instanz ist. Die Benutzerisolations- und Datensicherheitsmaßnahmen sind nicht so streng wie in der Produktionsinstanz. Verwenden Sie diese Instanz nicht zur Überwachung realer Geräte.", diff --git a/src/test/shell/data/i18n/en.json b/src/test/shell/data/i18n/en.json index 8dd9565922a6b8de7d1174ac468ed59421334413..b305c12961697f41c5829a9ab2c7ce332d8b9cd5 100644 --- a/src/test/shell/data/i18n/en.json +++ b/src/test/shell/data/i18n/en.json @@ -93,6 +93,11 @@ "LOGIN_CAN_NOT_BE_PERFORMED_MESSAGE": "System component login cannot be performed", "USER_DISABLED_MESSAGE": "User is disabled" }, + "ACCOUNT_LINKING": { + "HEADER": "Account linking", + "INFO": "You are trying to log in to an local account that already exists in our system with the email address you provided. Please enter your password to verify it's You and confirm linking of these accounts.", + "CONFIRM": "Confirm" + }, "TEST_INSTANCE_MODAL": { "HEADER": "nmaas test instance", "BODY": "Please be aware that this is a test nmaas instance. The user isolation and data security measures are not so strict as on the production instance. Do not use this instance to monitor real equipment.", diff --git a/src/test/shell/data/i18n/fr.json b/src/test/shell/data/i18n/fr.json index ae72dda76bce23267c94aad13a36c485484d7a6d..013fcd57fc58d69d431ee3bcb159a4a58c03d773 100644 --- a/src/test/shell/data/i18n/fr.json +++ b/src/test/shell/data/i18n/fr.json @@ -93,6 +93,12 @@ "LOGIN_CAN_NOT_BE_PERFORMED_MESSAGE": "Il est impossible de se connecter au composant système", "USER_DISABLED_MESSAGE": "User is disabled" }, + "ACCOUNT_LINKING": { + "HEADER": "Association de comptes", + "INFO": "Vous essayez de vous connecter à un compte local qui existe déjà dans notre système avec l'adresse e-mail que vous avez fournie. Veuillez entrer votre mot de passe pour vérifier votre identité et confirmer la liaison des comptes.", + "CONFIRM": "Confirm" + + }, "TEST_INSTANCE_MODAL": { "HEADER": "Instance de test nmaas", "BODY": "Veuillez noter qu'il s'agit d'une instance de test nmaas. Les mesures d'isolation de l'utilisateur et de sécurité des données ne sont pas aussi strictes que sur l'instance de production. N'utilisez pas cette instance pour surveiller un équipement réel.", diff --git a/src/test/shell/data/i18n/pl.json b/src/test/shell/data/i18n/pl.json index 756a41814e2570e2e2a4d1ea75913d20b405b9d8..f9d315babc51d0dbc5c27d5388742dc8ce8ee4e4 100644 --- a/src/test/shell/data/i18n/pl.json +++ b/src/test/shell/data/i18n/pl.json @@ -93,6 +93,11 @@ "LOGIN_CAN_NOT_BE_PERFORMED_MESSAGE": "Błąd logowania do systemu", "USER_DISABLED_MESSAGE": "Użytkownik jest nieaktywny" }, + "ACCOUNT_LINKING": { + "HEADER": "Powiązanie kont", + "INFO": "Próbujesz zalogować się na lokalne konto, które już istnieje w naszym systemie dla podanego adresu e-mail. Wprowadź swoje hasło, aby potwierdzić swoją tożsamość i zatwierdzić połączenie kont.", + "CONFIRM": "Potwierdź" + }, "TEST_INSTANCE_MODAL": { "HEADER": "Instancja testowa nmaas", "BODY": "Pamiętaj, że jest to testowa instancja nmaas. Izolacja użytkowników oraz środki zabezpieczenia danych nie są tak rygorystyczne, jak w przypadku instancji produkcyjnej. Nie używaj tej instancji do monitorowania rzeczywistych urządzeń.",