diff --git a/charts/nebulous-security-manager/Chart.yaml b/charts/nebulous-security-manager/Chart.yaml index a781ba3..b99a77a 100644 --- a/charts/nebulous-security-manager/Chart.yaml +++ b/charts/nebulous-security-manager/Chart.yaml @@ -22,3 +22,8 @@ version: 0.1.0 # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. appVersion: "latest" + +dependencies: + - name: postgresql + version: 14.3.3 + repository: https://charts.bitnami.com/bitnami diff --git a/charts/nebulous-security-manager/templates/deployment.yaml b/charts/nebulous-security-manager/templates/deployment.yaml index e444abc..5333980 100644 --- a/charts/nebulous-security-manager/templates/deployment.yaml +++ b/charts/nebulous-security-manager/templates/deployment.yaml @@ -25,6 +25,10 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} serviceAccountName: {{ include "nebulous-security-manager.serviceAccountName" . }} + initContainers: + - name: wait-for-postgresql + image: docker.io/bitnami/postgresql:14.3.0 + command: ['sh', '-c', 'until pg_isready -h nebulous-overlay-network-manager-postgresql -p 5432; do echo waiting for database; sleep 2; done;'] securityContext: {{- toYaml .Values.podSecurityContext | nindent 8 }} containers: diff --git a/charts/nebulous-security-manager/values.yaml b/charts/nebulous-security-manager/values.yaml index 1072e3b..d3147f5 100644 --- a/charts/nebulous-security-manager/values.yaml +++ b/charts/nebulous-security-manager/values.yaml @@ -5,7 +5,7 @@ replicaCount: 1 image: - repository: "quay.io/nebulous/security-manager-java-spring-boot-demo" + repository: "quay.io/nebulous/security-manager-security-layer" pullPolicy: IfNotPresent # Overrides the image tag whose default is the chart appVersion. tag: "" @@ -38,7 +38,7 @@ securityContext: {} service: type: ClusterIP - port: 80 + port: 8080 ingress: enabled: false @@ -80,3 +80,12 @@ nodeSelector: {} tolerations: [] affinity: {} + +postgresql: + global: + postgresql: + auth: + postgresPassword: "nebulous" + username: "postgresql" + password: "postgresql" + database: "postgres" diff --git a/java-spring-boot-demo/.gitignore b/java-spring-boot-demo/.gitignore deleted file mode 100644 index 549e00a..0000000 --- a/java-spring-boot-demo/.gitignore +++ /dev/null @@ -1,33 +0,0 @@ -HELP.md -target/ -!.mvn/wrapper/maven-wrapper.jar -!**/src/main/**/target/ -!**/src/test/**/target/ - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ -build/ -!**/src/main/**/build/ -!**/src/test/**/build/ - -### VS Code ### -.vscode/ diff --git a/java-spring-boot-demo/Dockerfile b/java-spring-boot-demo/Dockerfile deleted file mode 100644 index 427e30e..0000000 --- a/java-spring-boot-demo/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -# -# Build stage -# -FROM docker.io/library/maven:3.9.2-eclipse-temurin-17 AS build -COPY src /home/app/src -COPY pom.xml /home/app -RUN mvn -f /home/app/pom.xml clean package - -# -# Package stage -# -FROM docker.io/library/eclipse-temurin:17-jre -COPY --from=build /home/app/target/demo-0.0.1-SNAPSHOT.jar /usr/local/lib/demo.jar -EXPOSE 8080 -ENTRYPOINT ["java","-jar","/usr/local/lib/demo.jar"] diff --git a/java-spring-boot-demo/pom.xml b/java-spring-boot-demo/pom.xml deleted file mode 100644 index 76e0f0e..0000000 --- a/java-spring-boot-demo/pom.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 3.1.0 - - - com.example - demo - 0.0.1-SNAPSHOT - demo - Demo project for Spring Boot - - 17 - - - - org.springframework.boot - spring-boot-starter - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - diff --git a/java-spring-boot-demo/src/main/java/com/example/demo/DemoApplication.java b/java-spring-boot-demo/src/main/java/com/example/demo/DemoApplication.java deleted file mode 100644 index 094d95b..0000000 --- a/java-spring-boot-demo/src/main/java/com/example/demo/DemoApplication.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.example.demo; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@SpringBootApplication -public class DemoApplication { - - public static void main(String[] args) { - SpringApplication.run(DemoApplication.class, args); - } - -} diff --git a/java-spring-boot-demo/src/main/java/com/example/demo/DemoController.java b/java-spring-boot-demo/src/main/java/com/example/demo/DemoController.java deleted file mode 100644 index 61a5075..0000000 --- a/java-spring-boot-demo/src/main/java/com/example/demo/DemoController.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.example.demo; - -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -public class DemoController { - - @RequestMapping("/") - public Object root() { - return null; - } - -} diff --git a/java-spring-boot-demo/src/main/resources/application.properties b/java-spring-boot-demo/src/main/resources/application.properties deleted file mode 100644 index e69de29..0000000 diff --git a/java-spring-boot-demo/src/test/java/com/example/demo/DemoApplicationTests.java b/java-spring-boot-demo/src/test/java/com/example/demo/DemoApplicationTests.java deleted file mode 100644 index eaa9969..0000000 --- a/java-spring-boot-demo/src/test/java/com/example/demo/DemoApplicationTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.example.demo; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class DemoApplicationTests { - - @Test - void contextLoads() { - } - -} diff --git a/security-layer/.dockerignore b/security-layer/.dockerignore new file mode 100644 index 0000000..3977568 --- /dev/null +++ b/security-layer/.dockerignore @@ -0,0 +1,4 @@ +!target/*-runner +!target/*-runner.jar +!target/lib/* +!target/quarkus-app/* diff --git a/security-layer/.env b/security-layer/.env new file mode 100644 index 0000000..72c7541 --- /dev/null +++ b/security-layer/.env @@ -0,0 +1,19 @@ +QUARKUS_KUBERNETES_CLIENT_MASTER_URL= +QUARKUS_KUBERNETES_CLIENT_USERNAME= +QUARKUS_KUBERNETES_CLIENT_CA_CERT_DATA= +QUARKUS_KUBERNETES_CLIENT_CLIENT_CERT_DATA= +QUARKUS_KUBERNETES_CLIENT_CLIENT_KEY_DATA= +QUARKUS_KUBERNETES_IMAGE_PULL_POLICY=ifNotPresent + +QUARKUS_KUBERNETES_CLIENT_NAMESPACE= +QUARKUS_DEPLOY_TARGET= + +QUARKUS_NATIVE_ADDITIONAL_BUILD_ARGS=-march=compatibility + +QUARKUS_CONTAINER_IMAGE_BUILD= +QUARKUS_CONTAINER_IMAGE_GROUP= +QUARKUS_CONTAINER_IMAGE_NAME= +QUARKUS_CONTAINER_IMAGE_TAG= + + +RESPONSE_TOPIC= \ No newline at end of file diff --git a/security-layer/.gitignore b/security-layer/.gitignore new file mode 100644 index 0000000..d90f32c --- /dev/null +++ b/security-layer/.gitignore @@ -0,0 +1,43 @@ +#Maven +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +release.properties +.flattened-pom.xml + +# Eclipse +.project +.classpath +.settings/ +bin/ + +# IntelliJ +.idea +*.ipr +*.iml +*.iws + +# NetBeans +nb-configuration.xml + +# Visual Studio Code +.vscode +.factorypath + +# OSX +.DS_Store + +# Vim +*.swp +*.swo + +# patch +*.orig +*.rej + +# Local environment +.env.ubi + +# Plugin directory +/.quarkus/cli/plugins/ diff --git a/security-layer/.gitlab-ci.yml b/security-layer/.gitlab-ci.yml new file mode 100644 index 0000000..e2bae04 --- /dev/null +++ b/security-layer/.gitlab-ci.yml @@ -0,0 +1,9 @@ +image: maven:3.8.4-openjdk-17 + +stages: + - docker-build + +docker-build-job: + stage: docker-build + script: + - mvn package -DskipTests -Dquarkus.container-image.build=true diff --git a/security-layer/.mvn/wrapper/.gitignore b/security-layer/.mvn/wrapper/.gitignore new file mode 100644 index 0000000..e72f5e8 --- /dev/null +++ b/security-layer/.mvn/wrapper/.gitignore @@ -0,0 +1 @@ +maven-wrapper.jar diff --git a/security-layer/.mvn/wrapper/MavenWrapperDownloader.java b/security-layer/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 0000000..b7eb954 --- /dev/null +++ b/security-layer/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import java.io.IOException; +import java.io.InputStream; +import java.net.Authenticator; +import java.net.PasswordAuthentication; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; + +public final class MavenWrapperDownloader { + private static final String WRAPPER_VERSION = "3.2.0"; + + private static final boolean VERBOSE = Boolean.parseBoolean(System.getenv("MVNW_VERBOSE")); + + public static void main(String[] args) { + log("Apache Maven Wrapper Downloader " + WRAPPER_VERSION); + + if (args.length != 2) { + System.err.println(" - ERROR wrapperUrl or wrapperJarPath parameter missing"); + System.exit(1); + } + + try { + log(" - Downloader started"); + final URL wrapperUrl = new URL(args[0]); + final String jarPath = args[1].replace("..", ""); // Sanitize path + final Path wrapperJarPath = Paths.get(jarPath).toAbsolutePath().normalize(); + downloadFileFromURL(wrapperUrl, wrapperJarPath); + log("Done"); + } catch (IOException e) { + System.err.println("- Error downloading: " + e.getMessage()); + if (VERBOSE) { + e.printStackTrace(); + } + System.exit(1); + } + } + + private static void downloadFileFromURL(URL wrapperUrl, Path wrapperJarPath) + throws IOException { + log(" - Downloading to: " + wrapperJarPath); + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + final String username = System.getenv("MVNW_USERNAME"); + final char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + try (InputStream inStream = wrapperUrl.openStream()) { + Files.copy(inStream, wrapperJarPath, StandardCopyOption.REPLACE_EXISTING); + } + log(" - Downloader complete"); + } + + private static void log(String msg) { + if (VERBOSE) { + System.out.println(msg); + } + } + +} diff --git a/security-layer/.mvn/wrapper/maven-wrapper.properties b/security-layer/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..346d645 --- /dev/null +++ b/security-layer/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar diff --git a/security-layer/Dockerfile b/security-layer/Dockerfile new file mode 100644 index 0000000..6ca1a1d --- /dev/null +++ b/security-layer/Dockerfile @@ -0,0 +1,26 @@ +# Build stage +FROM docker.io/library/maven:3.9.2-eclipse-temurin-17 AS build +COPY src /home/app/src +COPY pom.xml /home/app +RUN mvn -f /home/app/pom.xml clean package -DskipTests + +# Package stage +FROM registry.access.redhat.com/ubi8/openjdk-17:1.18 + +# Set environment variables +ENV LANGUAGE='en_US:en' +ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" +ENV JAVA_APP_JAR="/deployments/quarkus-run.jar" + +# Copy built artifact from the build stage +COPY --from=build /home/app/target/quarkus-app/lib/ /deployments/lib/ +COPY --from=build /home/app/target/quarkus-app/*.jar /deployments/ +COPY --from=build /home/app/target/quarkus-app/app/ /deployments/app/ +COPY --from=build /home/app/target/quarkus-app/quarkus/ /deployments/quarkus/ + +# Expose port and define user +EXPOSE 8080 +USER 185 + +# Set the entrypoint +ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ] diff --git a/security-layer/README.md b/security-layer/README.md new file mode 100644 index 0000000..c82bd34 --- /dev/null +++ b/security-layer/README.md @@ -0,0 +1,25 @@ +# Security Manager + +If not already installed, set up k8s-gatekeeper in your Kubernetes cluster. Follow the instructions provided in the [k8s-gatekeeper repository](https://github.com/npapageorgopoulos12/k8s-gatekeeper). + +### Prerequisites + +**Kubeconfig:** File used by Kubernetes clients + +**Access to Kubernetes Cluster:** Ensure you have access to the Kubernetes cluster and the necessary permissions to interact with the resources your application will manage + +### Configuring Environment Variables + +Based on kubeconfig file, set the following environment variables: + +1. KUBERNETES_CLIENT_MASTER_URL: The URL of the Kubernetes API server. +2. KUBERNETES_CLIENT_USERNAME: The username for Kubernetes cluster authentication +3. KUBERNETES_CLIENT_CA_CERT_DATA: The CA certificate data for the Kubernetes cluster. +4. KUBERNETES_CLIENT_CLIENT_CERT_DATA: The client certificate data for authentication +5. KUBERNETES_CLIENT_CLIENT_KEY_DATA: The client key data for authentication +6. KUBERNETES_CLIENT_NAMESPACE: The default namespace for your Kubernetes operations + + + +### Accessing The Client +The application and DevUI are on _port 8080_ \ No newline at end of file diff --git a/security-layer/docker-compose.yml b/security-layer/docker-compose.yml new file mode 100644 index 0000000..3d53d0e --- /dev/null +++ b/security-layer/docker-compose.yml @@ -0,0 +1,34 @@ +version: '3.8' + +services: +# activemq-artemis: +# image: quay.io/artemiscloud/activemq-artemis-broker:1.0.21 +# environment: +# AMQ_USER: quarkus +# AMQ_PASSWORD: quarkus +# AMQ_EXTRA_ARGS: "--host 0.0.0.0 --http-host 0.0.0.0 --relax-jolokia" +# ports: +# - "8161:8161" # Web Server +# - "61616:61616" # Core,MQTT,AMQP,HORNETQ,STOMP,Openwire +# - "5672:5672" # AMQP +# restart: unless-stopped +# + + + activemq: + image: rmohr/activemq:latest + environment: + ACTIVEMQ_ADMIN_LOGIN: quarkus + ACTIVEMQ_ADMIN_PASSWORD: quarkus + ACTIVEMQ_USER_LOGIN: quarkus + ACTIVEMQ_USER_PASSWORD: quarkus + ports: + - "8161:8161" # Web Console + - "61616:61616" # JMS + - "5672:5672" # AMQP + - "61613:61613" # STOMP + - "1883:1883" # MQTT + - "61614:61614" # WS + restart: unless-stopped + + diff --git a/security-layer/mvnw b/security-layer/mvnw new file mode 100755 index 0000000..8d937f4 --- /dev/null +++ b/security-layer/mvnw @@ -0,0 +1,308 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.2.0 +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "$(uname)" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME + else + JAVA_HOME="/Library/Java/Home"; export JAVA_HOME + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=$(java-config --jre-home) + fi +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --unix "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --unix "$CLASSPATH") +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] && + JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="$(which javac)" + if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=$(which readlink) + if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then + if $darwin ; then + javaHome="$(dirname "\"$javaExecutable\"")" + javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac" + else + javaExecutable="$(readlink -f "\"$javaExecutable\"")" + fi + javaHome="$(dirname "\"$javaExecutable\"")" + javaHome=$(expr "$javaHome" : '\(.*\)/bin') + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=$(cd "$wdir/.." || exit 1; pwd) + fi + # end of workaround + done + printf '%s' "$(cd "$basedir" || exit 1; pwd)" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + # Remove \r in case we run on Windows within Git Bash + # and check out the repository with auto CRLF management + # enabled. Otherwise, we may read lines that are delimited with + # \r\n and produce $'-Xarg\r' rather than -Xarg due to word + # splitting rules. + tr -s '\r\n' ' ' < "$1" + fi +} + +log() { + if [ "$MVNW_VERBOSE" = true ]; then + printf '%s\n' "$1" + fi +} + +BASE_DIR=$(find_maven_basedir "$(dirname "$0")") +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR +log "$MAVEN_PROJECTBASEDIR" + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" +if [ -r "$wrapperJarPath" ]; then + log "Found $wrapperJarPath" +else + log "Couldn't find $wrapperJarPath, downloading it ..." + + if [ -n "$MVNW_REPOURL" ]; then + wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + else + wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + fi + while IFS="=" read -r key value; do + # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) + safeValue=$(echo "$value" | tr -d '\r') + case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;; + esac + done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" + log "Downloading from: $wrapperUrl" + + if $cygwin; then + wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") + fi + + if command -v wget > /dev/null; then + log "Found wget ... using wget" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + log "Found curl ... using curl" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + else + curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + fi + else + log "Falling back to using Java to download" + javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" + javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaSource=$(cygpath --path --windows "$javaSource") + javaClass=$(cygpath --path --windows "$javaClass") + fi + if [ -e "$javaSource" ]; then + if [ ! -e "$javaClass" ]; then + log " - Compiling MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/javac" "$javaSource") + fi + if [ -e "$javaClass" ]; then + log " - Running MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +# If specified, validate the SHA-256 sum of the Maven wrapper jar file +wrapperSha256Sum="" +while IFS="=" read -r key value; do + case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;; + esac +done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" +if [ -n "$wrapperSha256Sum" ]; then + wrapperSha256Result=false + if command -v sha256sum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + elif command -v shasum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." + echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." + exit 1 + fi + if [ $wrapperSha256Result = false ]; then + echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 + echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 + echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 + exit 1 + fi +fi + +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --windows "$CLASSPATH") + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +# shellcheck disable=SC2086 # safe args +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/security-layer/mvnw.cmd b/security-layer/mvnw.cmd new file mode 100644 index 0000000..c4586b5 --- /dev/null +++ b/security-layer/mvnw.cmd @@ -0,0 +1,205 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.2.0 +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %WRAPPER_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file +SET WRAPPER_SHA_256_SUM="" +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B +) +IF NOT %WRAPPER_SHA_256_SUM%=="" ( + powershell -Command "&{"^ + "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ + "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ + " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ + " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ + " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ + " exit 1;"^ + "}"^ + "}" + if ERRORLEVEL 1 goto error +) + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/security-layer/pom.xml b/security-layer/pom.xml new file mode 100644 index 0000000..515ec7a --- /dev/null +++ b/security-layer/pom.xml @@ -0,0 +1,190 @@ + + + 4.0.0 + ubi.nebulous + casbin-models + 1.0-SNAPSHOT + + 3.11.0 + 17 + UTF-8 + UTF-8 + quarkus-bom + io.quarkus.platform + 3.6.1 + true + 3.1.2 + + + + + ${quarkus.platform.group-id} + ${quarkus.platform.artifact-id} + ${quarkus.platform.version} + pom + import + + + ${quarkus.platform.group-id} + quarkus-qpid-jms-bom + ${quarkus.platform.version} + pom + import + + + + + + io.quarkus + quarkus-smallrye-openapi + + + io.quarkus + quarkus-kubernetes-client + + + io.quarkus + quarkus-resteasy-jackson + + + io.quarkus + quarkus-smallrye-health + + + io.quarkus + quarkus-rest-client + + + org.mapstruct + mapstruct + 1.5.5.Final + + + io.quarkus + quarkus-arc + + + io.quarkus + quarkus-resteasy + + + org.projectlombok + lombok + 1.18.30 + + + com.asyncapi + asyncapi-core + 1.0.0-EAP-2 + runtime + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + org.amqphub.quarkus + quarkus-qpid-jms + + + com.fasterxml.jackson.core + jackson-databind + + + com.google.code.gson + gson + + + + + + ${quarkus.platform.group-id} + quarkus-maven-plugin + ${quarkus.platform.version} + true + + + + build + generate-code + generate-code-tests + + + + + + maven-compiler-plugin + ${compiler-plugin.version} + + + -parameters + + + + org.projectlombok + lombok + 1.18.30 + + + org.mapstruct + mapstruct-processor + 1.5.5.Final + + + ${maven.compiler.release} + ${maven.compiler.release} + + + + maven-surefire-plugin + ${surefire-plugin.version} + + + org.jboss.logmanager.LogManager + ${maven.home} + + + + + maven-failsafe-plugin + ${surefire-plugin.version} + + + + integration-test + verify + + + + + + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + + + native + + + native + + + + false + native + + + + diff --git a/security-layer/src/main/docker/Dockerfile.jvm b/security-layer/src/main/docker/Dockerfile.jvm new file mode 100644 index 0000000..571763e --- /dev/null +++ b/security-layer/src/main/docker/Dockerfile.jvm @@ -0,0 +1,97 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode +# +# Before building the container image run: +# +# ./mvnw package +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/casbin-models-jvm . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/casbin-models-jvm +# +# If you want to include the debug port into your docker image +# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005. +# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005 +# when running the container +# +# Then run the container using : +# +# docker run -i --rm -p 8080:8080 quarkus/casbin-models-jvm +# +# This image uses the `run-java.sh` script to run the application. +# This scripts computes the command line to execute your Java application, and +# includes memory/GC tuning. +# You can configure the behavior using the following environment properties: +# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class") +# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options +# in JAVA_OPTS (example: "-Dsome.property=foo") +# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is +# used to calculate a default maximal heap memory based on a containers restriction. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio +# of the container available memory as set here. The default is `50` which means 50% +# of the available memory is used as an upper boundary. You can skip this mechanism by +# setting this value to `0` in which case no `-Xmx` option is added. +# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This +# is used to calculate a default initial heap memory based on the maximum heap memory. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio +# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx` +# is used as the initial heap size. You can skip this mechanism by setting this value +# to `0` in which case no `-Xms` option is added (example: "25") +# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS. +# This is used to calculate the maximum value of the initial heap memory. If used in +# a container without any memory constraints for the container then this option has +# no effect. If there is a memory constraint then `-Xms` is limited to the value set +# here. The default is 4096MB which means the calculated value of `-Xms` never will +# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096") +# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output +# when things are happening. This option, if set to true, will set +# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true"). +# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example: +# true"). +# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787"). +# - CONTAINER_CORE_LIMIT: A calculated core limit as described in +# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2") +# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024"). +# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion. +# (example: "20") +# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking. +# (example: "40") +# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection. +# (example: "4") +# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus +# previous GC times. (example: "90") +# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20") +# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100") +# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should +# contain the necessary JRE command-line options to specify the required GC, which +# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC). +# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080") +# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080") +# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be +# accessed directly. (example: "foo.example.com,bar.example.com") +# +### +FROM registry.access.redhat.com/ubi8/openjdk-17:1.18 + +ENV LANGUAGE='en_US:en' + + +# We make four distinct layers so if there are application changes the library layers can be re-used +COPY --chown=185 target/quarkus-app/lib/ /deployments/lib/ +COPY --chown=185 target/quarkus-app/*.jar /deployments/ +COPY --chown=185 target/quarkus-app/app/ /deployments/app/ +COPY --chown=185 target/quarkus-app/quarkus/ /deployments/quarkus/ + +EXPOSE 8080 +USER 185 +ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" +ENV JAVA_APP_JAR="/deployments/quarkus-run.jar" + +ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ] + diff --git a/security-layer/src/main/docker/Dockerfile.legacy-jar b/security-layer/src/main/docker/Dockerfile.legacy-jar new file mode 100644 index 0000000..bc4c5aa --- /dev/null +++ b/security-layer/src/main/docker/Dockerfile.legacy-jar @@ -0,0 +1,93 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode +# +# Before building the container image run: +# +# ./mvnw package -Dquarkus.package.type=legacy-jar +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.legacy-jar -t quarkus/casbin-models-legacy-jar . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/casbin-models-legacy-jar +# +# If you want to include the debug port into your docker image +# you will have to expose the debug port (default 5005 being the default) like this : EXPOSE 8080 5005. +# Additionally you will have to set -e JAVA_DEBUG=true and -e JAVA_DEBUG_PORT=*:5005 +# when running the container +# +# Then run the container using : +# +# docker run -i --rm -p 8080:8080 quarkus/casbin-models-legacy-jar +# +# This image uses the `run-java.sh` script to run the application. +# This scripts computes the command line to execute your Java application, and +# includes memory/GC tuning. +# You can configure the behavior using the following environment properties: +# - JAVA_OPTS: JVM options passed to the `java` command (example: "-verbose:class") +# - JAVA_OPTS_APPEND: User specified Java options to be appended to generated options +# in JAVA_OPTS (example: "-Dsome.property=foo") +# - JAVA_MAX_MEM_RATIO: Is used when no `-Xmx` option is given in JAVA_OPTS. This is +# used to calculate a default maximal heap memory based on a containers restriction. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xmx` is set to a ratio +# of the container available memory as set here. The default is `50` which means 50% +# of the available memory is used as an upper boundary. You can skip this mechanism by +# setting this value to `0` in which case no `-Xmx` option is added. +# - JAVA_INITIAL_MEM_RATIO: Is used when no `-Xms` option is given in JAVA_OPTS. This +# is used to calculate a default initial heap memory based on the maximum heap memory. +# If used in a container without any memory constraints for the container then this +# option has no effect. If there is a memory constraint then `-Xms` is set to a ratio +# of the `-Xmx` memory as set here. The default is `25` which means 25% of the `-Xmx` +# is used as the initial heap size. You can skip this mechanism by setting this value +# to `0` in which case no `-Xms` option is added (example: "25") +# - JAVA_MAX_INITIAL_MEM: Is used when no `-Xms` option is given in JAVA_OPTS. +# This is used to calculate the maximum value of the initial heap memory. If used in +# a container without any memory constraints for the container then this option has +# no effect. If there is a memory constraint then `-Xms` is limited to the value set +# here. The default is 4096MB which means the calculated value of `-Xms` never will +# be greater than 4096MB. The value of this variable is expressed in MB (example: "4096") +# - JAVA_DIAGNOSTICS: Set this to get some diagnostics information to standard output +# when things are happening. This option, if set to true, will set +# `-XX:+UnlockDiagnosticVMOptions`. Disabled by default (example: "true"). +# - JAVA_DEBUG: If set remote debugging will be switched on. Disabled by default (example: +# true"). +# - JAVA_DEBUG_PORT: Port used for remote debugging. Defaults to 5005 (example: "8787"). +# - CONTAINER_CORE_LIMIT: A calculated core limit as described in +# https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt. (example: "2") +# - CONTAINER_MAX_MEMORY: Memory limit given to the container (example: "1024"). +# - GC_MIN_HEAP_FREE_RATIO: Minimum percentage of heap free after GC to avoid expansion. +# (example: "20") +# - GC_MAX_HEAP_FREE_RATIO: Maximum percentage of heap free after GC to avoid shrinking. +# (example: "40") +# - GC_TIME_RATIO: Specifies the ratio of the time spent outside the garbage collection. +# (example: "4") +# - GC_ADAPTIVE_SIZE_POLICY_WEIGHT: The weighting given to the current GC time versus +# previous GC times. (example: "90") +# - GC_METASPACE_SIZE: The initial metaspace size. (example: "20") +# - GC_MAX_METASPACE_SIZE: The maximum metaspace size. (example: "100") +# - GC_CONTAINER_OPTIONS: Specify Java GC to use. The value of this variable should +# contain the necessary JRE command-line options to specify the required GC, which +# will override the default of `-XX:+UseParallelGC` (example: -XX:+UseG1GC). +# - HTTPS_PROXY: The location of the https proxy. (example: "myuser@127.0.0.1:8080") +# - HTTP_PROXY: The location of the http proxy. (example: "myuser@127.0.0.1:8080") +# - NO_PROXY: A comma separated lists of hosts, IP addresses or domains that can be +# accessed directly. (example: "foo.example.com,bar.example.com") +# +### +FROM registry.access.redhat.com/ubi8/openjdk-17:1.18 + +ENV LANGUAGE='en_US:en' + + +COPY target/lib/* /deployments/lib/ +COPY target/*-runner.jar /deployments/quarkus-run.jar + +EXPOSE 8080 +USER 185 +ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" +ENV JAVA_APP_JAR="/deployments/quarkus-run.jar" + +ENTRYPOINT [ "/opt/jboss/container/java/run/run-java.sh" ] diff --git a/security-layer/src/main/docker/Dockerfile.native b/security-layer/src/main/docker/Dockerfile.native new file mode 100644 index 0000000..47f5d51 --- /dev/null +++ b/security-layer/src/main/docker/Dockerfile.native @@ -0,0 +1,27 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. +# +# Before building the container image run: +# +# ./mvnw package -Dnative +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.native -t quarkus/casbin-models . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/casbin-models +# +### +FROM registry.access.redhat.com/ubi8/ubi-minimal:8.9 +WORKDIR /work/ +RUN chown 1001 /work \ + && chmod "g+rwX" /work \ + && chown 1001:root /work +COPY --chown=1001:root target/*-runner /work/application + +EXPOSE 8080 +USER 1001 + +ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"] diff --git a/security-layer/src/main/docker/Dockerfile.native-micro b/security-layer/src/main/docker/Dockerfile.native-micro new file mode 100644 index 0000000..b343300 --- /dev/null +++ b/security-layer/src/main/docker/Dockerfile.native-micro @@ -0,0 +1,30 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode. +# It uses a micro base image, tuned for Quarkus native executables. +# It reduces the size of the resulting container image. +# Check https://quarkus.io/guides/quarkus-runtime-base-image for further information about this image. +# +# Before building the container image run: +# +# ./mvnw package -Dnative +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.native-micro -t quarkus/casbin-models . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/casbin-models +# +### +FROM quay.io/quarkus/quarkus-micro-image:2.0 +WORKDIR /work/ +RUN chown 1001 /work \ + && chmod "g+rwX" /work \ + && chown 1001:root /work +COPY --chown=1001:root target/*-runner /work/application + +EXPOSE 8080 +USER 1001 + +ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"] diff --git a/security-layer/src/main/java/ubi/nebulous/APIConfiguration.java b/security-layer/src/main/java/ubi/nebulous/APIConfiguration.java new file mode 100644 index 0000000..2481ce6 --- /dev/null +++ b/security-layer/src/main/java/ubi/nebulous/APIConfiguration.java @@ -0,0 +1,25 @@ +package ubi.nebulous; + +import jakarta.ws.rs.core.Application; +import org.eclipse.microprofile.openapi.annotations.OpenAPIDefinition; +import org.eclipse.microprofile.openapi.annotations.info.Contact; +import org.eclipse.microprofile.openapi.annotations.info.Info; +import org.eclipse.microprofile.openapi.annotations.info.License; + +@OpenAPIDefinition( + info = @Info( + title = "Casbin Policy Engine Client", + version = "1.0.0", + description = "API for managing Casbin resource policies in Kubernetes.", + contact = @Contact( + name = "Nikos Papageorgopoulos", + url = "https://ubitech.eu", + email = "npapageorgopoulos@ubitech.eu" + ), + license = @License( + name = "Apache 2.0", + url = "http://www.apache.org/licenses/LICENSE-2.0.html" + ) + ) +) +public class APIConfiguration extends Application {} diff --git a/security-layer/src/main/java/ubi/nebulous/ExampleResource.java b/security-layer/src/main/java/ubi/nebulous/ExampleResource.java new file mode 100644 index 0000000..d4f33d0 --- /dev/null +++ b/security-layer/src/main/java/ubi/nebulous/ExampleResource.java @@ -0,0 +1,18 @@ +package ubi.nebulous; + +import io.vertx.core.cli.annotations.Hidden; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +@Hidden +@Path("/hello") +public class ExampleResource { + + @GET + @Produces(MediaType.TEXT_PLAIN) + public String hello() { + return "Hello RESTEasy"; + } +} diff --git a/security-layer/src/main/java/ubi/nebulous/MyLivenessCheck.java b/security-layer/src/main/java/ubi/nebulous/MyLivenessCheck.java new file mode 100644 index 0000000..a3af103 --- /dev/null +++ b/security-layer/src/main/java/ubi/nebulous/MyLivenessCheck.java @@ -0,0 +1,15 @@ +package ubi.nebulous; + +import org.eclipse.microprofile.health.HealthCheck; +import org.eclipse.microprofile.health.HealthCheckResponse; +import org.eclipse.microprofile.health.Liveness; + +@Liveness +public class MyLivenessCheck implements HealthCheck { + + @Override + public HealthCheckResponse call() { + return HealthCheckResponse.up("alive"); + } + +} \ No newline at end of file diff --git a/security-layer/src/main/java/ubi/nebulous/dto/CasbinModelDTO.java b/security-layer/src/main/java/ubi/nebulous/dto/CasbinModelDTO.java new file mode 100644 index 0000000..27c6492 --- /dev/null +++ b/security-layer/src/main/java/ubi/nebulous/dto/CasbinModelDTO.java @@ -0,0 +1,18 @@ +package ubi.nebulous.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class CasbinModelDTO { + private String name; + private boolean enabled; + private String modelText; + private String namespace; + +} diff --git a/security-layer/src/main/java/ubi/nebulous/dto/CasbinPolicyDTO.java b/security-layer/src/main/java/ubi/nebulous/dto/CasbinPolicyDTO.java new file mode 100644 index 0000000..94d38ee --- /dev/null +++ b/security-layer/src/main/java/ubi/nebulous/dto/CasbinPolicyDTO.java @@ -0,0 +1,17 @@ +package ubi.nebulous.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class CasbinPolicyDTO { + private String name; + private String policyItem; + private String namespace; + +} diff --git a/security-layer/src/main/java/ubi/nebulous/dto/DeploymentDTO.java b/security-layer/src/main/java/ubi/nebulous/dto/DeploymentDTO.java new file mode 100644 index 0000000..f557a4c --- /dev/null +++ b/security-layer/src/main/java/ubi/nebulous/dto/DeploymentDTO.java @@ -0,0 +1,31 @@ +package ubi.nebulous.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.io.Serializable; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class DeploymentDTO implements Serializable { + private String namespace; + private String name; + private String image; + private int replicas; + private int containerPort; + + @Override + public String toString() { + return "DeploymentDTO{" + + "namespace='" + namespace + '\'' + + ", name='" + name + '\'' + + ", image='" + image + '\'' + + ", replicas=" + replicas + + ", containerPort=" + containerPort + + '}'; + } +} diff --git a/security-layer/src/main/java/ubi/nebulous/dto/mapper/CasbinModelMapper.java b/security-layer/src/main/java/ubi/nebulous/dto/mapper/CasbinModelMapper.java new file mode 100644 index 0000000..52c8588 --- /dev/null +++ b/security-layer/src/main/java/ubi/nebulous/dto/mapper/CasbinModelMapper.java @@ -0,0 +1,19 @@ +package ubi.nebulous.dto.mapper; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; +import ubi.nebulous.dto.CasbinModelDTO; +import ubi.nebulous.model.casbin.model.CasbinModel; + +@Mapper(componentModel = "cdi") +public interface CasbinModelMapper { + + CasbinModelMapper INSTANCE = Mappers.getMapper(CasbinModelMapper.class); + + @Mapping(target = "metadata.name", source = "name") + CasbinModel dtoToModel(CasbinModelDTO dto); + + @Mapping(target = "name", source = "metadata.name") + CasbinModelDTO modelToDto(CasbinModel model); +} diff --git a/security-layer/src/main/java/ubi/nebulous/dto/mapper/CasbinPolicyMapper.java b/security-layer/src/main/java/ubi/nebulous/dto/mapper/CasbinPolicyMapper.java new file mode 100644 index 0000000..8676dbf --- /dev/null +++ b/security-layer/src/main/java/ubi/nebulous/dto/mapper/CasbinPolicyMapper.java @@ -0,0 +1,19 @@ +package ubi.nebulous.dto.mapper; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; +import ubi.nebulous.dto.CasbinPolicyDTO; +import ubi.nebulous.model.casbin.policy.CasbinPolicy; + +@Mapper(componentModel = "cdi") +public interface CasbinPolicyMapper { + + CasbinPolicyMapper INSTANCE = Mappers.getMapper(CasbinPolicyMapper.class); + + @Mapping(target = "metadata.name", source = "name") + CasbinPolicy dtoToModel(CasbinPolicyDTO dto); + + @Mapping(target = "name", source = "metadata.name") + CasbinPolicyDTO modelToDto(CasbinPolicy model); +} \ No newline at end of file diff --git a/security-layer/src/main/java/ubi/nebulous/dto/mapper/DeploymentMapper.java b/security-layer/src/main/java/ubi/nebulous/dto/mapper/DeploymentMapper.java new file mode 100644 index 0000000..0aea493 --- /dev/null +++ b/security-layer/src/main/java/ubi/nebulous/dto/mapper/DeploymentMapper.java @@ -0,0 +1,15 @@ +package ubi.nebulous.dto.mapper; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; +import ubi.nebulous.dto.DeploymentDTO; +import ubi.nebulous.model.DeploymentModel; + +@Mapper +public interface DeploymentMapper { + + DeploymentMapper INSTANCE = Mappers.getMapper(DeploymentMapper.class); + + DeploymentDTO modelToDTO(DeploymentModel deploymentModel); + DeploymentModel dtoToModel(DeploymentDTO deploymentDTO); +} diff --git a/security-layer/src/main/java/ubi/nebulous/messaging/consumer/CasbinModelConsumer.java b/security-layer/src/main/java/ubi/nebulous/messaging/consumer/CasbinModelConsumer.java new file mode 100644 index 0000000..ea8ff21 --- /dev/null +++ b/security-layer/src/main/java/ubi/nebulous/messaging/consumer/CasbinModelConsumer.java @@ -0,0 +1,148 @@ +package ubi.nebulous.messaging.consumer; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import io.quarkus.runtime.ShutdownEvent; +import io.quarkus.runtime.StartupEvent; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Observes; +import jakarta.inject.Inject; +import jakarta.jms.*; +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.jboss.logging.Logger; +import ubi.nebulous.dto.CasbinModelDTO; +import ubi.nebulous.dto.CasbinPolicyDTO; +import ubi.nebulous.messaging.util.JsonUtil; +import ubi.nebulous.model.casbin.model.CasbinModel; +import ubi.nebulous.service.CasbinModelService; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +@ApplicationScoped +public class CasbinModelConsumer implements Runnable { + + private static final Logger logger = Logger.getLogger(CasbinModelConsumer.class); + + @Inject + ConnectionFactory connectionFactory; + + @Inject + CasbinModelService casbinModelService; + + @ConfigProperty(name = "CASBIN_MODEL_CREATE_TOPIC") + String casbinModelCreateTopic; + + @ConfigProperty(name = "CASBIN_MODEL_DELETE_TOPIC") + String casbinModelDeleteTopic; + + @ConfigProperty(name = "CASBIN_MODEL_GET_ALL_TOPIC") + String casbinModelGetAllTopic; + + @ConfigProperty(name = "RESPONSE_TOPIC") + String responseTopicName; + + private final ExecutorService scheduler = Executors.newSingleThreadExecutor(); + + void onStart(@Observes StartupEvent ev) { + logger.info("Starting CasbinModel Consumer..."); + scheduler.submit(this); + } + + void onStop(@Observes ShutdownEvent ev) { + logger.info("Stopping CasbinModel Consumer..."); + scheduler.shutdown(); + } + @Override + public void run() { + try (JMSContext context = connectionFactory.createContext(JMSContext.AUTO_ACKNOWLEDGE)) { + var createConsumer = context.createConsumer(context.createTopic(casbinModelCreateTopic)); + var deleteConsumer = context.createConsumer(context.createTopic(casbinModelDeleteTopic)); + var getAllConsumer = context.createConsumer(context.createTopic(casbinModelGetAllTopic)); + + while (!Thread.currentThread().isInterrupted()) { + processMessages(createConsumer, deleteConsumer, getAllConsumer, context); + } + } catch (RuntimeException e) { + logger.errorf(e, "Error in CasbinModel Consumer."); + } + } + private void processMessages(JMSConsumer createConsumer, JMSConsumer deleteConsumer, JMSConsumer getAllConsumer, JMSContext context) { + try { + var createMessage = createConsumer.receiveNoWait(); + if (createMessage != null) { + processCreateOrUpdateCasbinModel(createMessage.getBody(List.class), context); + } + + var deleteMessage = deleteConsumer.receiveNoWait(); + if (deleteMessage != null) { + processDeleteCasbinModel(deleteMessage.getBody(String.class), context); + } + + var getAllMessage = getAllConsumer.receiveNoWait(); + if (getAllMessage != null) { + processListCasbinModels(getAllMessage.getBody(String.class), context); + } + } catch (JMSException e) { + logger.errorf(e, "Error processing messages in CasbinModel Consumer."); + } + } + + private void processCreateOrUpdateCasbinModel(List> messageList, JMSContext context) { + try { + Map modelMap = messageList.get(0); + CasbinModelDTO casbinModelDTO = new CasbinModelDTO(); + casbinModelDTO.setName(modelMap.get("name") != null ? modelMap.get("name") : null); + casbinModelDTO.setModelText(modelMap.get("model") != null ? modelMap.get("model") : null); + casbinModelDTO.setNamespace(modelMap.get("namespace") != null ? modelMap.get("namespace") : null); + + +// CasbinModelDTO casbinModelDTO = JsonUtil.deserialize(jsonMessage, CasbinModelDTO.class); + logger.infof("message received for "+ casbinModelDTO.toString()); + CasbinModel model = casbinModelService.createOrUpdateCasbinModel(casbinModelDTO); + sendResponse(context, "CasbinModel created/updated successfully"); + } catch (Exception e) { + sendResponse(context, "Error creating/updating CasbinModel: " + e.getMessage()); + } + } + + private void processDeleteCasbinModel(String jsonMessage, JMSContext context) { + try { + JsonObject jsonObject = JsonParser.parseString(jsonMessage).getAsJsonObject(); + String name = jsonObject.get("name").getAsString(); + String namespace = jsonObject.has("namespace") ? jsonObject.get("namespace").getAsString() : "default"; + + if (casbinModelService.casbinModelExists(name, namespace)) { + casbinModelService.deleteCasbinModel(name, namespace); + sendResponse(context, String.format("CasbinModel '%s' in namespace '%s' deleted successfully", name, namespace)); + } else { + sendResponse(context, String.format("CasbinModel '%s' in namespace '%s' not found", name, namespace)); + } + } catch (Exception e) { + logger.errorf(e, "Error processing delete CasbinModel message."); + sendResponse(context, "Error deleting CasbinModel: " + e.getMessage()); + } + } + + private void processListCasbinModels(String namespace, JMSContext context) { + try { + List models = casbinModelService.listCasbinModels(namespace != null ? namespace : "default"); + sendResponse(context, JsonUtil.serialize(models)); + logger.infof(JsonUtil.serialize(models)); + } catch (Exception e) { + logger.errorf(e, "Error processing list CasbinModels message."); + sendResponse(context, "Error retrieving CasbinModels: " + e.getMessage()); + } + } + + private void sendResponse(JMSContext context, String message) { + try { + TextMessage responseMessage = context.createTextMessage(message); + context.createProducer().send(context.createTopic(responseTopicName), responseMessage); + } catch (RuntimeException e) { + logger.errorf(e, "Error sending response message."); + } + } +} \ No newline at end of file diff --git a/security-layer/src/main/java/ubi/nebulous/messaging/consumer/CasbinPolicyConsumer.java b/security-layer/src/main/java/ubi/nebulous/messaging/consumer/CasbinPolicyConsumer.java new file mode 100644 index 0000000..91e4a1f --- /dev/null +++ b/security-layer/src/main/java/ubi/nebulous/messaging/consumer/CasbinPolicyConsumer.java @@ -0,0 +1,149 @@ +package ubi.nebulous.messaging.consumer; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import io.quarkus.runtime.ShutdownEvent; +import io.quarkus.runtime.StartupEvent; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Observes; +import jakarta.inject.Inject; +import jakarta.jms.*; +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.jboss.logging.Logger; +import ubi.nebulous.dto.CasbinModelDTO; +import ubi.nebulous.dto.CasbinPolicyDTO; +import ubi.nebulous.messaging.util.JsonUtil; +import ubi.nebulous.model.casbin.policy.CasbinPolicy; +import ubi.nebulous.service.CasbinPolicyService; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +@ApplicationScoped +public class CasbinPolicyConsumer implements Runnable { + + private static final Logger logger = Logger.getLogger(CasbinPolicyConsumer.class); + + @Inject + ConnectionFactory connectionFactory; + + @Inject + CasbinPolicyService casbinPolicyService; + + @ConfigProperty(name = "CASBIN_POLICY_CREATE_TOPIC") + String casbinPolicyCreateTopic; + + @ConfigProperty(name = "CASBIN_POLICY_DELETE_TOPIC") + String casbinPolicyDeleteTopic; + + @ConfigProperty(name = "CASBIN_POLICY_GET_ALL_TOPIC") + String casbinPolicyGetAllTopic; + + @ConfigProperty(name = "RESPONSE_TOPIC") + String responseTopicName; + + private final ExecutorService scheduler = Executors.newSingleThreadExecutor(); + + void onStart(@Observes StartupEvent ev) { + logger.info("Starting CasbinPolicy Consumer..."); + scheduler.submit(this); + } + + void onStop(@Observes ShutdownEvent ev) { + logger.info("Stopping CasbinPolicy Consumer..."); + scheduler.shutdown(); + } + + @Override + public void run() { + try (JMSContext context = connectionFactory.createContext(JMSContext.AUTO_ACKNOWLEDGE)) { + var createConsumer = context.createConsumer(context.createTopic(casbinPolicyCreateTopic)); + var deleteConsumer = context.createConsumer(context.createTopic(casbinPolicyDeleteTopic)); + var getAllConsumer = context.createConsumer(context.createTopic(casbinPolicyGetAllTopic)); + + while (!Thread.currentThread().isInterrupted()) { + processMessages(createConsumer, deleteConsumer, getAllConsumer, context); + } + } catch (RuntimeException e) { + logger.errorf(e, "Error in CasbinPolicy Consumer."); + } + } + + private void processMessages(JMSConsumer createConsumer, JMSConsumer deleteConsumer, JMSConsumer getAllConsumer, JMSContext context) { + try { + var createMessage = createConsumer.receiveNoWait(); + if (createMessage != null) { +// processCreateOrUpdateCasbinPolicy(createMessage.getBody(String.class), context); + processCreateOrUpdateCasbinPolicy(createMessage.getBody(List.class), context); + } + + var deleteMessage = deleteConsumer.receiveNoWait(); + if (deleteMessage != null) { + processDeleteCasbinPolicy(deleteMessage.getBody(String.class), context); + } + + var getAllMessage = getAllConsumer.receiveNoWait(); + if (getAllMessage != null) { + processListCasbinPolicies(getAllMessage.getBody(String.class), context); + } + } catch (JMSException e) { + logger.errorf(e, "Error processing messages in CasbinPolicy Consumer."); + } + } + + private void processCreateOrUpdateCasbinPolicy(List> messageList, JMSContext context) { + try { + Map policyMap = messageList.get(0); + CasbinPolicyDTO casbinPolicyDTO = new CasbinPolicyDTO(); + casbinPolicyDTO.setName(policyMap.get("name") != null ? policyMap.get("name") : null); + casbinPolicyDTO.setPolicyItem(policyMap.get("policyItem") != null ? policyMap.get("policyItem") : null); + casbinPolicyDTO.setNamespace(policyMap.get("namespace") != null ? policyMap.get("namespace") : null); + +// CasbinPolicyDTO dto = JsonUtil.deserialize(jsonMessage, CasbinPolicyDTO.class); + logger.infof("message received for "+ casbinPolicyDTO.toString()); + CasbinPolicy policy = casbinPolicyService.createOrUpdateCasbinPolicy(casbinPolicyDTO); + sendResponse(context, "CasbinPolicy created/updated successfully"); + } catch (Exception e) { + sendResponse(context, "Error creating/updating CasbinPolicy: " + e.getMessage()); + } + } + + private void processDeleteCasbinPolicy(String jsonMessage, JMSContext context) { + try { + JsonObject jsonObject = JsonParser.parseString(jsonMessage).getAsJsonObject(); + String name = jsonObject.get("name").getAsString(); + String namespace = jsonObject.has("namespace") ? jsonObject.get("namespace").getAsString() : "default"; + + if (casbinPolicyService.casbinPolicyExists(name, namespace)) { + casbinPolicyService.deleteCasbinPolicy(name, namespace); + sendResponse(context, String.format("CasbinPolicy '%s' deleted successfully", name)); + } else { + sendResponse(context, String.format("CasbinPolicy '%s' not found", name)); + logger.infof("CasbinPolicy '%s' not found", name); + } + } catch (Exception e) { + sendResponse(context, "Error deleting CasbinPolicy: " + e.getMessage()); + } + } + + private void processListCasbinPolicies(String namespace, JMSContext context) { + try { + List policies = casbinPolicyService.listCasbinPolicies(namespace); + sendResponse(context, JsonUtil.serialize(policies)); + logger.infof("Listed CasbinPolicies for namespace: %s", namespace); + } catch (Exception e) { + sendResponse(context, "Error listing CasbinPolicies: " + e.getMessage()); + } + } + + private void sendResponse(JMSContext context, String message) { + try { + TextMessage responseMessage = context.createTextMessage(message); + context.createProducer().send(context.createTopic(responseTopicName), responseMessage); + } catch (RuntimeException e) { + logger.errorf(e, "Error sending response message."); + } + } +} \ No newline at end of file diff --git a/security-layer/src/main/java/ubi/nebulous/messaging/consumer/DeploymentConsumer.java b/security-layer/src/main/java/ubi/nebulous/messaging/consumer/DeploymentConsumer.java new file mode 100644 index 0000000..9ba687e --- /dev/null +++ b/security-layer/src/main/java/ubi/nebulous/messaging/consumer/DeploymentConsumer.java @@ -0,0 +1,145 @@ +package ubi.nebulous.messaging.consumer; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.quarkus.runtime.ShutdownEvent; +import io.quarkus.runtime.StartupEvent; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Observes; +import jakarta.inject.Inject; +import jakarta.jms.*; +import org.eclipse.microprofile.config.inject.ConfigProperty; + +import org.jboss.logging.Logger; +import ubi.nebulous.messaging.util.JsonUtil; +import ubi.nebulous.dto.DeploymentDTO; +import ubi.nebulous.service.DeploymentService; + +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +@ApplicationScoped +public class DeploymentConsumer implements Runnable { + + private static final Logger logger = Logger.getLogger(DeploymentConsumer.class); + + @Inject + ConnectionFactory connectionFactory; + + @Inject + DeploymentService k8sDeploymentService; + + @ConfigProperty(name = "DEPLOYMENT_CREATE_TOPIC") + String deploymentCreateTopic; + + @ConfigProperty(name = "DEPLOYMENT_DELETE_TOPIC") + String deploymentDeleteTopic; + + @ConfigProperty(name = "DEPLOYMENT_GET_ALL_TOPIC") + String deploymentGetAllTopic; + + @ConfigProperty(name = "RESPONSE_TOPIC") + String responseTopicName; + + private final ExecutorService scheduler = Executors.newSingleThreadExecutor(); + + void onStart(@Observes StartupEvent ev) { + logger.info("Starting Deployment Consumer..."); + scheduler.submit(this); + } + + void onStop(@Observes ShutdownEvent ev) { + logger.info("Stopping Deployment Consumer..."); + scheduler.shutdown(); + } + + @Override + public void run() { + try (JMSContext context = connectionFactory.createContext(JMSContext.AUTO_ACKNOWLEDGE)) { + var createConsumer = context.createConsumer(context.createTopic(deploymentCreateTopic)); + var deleteConsumer = context.createConsumer(context.createTopic(deploymentDeleteTopic)); + var getAllConsumer = context.createConsumer(context.createTopic(deploymentGetAllTopic)); + + while (!Thread.currentThread().isInterrupted()) { + processMessages(createConsumer, deleteConsumer, getAllConsumer, context); + } + } + } + + private void processMessages(JMSConsumer createConsumer, JMSConsumer deleteConsumer, JMSConsumer getAllConsumer, JMSContext context) { + var createMessage = createConsumer.receiveNoWait(); + if (createMessage != null) { + try { + processCreateDeployment(createMessage.getBody(String.class), context); + } catch (JMSException e) { + throw new RuntimeException(e); + } + } + + var deleteMessage = deleteConsumer.receiveNoWait(); + if (deleteMessage != null) { + try { + processDeleteDeployment(deleteMessage.getBody(String.class), context); + } catch (JMSException e) { + throw new RuntimeException(e); + } + } + + var getAllMessage = getAllConsumer.receiveNoWait(); + if (getAllMessage != null) { + try { + processGetAllDeployments(getAllMessage.getBody(String.class), context); + } catch (JMSException e) { + throw new RuntimeException(e); + } + } + } + + private void processCreateDeployment(String jsonMessage, JMSContext context) { + try { + DeploymentDTO deploymentDTO = JsonUtil.deserialize(jsonMessage, DeploymentDTO.class); + logger.infof("Received Create Deployment Message: %s", deploymentDTO); + k8sDeploymentService.createDeployment(deploymentDTO); + sendResponse(context, "Deployment created successfully"); + } catch (Exception e) { + sendResponse(context, "Error creating deployment: " + e.getMessage()); + } + } + + private void processDeleteDeployment(String jsonMessage, JMSContext context) { + try { + JsonObject jsonObject = JsonParser.parseString(jsonMessage).getAsJsonObject(); + String name = jsonObject.get("name").getAsString(); + String namespace = jsonObject.has("namespace") ? jsonObject.get("namespace").getAsString() : "default"; + + if (k8sDeploymentService.deploymentExists(name, namespace)) { + k8sDeploymentService.deleteDeployment(name, namespace); + sendResponse(context, String.format("Deployment '%s' in namespace '%s' deleted successfully", name, namespace)); + } else { + logger.infof("Deployment '%s' in namespace '%s' not found", name, namespace); + sendResponse(context, String.format("Deployment '%s' in namespace '%s' not found", name, namespace)); + } + } catch (Exception e) { + sendResponse(context, "Error deleting deployment: " + e.getMessage()); + } + } + + private void processGetAllDeployments(String namespace, JMSContext context) { + try { + List deployments = k8sDeploymentService.getDeployments(namespace != null ? namespace : "default"); + logger.infof("Received Get All Deployments Message for Namespace: %s", namespace); + // send back list of deployments + logger.infof(JsonUtil.serialize(deployments)); + sendResponse(context, JsonUtil.serialize(deployments)); + } catch (Exception e) { + sendResponse(context, "Error retrieving deployments: " + e.getMessage()); + } + } + + private void sendResponse(JMSContext context, String message) { + TextMessage responseMessage = context.createTextMessage(message); + context.createProducer().send(context.createTopic(responseTopicName), responseMessage); + } +} diff --git a/security-layer/src/main/java/ubi/nebulous/messaging/util/JsonUtil.java b/security-layer/src/main/java/ubi/nebulous/messaging/util/JsonUtil.java new file mode 100644 index 0000000..91a5176 --- /dev/null +++ b/security-layer/src/main/java/ubi/nebulous/messaging/util/JsonUtil.java @@ -0,0 +1,27 @@ +package ubi.nebulous.messaging.util; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.IOException; + +public class JsonUtil { + private static final ObjectMapper objectMapper = new ObjectMapper(); + + public static T deserialize(String json, Class clazz) { + try { + return objectMapper.readValue(json, clazz); + } catch (IOException e) { + throw new RuntimeException("Error deserializing JSON", e); + } + } + + public static String serialize(Object obj) { + try { + return objectMapper.writeValueAsString(obj); + } catch (JsonProcessingException e) { + throw new RuntimeException("Error serializing object to JSON", e); + } + } +} + diff --git a/security-layer/src/main/java/ubi/nebulous/model/DeploymentModel.java b/security-layer/src/main/java/ubi/nebulous/model/DeploymentModel.java new file mode 100644 index 0000000..f5f8dfa --- /dev/null +++ b/security-layer/src/main/java/ubi/nebulous/model/DeploymentModel.java @@ -0,0 +1,24 @@ +package ubi.nebulous.model; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class DeploymentModel { + private String namespace; + private String name; + private String image; + private int replicas; + private int containerPort; + + + // if there is no namespace use default + public String getNamespace() { + return namespace != null ? namespace : "default"; + } +} diff --git a/security-layer/src/main/java/ubi/nebulous/model/casbin/model/CasbinModel.java b/security-layer/src/main/java/ubi/nebulous/model/casbin/model/CasbinModel.java new file mode 100644 index 0000000..e6b76c0 --- /dev/null +++ b/security-layer/src/main/java/ubi/nebulous/model/casbin/model/CasbinModel.java @@ -0,0 +1,26 @@ +package ubi.nebulous.model.casbin.model; + +import io.fabric8.kubernetes.api.model.Namespaced; +import io.fabric8.kubernetes.client.CustomResource; +import io.fabric8.kubernetes.model.annotation.Group; +import io.fabric8.kubernetes.model.annotation.Version; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Group("auth.casbin.org") +@Version("v1") +public class CasbinModel extends CustomResource implements Namespaced { + + private String namespace; + + @Override + public String toString() { + return "CasbinModel{" + + "spec=" + spec + + ", status=" + status + + '}'; + } +} + diff --git a/security-layer/src/main/java/ubi/nebulous/model/casbin/model/CasbinModelSpec.java b/security-layer/src/main/java/ubi/nebulous/model/casbin/model/CasbinModelSpec.java new file mode 100644 index 0000000..2a8e864 --- /dev/null +++ b/security-layer/src/main/java/ubi/nebulous/model/casbin/model/CasbinModelSpec.java @@ -0,0 +1,12 @@ +package ubi.nebulous.model.casbin.model; + +import io.fabric8.kubernetes.api.model.KubernetesResource; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class CasbinModelSpec implements KubernetesResource { + private boolean enabled; + private String modelText; +} diff --git a/security-layer/src/main/java/ubi/nebulous/model/casbin/model/CasbinModelStatus.java b/security-layer/src/main/java/ubi/nebulous/model/casbin/model/CasbinModelStatus.java new file mode 100644 index 0000000..2eb90a2 --- /dev/null +++ b/security-layer/src/main/java/ubi/nebulous/model/casbin/model/CasbinModelStatus.java @@ -0,0 +1,6 @@ +package ubi.nebulous.model.casbin.model; + +import io.fabric8.kubernetes.api.model.KubernetesResource; + +public class CasbinModelStatus implements KubernetesResource { +} diff --git a/security-layer/src/main/java/ubi/nebulous/model/casbin/policy/CasbinPolicy.java b/security-layer/src/main/java/ubi/nebulous/model/casbin/policy/CasbinPolicy.java new file mode 100644 index 0000000..e19229c --- /dev/null +++ b/security-layer/src/main/java/ubi/nebulous/model/casbin/policy/CasbinPolicy.java @@ -0,0 +1,28 @@ +package ubi.nebulous.model.casbin.policy; + +import io.fabric8.kubernetes.api.model.Namespaced; +import io.fabric8.kubernetes.client.CustomResource; +import io.fabric8.kubernetes.model.annotation.Group; +import io.fabric8.kubernetes.model.annotation.Version; +import lombok.Getter; +import lombok.Setter; + +import javax.naming.Name; + +@Getter +@Setter +@Group("auth.casbin.org") +@Version("v1") +public class CasbinPolicy extends CustomResource implements Namespaced { + + private String namespace; + + @Override + public String toString() { + return "CasbinPolicy{" + + "metadata=" + getMetadata() + + ", spec=" + spec + + ", status=" + status + + '}'; + } +} diff --git a/security-layer/src/main/java/ubi/nebulous/model/casbin/policy/CasbinPolicySpec.java b/security-layer/src/main/java/ubi/nebulous/model/casbin/policy/CasbinPolicySpec.java new file mode 100644 index 0000000..ae41a3e --- /dev/null +++ b/security-layer/src/main/java/ubi/nebulous/model/casbin/policy/CasbinPolicySpec.java @@ -0,0 +1,11 @@ +package ubi.nebulous.model.casbin.policy; + +import io.fabric8.kubernetes.api.model.KubernetesResource; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class CasbinPolicySpec implements KubernetesResource { + private String policyItem; +} diff --git a/security-layer/src/main/java/ubi/nebulous/model/casbin/policy/CasbinPolicyStatus.java b/security-layer/src/main/java/ubi/nebulous/model/casbin/policy/CasbinPolicyStatus.java new file mode 100644 index 0000000..00136f6 --- /dev/null +++ b/security-layer/src/main/java/ubi/nebulous/model/casbin/policy/CasbinPolicyStatus.java @@ -0,0 +1,9 @@ +package ubi.nebulous.model.casbin.policy; + +import io.fabric8.kubernetes.api.model.KubernetesResource; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class CasbinPolicyStatus implements KubernetesResource {} diff --git a/security-layer/src/main/java/ubi/nebulous/rest/CasbinModelResource.java b/security-layer/src/main/java/ubi/nebulous/rest/CasbinModelResource.java new file mode 100644 index 0000000..dace71f --- /dev/null +++ b/security-layer/src/main/java/ubi/nebulous/rest/CasbinModelResource.java @@ -0,0 +1,106 @@ +package ubi.nebulous.rest; + +import jakarta.inject.Inject; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import org.eclipse.microprofile.openapi.annotations.Operation; +import org.eclipse.microprofile.openapi.annotations.media.Content; +import org.eclipse.microprofile.openapi.annotations.media.ExampleObject; +import org.eclipse.microprofile.openapi.annotations.media.Schema; +import org.eclipse.microprofile.openapi.annotations.parameters.Parameter; +import org.eclipse.microprofile.openapi.annotations.parameters.RequestBody; +import ubi.nebulous.dto.CasbinModelDTO; +import ubi.nebulous.dto.mapper.CasbinModelMapper; +import ubi.nebulous.model.DeploymentModel; +import ubi.nebulous.model.casbin.model.CasbinModel; +import ubi.nebulous.service.CasbinModelService; + +import java.util.List; + +@Path("/casbin-model") +public class CasbinModelResource { + + @Inject + CasbinModelService casbinModelService; + + /** + * Creates a CasbinModel based on the provided DTO + * + * @param casbinModelDTO The CasbinModel containing the relevant policy model + * @return The created CasbinModel resource as object + */ + @POST + @Operation(summary = "Create a CasbinModel", description = "Creates a CasbinModel based on the provided Policy Model.") + @RequestBody(required = true, content = @Content( + mediaType = MediaType.APPLICATION_JSON, + schema = @Schema(implementation = CasbinModelDTO.class), + examples = @ExampleObject(name = "example", + value = "{\n" + + " \"name\": \"allowed-repo\",\n" + + " \"enabled\": true,\n" + + " \"modelText\": \"[request_definition]\\nr = obj\\n\\n[policy_definition]\\np = obj,eft\\n\\n[policy_effect]\\ne = !some(where (p.eft == deny))\\n\\n[matchers]\\nm = ${NAMESPACE} == \\\"default\\\" && ${RESOURCE} ==\\\"deployments\\\" && access(${OBJECT}.Spec.Template.Spec.Containers , 0, \\\"Image\\\") == p.obj\"\n" + + "}") + )) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response createOrUpdateCasbinModel(CasbinModelDTO casbinModelDTO) { + try { + CasbinModel createdModel = casbinModelService.createOrUpdateCasbinModel(casbinModelDTO); + return Response.status(Response.Status.CREATED).entity(createdModel).build(); + } catch (Exception e) { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("Error creating/updating CasbinModel: " + e.getMessage()) + .build(); + } + } + + /** + * lists crd of CasbinModel in the specified namespace. + * + * @param namespace from which to retrieve CasbinModel + * @return A list of CasbinModel + */ + @GET + @Path("/{namespace}") + @Produces(MediaType.APPLICATION_JSON) + @Operation(summary = "Retrieves CasbinModel CRDs", description = "Retrieves a list of CasbinModel CRDs.") + public Response listCasbinModels(@PathParam("namespace") @DefaultValue("default") String namespace) { + try { + List models = casbinModelService.listCasbinModels(namespace); + return Response.ok(models).build(); + } catch (Exception e) { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("Error listing CasbinModels: " + e.getMessage()) + .build(); + } + } + + + /** + * Deletes CasbinModel with the given name + * + * @param casbinModelName The name of the CasbinModel to be deleted + * @return response indicating the outcome of the delete operation + */ + @DELETE + @Path("/{casbinModelName}") + @Parameter(name = "casbinmodelname", description = "The name of the CasbinModel to be deleted", required = true) + @Operation(summary = "Deletes a Kubernetes CasbinModel CRD", description = "Deletes a CasbinModel CRD based on the provided casbinmodelname.") + @Produces(MediaType.TEXT_PLAIN) + public Response deleteCasbinModel(@PathParam("casbinModelName") String casbinModelName, @QueryParam("namespace") @DefaultValue("default") String namespace) { + try { + if (!casbinModelService.casbinModelExists(casbinModelName, namespace)) { + return Response.status(Response.Status.NOT_FOUND) + .entity("CasbinModel not found") + .build(); + } + casbinModelService.deleteCasbinModel(casbinModelName, namespace); + return Response.ok("CasbinModel deleted").build(); + } catch (Exception e) { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("Error deleting CasbinModel: " + e.getMessage()) + .build(); + } + } +} \ No newline at end of file diff --git a/security-layer/src/main/java/ubi/nebulous/rest/CasbinPolicyResource.java b/security-layer/src/main/java/ubi/nebulous/rest/CasbinPolicyResource.java new file mode 100644 index 0000000..586fa05 --- /dev/null +++ b/security-layer/src/main/java/ubi/nebulous/rest/CasbinPolicyResource.java @@ -0,0 +1,105 @@ +package ubi.nebulous.rest; + +import jakarta.inject.Inject; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import org.eclipse.microprofile.openapi.annotations.Operation; +import org.eclipse.microprofile.openapi.annotations.media.Content; +import org.eclipse.microprofile.openapi.annotations.media.ExampleObject; +import org.eclipse.microprofile.openapi.annotations.media.Schema; +import org.eclipse.microprofile.openapi.annotations.parameters.RequestBody; +import ubi.nebulous.dto.CasbinModelDTO; +import ubi.nebulous.dto.CasbinPolicyDTO; +import ubi.nebulous.dto.mapper.CasbinPolicyMapper; +import ubi.nebulous.model.casbin.policy.CasbinPolicy; +import ubi.nebulous.service.CasbinPolicyService; + +import java.util.List; + +@Path("/casbin-policy") +public class CasbinPolicyResource { + + @Inject + CasbinPolicyService casbinPolicyService; + + /** + * Creates a CasbinPolicy based on the provided DTO + * + * @param casbinPolicyDTO containing the relevant policy + * @return The created CasbinPolicy resource as object + */ + @POST + @Operation(summary = "Create or Update a CasbinPolicy", description = "Creates or updates a CasbinPolicy based on the provided Policy DTO.") + @RequestBody(required = true, content = @Content( + mediaType = MediaType.APPLICATION_JSON, + schema = @Schema(implementation = CasbinPolicy.class), + examples = @ExampleObject(name = "example", + value = "{\n" + + " \"name\": \"allowed-repo\",\n" + + " \"policyItem\": \"p, \\\"nginx:1.13.1\\\",allow\\np, \\\"nginx:1.14.1\\\",deny\"\n" + + " }") + )) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response createOrUpdateCasbinPolicy(CasbinPolicyDTO casbinPolicyDTO) { + try { + CasbinPolicy createdPolicy = casbinPolicyService.createOrUpdateCasbinPolicy(casbinPolicyDTO); + return Response.status(Response.Status.CREATED).entity(createdPolicy).build(); + } catch (Exception e) { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("Error creating/updating CasbinPolicy: " + e.getMessage()) + .build(); + } + } + + + + /** + * lists crd of CasbinPolicy in the specified namespace. + * + * @param namespace from which to retrieve CasbinPolicy + * @return A list of CasbinPolicy + */ + @GET + @Path("/{namespace}") + @Operation(summary = "List CasbinPolicies", description = "Lists all CasbinPolicies in the specified namespace.") + @Produces(MediaType.APPLICATION_JSON) + public Response listCasbinPolicies(@PathParam("namespace") String namespace) { + try { + List policies = casbinPolicyService.listCasbinPolicies(namespace); + return Response.ok(policies).build(); + } catch (Exception e) { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("Error listing CasbinPolicies: " + e.getMessage()) + .build(); + } + } + + /** + * Deletes CasbinPolicy with the given name + * + * @param casbinPolicyName The name of the CasbinPolicy to be deleted + * @return response indicating the outcome of the delete operation + */ + @DELETE + @Path("/{casbinPolicyName}") + @Operation(summary = "Delete a CasbinPolicy", description = "Deletes a CasbinPolicy with the specified name from the given namespace.") + @Produces(MediaType.TEXT_PLAIN) + public Response deleteCasbinPolicy(@PathParam("casbinPolicyName") String casbinPolicyName, @QueryParam("namespace") @DefaultValue("default") String namespace) { + try { + if (!casbinPolicyService.casbinPolicyExists(casbinPolicyName, namespace)) { + return Response.status(Response.Status.NOT_FOUND) + .entity("CasbinPolicy not found") + .build(); + } + casbinPolicyService.deleteCasbinPolicy(casbinPolicyName, namespace); + return Response.ok("CasbinPolicy deleted").build(); + } catch (Exception e) { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("Error deleting CasbinPolicy: " + e.getMessage()) + .build(); + } + } +} + diff --git a/security-layer/src/main/java/ubi/nebulous/rest/DeploymentResource.java b/security-layer/src/main/java/ubi/nebulous/rest/DeploymentResource.java new file mode 100644 index 0000000..a08dd4a --- /dev/null +++ b/security-layer/src/main/java/ubi/nebulous/rest/DeploymentResource.java @@ -0,0 +1,116 @@ +package ubi.nebulous.rest; + +import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.fabric8.kubernetes.client.KubernetesClientException; +import jakarta.inject.Inject; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import org.eclipse.microprofile.openapi.annotations.Operation; +import org.eclipse.microprofile.openapi.annotations.media.Content; +import org.eclipse.microprofile.openapi.annotations.media.ExampleObject; +import org.eclipse.microprofile.openapi.annotations.media.Schema; +import org.eclipse.microprofile.openapi.annotations.parameters.Parameter; +import org.eclipse.microprofile.openapi.annotations.parameters.RequestBody; +import org.jboss.logging.Logger; +import ubi.nebulous.dto.DeploymentDTO; +import ubi.nebulous.model.DeploymentModel; +import ubi.nebulous.service.DeploymentService; + +import java.util.List; + +@Path("/deployment") +public class DeploymentResource { + private static final Logger LOG = Logger.getLogger(DeploymentResource.class); + + @Inject + DeploymentService kubernetesDeploymentService; + + + /** + * lists deployments in the specified namespace. + * + * @param namespace from which to retrieve deployments + * @return A list of Deployments + */ + @GET + @Path("/{namespace}") + @Produces(MediaType.APPLICATION_JSON) + @Operation(summary = "Retrieves K8s Deployments", description = "Retrieves a list of Kubernetes deployments.") + public Response getDeployments(@PathParam("namespace") @DefaultValue("default") String namespace) { + try { + List deployments = kubernetesDeploymentService.getDeployments(namespace); + return Response.ok(deployments).build(); + } catch (Exception e) { + LOG.error(e.getMessage()); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("Error retrieving deployments: " + e.getMessage()) + .build(); + } + } + + /** + * Creates a Kubernetes Deployment based on the provided DeploymentDTO + * + * @param deploymentDTO containing the deployment specifications + * @return The created Deployment resource as object + */ + @POST + @Operation(summary = "Create a Kubernetes Deployment", description = "Creates a Kubernetes Deployment based on the provided DeploymentDTO.") + @RequestBody(required = true, content = @Content( + mediaType = MediaType.APPLICATION_JSON, + schema = @Schema(implementation = DeploymentModel.class), + examples = @ExampleObject(name = "example", + value = "{\"name\": \"nginx-deployment\", \"image\": \"nginx:1.13.1\", \"replicas\": 1, \"containerPort\": 80}") + )) + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response createDeployment(DeploymentDTO deploymentDTO) { + try { + Deployment deployment = kubernetesDeploymentService.createDeployment(deploymentDTO); + return Response.status(Response.Status.CREATED) + .entity(deployment) + .build(); + } catch (Exception e) { + LOG.error(e.getMessage()); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("Error creating deployment: " + e.getMessage()) + .build(); + } + } + + + /** + * Deletes a Kubernetes Deployment with the given name + * + * @param deploymentName The name of the deployment to be deleted + * @return response indicating the outcome of the delete operation + */ + @DELETE + @Parameter(name = "deploymentname", description = "The name of the deployment to be deleted", required = true) + @Operation(summary = "Deletes a Kubernetes Deployment", description = "Deletes a Kubernetes Deployment based on the provided deploymentname.") + @Path("/{deploymentname}") + @Produces(MediaType.TEXT_PLAIN) + public Response deleteDeployment(@PathParam("deploymentname") String deploymentName, + @QueryParam("namespace") @DefaultValue("default") String namespace) { + try { + // check if exists + boolean exists = kubernetesDeploymentService.deploymentExists(deploymentName, namespace); + if (!exists) { + return Response.status(Response.Status.NOT_FOUND) + .entity(String.format("Deployment '%s' not found in namespace '%s'.", deploymentName, namespace)) + .build(); + } + // delete + kubernetesDeploymentService.deleteDeployment(deploymentName, namespace); + return Response.ok() + .entity(String.format("Deployment '%s' deleted successfully from namespace '%s'.", deploymentName, namespace)) + .build(); + } catch (KubernetesClientException e) { + LOG.error(e.getMessage()); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity(String.format("Error deleting deployment '%s' from namespace '%s': %s", deploymentName, namespace, e.getMessage())) + .build(); + } + } +} diff --git a/security-layer/src/main/java/ubi/nebulous/service/CasbinModelService.java b/security-layer/src/main/java/ubi/nebulous/service/CasbinModelService.java new file mode 100644 index 0000000..563b9d8 --- /dev/null +++ b/security-layer/src/main/java/ubi/nebulous/service/CasbinModelService.java @@ -0,0 +1,95 @@ +package ubi.nebulous.service; + +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.KubernetesClientException; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.jboss.logging.Logger; +import ubi.nebulous.dto.CasbinModelDTO; +import ubi.nebulous.model.casbin.model.CasbinModel; +import ubi.nebulous.model.casbin.model.CasbinModelSpec; +import ubi.nebulous.model.casbin.model.CasbinModelStatus; + +import java.util.List; + +@ApplicationScoped +public class CasbinModelService { + + private static final Logger logger = Logger.getLogger(CasbinModelService.class); + + @Inject + KubernetesClient kubernetesClient; + + public CasbinModel createOrUpdateCasbinModel(CasbinModelDTO dto) { + try { + String effectiveNamespace = (dto.getNamespace() != null && !dto.getNamespace().isEmpty()) ? dto.getNamespace() : "default"; + + CasbinModel casbinModel = new CasbinModel(); + casbinModel.setMetadata(new ObjectMetaBuilder() + .withName(dto.getName()) + .build()); + CasbinModelSpec spec = new CasbinModelSpec(); + spec.setEnabled(dto.isEnabled()); + spec.setModelText(dto.getModelText()); + + casbinModel.setSpec(spec); + casbinModel.setStatus(new CasbinModelStatus()); + + kubernetesClient.resources(CasbinModel.class) + .inNamespace(effectiveNamespace) + .createOrReplace(casbinModel); + + logger.infof("CasbinModel created/updated successfully: %s", casbinModel); + + return casbinModel; + }catch (KubernetesClientException e){ + logger.errorf(e, "Error processing create/update CasbinModel message."); + return null; + } + } + + + public List listCasbinModels(String namespace) { + try { + List casbinModelLists = kubernetesClient.resources(CasbinModel.class) + .inNamespace(namespace) + .list() + .getItems(); + logger.infof("Received List CasbinModels Message for Namespace: %s", namespace); + return casbinModelLists; + } catch (KubernetesClientException e) { + throw new KubernetesClientException("Error listing CasbinModels: " + e.getMessage(), e); + } + } + + + public void deleteCasbinModel(String name, String namespace) { + String effectiveNamespace = (namespace != null && !namespace.isEmpty()) ? namespace : "default"; + try { + kubernetesClient.resources(CasbinModel.class) + .inNamespace(effectiveNamespace) + .withName(name) + .delete(); + logger.infof("CasbinModel '%s' in namespace '%s' deleted successfully", name, namespace); + } catch (KubernetesClientException e) { + logger.errorf(e, "Error processing deleting CasbinModel."); + throw new RuntimeException("Error deleting CasbinModel: " + e.getMessage(), e); + } + } + + + + + public boolean casbinModelExists(String name, String namespace) { + String effectiveNamespace = (namespace != null && !namespace.isEmpty()) ? namespace : "default"; + try { + return kubernetesClient.resources(CasbinModel.class) + .inNamespace(effectiveNamespace) + .withName(name) + .get() != null; + } catch (KubernetesClientException e) { + throw new KubernetesClientException("Error checking CasbinModel existence: " + e.getMessage(), e); + } + } +} diff --git a/security-layer/src/main/java/ubi/nebulous/service/CasbinPolicyService.java b/security-layer/src/main/java/ubi/nebulous/service/CasbinPolicyService.java new file mode 100644 index 0000000..47d44c7 --- /dev/null +++ b/security-layer/src/main/java/ubi/nebulous/service/CasbinPolicyService.java @@ -0,0 +1,85 @@ +package ubi.nebulous.service; + +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.KubernetesClientException; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.jboss.logging.Logger; +import ubi.nebulous.dto.CasbinPolicyDTO; +import ubi.nebulous.model.casbin.policy.CasbinPolicy; +import ubi.nebulous.model.casbin.policy.CasbinPolicySpec; + +import java.util.List; + +@ApplicationScoped +public class CasbinPolicyService { + + private static final Logger logger = Logger.getLogger(CasbinPolicyService.class); + + @Inject + KubernetesClient kubernetesClient; + + public CasbinPolicy createOrUpdateCasbinPolicy(CasbinPolicyDTO dto) { + String effectiveNamespace = (dto.getNamespace() != null && !dto.getNamespace().isEmpty()) ? dto.getNamespace() : "default"; + try { + CasbinPolicy casbinPolicy = new CasbinPolicy(); + casbinPolicy.setMetadata(new ObjectMetaBuilder().withName(dto.getName()).build()); + CasbinPolicySpec spec = new CasbinPolicySpec(); + spec.setPolicyItem(dto.getPolicyItem()); + casbinPolicy.setSpec(spec); + + kubernetesClient.resources(CasbinPolicy.class) + .inNamespace(effectiveNamespace) + .createOrReplace(casbinPolicy); + logger.infof("CasbinPolicy created/updated successfully: %s", casbinPolicy); + return casbinPolicy; + } catch (KubernetesClientException e) { + logger.errorf(e, "Error creating/updating CasbinPolicy."); + throw new RuntimeException("Error creating/updating CasbinPolicy: " + e.getMessage(), e); + } + } + + + + public List listCasbinPolicies(String namespace) { + try { + List casbinPolicyList = kubernetesClient.resources(CasbinPolicy.class) + .inNamespace(namespace) + .list() + .getItems(); + logger.infof("Listed CasbinPolicies for namespace: %s", namespace); + return casbinPolicyList; + } catch (KubernetesClientException e) { + logger.errorf(e, "Error listing CasbinPolicies."); + throw new RuntimeException("Error listing CasbinPolicies: " + e.getMessage(), e); + } + } + + public void deleteCasbinPolicy(String name, String namespace) { + String effectiveNamespace = (namespace != null && !namespace.isEmpty()) ? namespace : "default"; + try { + kubernetesClient.resources(CasbinPolicy.class) + .inNamespace(effectiveNamespace) + .withName(name) + .delete(); + logger.infof("CasbinPolicy '%s' deleted successfully", name); + } catch (KubernetesClientException e) { + logger.errorf(e, "Error deleting CasbinPolicy."); + throw new RuntimeException("Error deleting CasbinPolicy: " + e.getMessage(), e); + } + } + + + public boolean casbinPolicyExists(String name, String namespace) { + String effectiveNamespace = (namespace != null && !namespace.isEmpty()) ? namespace : "default"; + try { + return kubernetesClient.resources(CasbinPolicy.class) + .inNamespace(effectiveNamespace) + .withName(name) + .get() != null; + } catch (KubernetesClientException e) { + throw new RuntimeException("Error checking CasbinPolicy existence: " + e.getMessage(), e); + } + } +} \ No newline at end of file diff --git a/security-layer/src/main/java/ubi/nebulous/service/DeploymentService.java b/security-layer/src/main/java/ubi/nebulous/service/DeploymentService.java new file mode 100644 index 0000000..36b5bf0 --- /dev/null +++ b/security-layer/src/main/java/ubi/nebulous/service/DeploymentService.java @@ -0,0 +1,92 @@ +package ubi.nebulous.service; + +import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.KubernetesClientException; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.jboss.logging.Logger; +import ubi.nebulous.dto.DeploymentDTO; +import ubi.nebulous.dto.mapper.DeploymentMapper; +import ubi.nebulous.messaging.consumer.DeploymentConsumer; +import ubi.nebulous.model.DeploymentModel; + +import java.util.List; + +@ApplicationScoped +public class DeploymentService { + + private static final Logger logger = Logger.getLogger(DeploymentService.class); + + @Inject + KubernetesClient kubernetesClient; + + public Deployment createDeployment(DeploymentDTO deploymentDTO){ + DeploymentModel deploymentModel = DeploymentMapper.INSTANCE.dtoToModel(deploymentDTO); + + Deployment deployment = new DeploymentBuilder() + .withNewMetadata() + .withName(deploymentDTO.getName()) + .endMetadata() + .withNewSpec() + .withReplicas(deploymentDTO.getReplicas()) + .withNewSelector() + .addToMatchLabels("app", deploymentDTO.getName()) + .endSelector() + .withNewTemplate() + .withNewMetadata() + .addToLabels("app", deploymentDTO.getName()) + .endMetadata() + .withNewSpec() + .addNewContainer() + .withName(deploymentDTO.getName()) + .withImage(deploymentDTO.getImage()) + .addNewPort() + .withContainerPort(deploymentDTO.getContainerPort()) + .endPort() + .endContainer() + .endSpec() + .endTemplate() + .endSpec() + .build(); + try { + deployment = kubernetesClient.apps().deployments() + .inNamespace(deploymentModel.getNamespace()) + .createOrReplace(deployment); + logger.infof("Deployment created successfully"); + } catch (KubernetesClientException e) { + logger.errorf(e, "Error processing Create Deployment message."); + e.printStackTrace(); + } + return deployment; + } + + public void deleteDeployment(String deploymentName, String namespace) { + try { + String effectiveNamespace = (namespace != null && !namespace.isEmpty()) ? namespace : "default"; + kubernetesClient.apps().deployments().inNamespace(effectiveNamespace) + .withName(deploymentName).delete(); + logger.infof("Deployment '%s' in namespace '%s' deleted successfully", deploymentName, namespace); + + }catch (RuntimeException e){ + logger.errorf(e, "Error processing Delete Deployment message."); + } + + } + + public boolean deploymentExists(String deploymentName, String namespace) { + String effectiveNamespace = (namespace != null && !namespace.isEmpty()) ? namespace : "default"; + return kubernetesClient.apps().deployments().inNamespace(effectiveNamespace) + .withName(deploymentName).get() != null; + } + + public List getDeployments(String namespace) { + try { + return kubernetesClient.apps().deployments().inNamespace(namespace).list().getItems(); + }catch (RuntimeException e){ + logger.errorf(e, "Error processing Get All Deployments message."); + return null; + } + } +} diff --git a/security-layer/src/main/resources/META-INF/resources/index.html b/security-layer/src/main/resources/META-INF/resources/index.html new file mode 100644 index 0000000..a517f56 --- /dev/null +++ b/security-layer/src/main/resources/META-INF/resources/index.html @@ -0,0 +1,378 @@ + + + + + casbin-models - 1.0-SNAPSHOT + + + +
+
+
+ + + + + quarkus_logo_horizontal_rgb_1280px_reverse + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+
+

You just made a Quarkus application.

+

This page is served by Quarkus.

+ Visit the Dev UI +

This page: src/main/resources/META-INF/resources/index.html

+

App configuration: src/main/resources/application.properties

+

Static assets: src/main/resources/META-INF/resources/

+

Code: src/main/java

+

Generated starter code:

+
    +
  • + RESTEasy JAX-RS Easily start your RESTful Web Services +
    @Path: /hello +
    Related guide +
  • + +
+
+
+

Selected extensions

+
    +
  • Keycloak Admin Client
  • +
  • SmallRye OpenAPI (guide) +
  • +
  • Kubernetes Client (guide) +
  • +
  • OpenID + Connect (guide) +
  • +
  • + Hibernate ORM with Panache (guide) +
  • +
  • RESTEasy Classic Jackson
  • +
  • Kubernetes Config (guide) +
  • +
  • SmallRye Health (guide) +
  • +
  • Kubernetes (guide) +
  • +
  • JDBC Driver - PostgreSQL (guide) +
  • +
+
Documentation
+

Practical step-by-step guides to help you achieve a specific goal. Use them to help get your work + done.

+
Set up your IDE
+

Everyone has a favorite IDE they like to use to code. Learn how to configure yours to maximize your + Quarkus productivity.

+
+
+
+ + diff --git a/security-layer/src/main/resources/application.properties b/security-layer/src/main/resources/application.properties new file mode 100644 index 0000000..1aea80b --- /dev/null +++ b/security-layer/src/main/resources/application.properties @@ -0,0 +1,33 @@ +quarkus.banner.path=banner.txt + +# Kubernetes Client Configuration +# The actual values are expected to be provided via environment variables +quarkus.kubernetes-client.master-url=${KUBERNETES_CLIENT_MASTER_URL:https://default-url:6443} +quarkus.kubernetes-client.username=${KUBERNETES_CLIENT_USERNAME:default-user} +quarkus.kubernetes-client.ca-cert-data=${KUBERNETES_CLIENT_CA_CERT_DATA:default-ca-cert} +quarkus.kubernetes-client.client-cert-data=${KUBERNETES_CLIENT_CLIENT_CERT_DATA:default-cert} +quarkus.kubernetes-client.client-key-data=${KUBERNETES_CLIENT_CLIENT_KEY_DATA:default-key} + +quarkus.kubernetes.image-pull-policy=ifNotPresent +quarkus.kubernetes-client.namespace=${KUBERNETES_CLIENT_NAMESPACE:default} +quarkus.deploy.target=kubernetes + +# Additional build arguments +quarkus.native.additional-build-args=-march=compatibility + +# Configures the Qpid JMS properties. +quarkus.qpid-jms.url=amqp://localhost:5672 +quarkus.qpid-jms.username=admin +quarkus.qpid-jms.password=admin + + +# Topic Names +CASBIN_MODEL_CREATE_TOPIC=eu.nebulouscloud.ui.policies.model.upsert +CASBIN_MODEL_DELETE_TOPIC=eu.nebulousclous.ui.policies.model.delete +CASBIN_MODEL_GET_ALL_TOPIC=eu.nebulousclous.ui.policies.model.read +CASBIN_POLICY_CREATE_TOPIC=eu.nebulouscloud.ui.policies.rule.upsert +CASBIN_POLICY_DELETE_TOPIC=eu.nebulouscloud.ui.policies.rule.delete +CASBIN_POLICY_GET_ALL_TOPIC=eu.nebulouscloud.ui.policies.rule.read +DEPLOYMENT_CREATE_TOPIC=eu.nebulousclous.ui.policies.deployment.create +DEPLOYMENT_DELETE_TOPIC=eu.nebulousclous.ui.policies.deployment.delete +DEPLOYMENT_GET_ALL_TOPIC=eu.nebulousclous.ui.policies.deployment.read \ No newline at end of file diff --git a/security-layer/src/main/resources/banner.txt b/security-layer/src/main/resources/banner.txt new file mode 100644 index 0000000..088463c --- /dev/null +++ b/security-layer/src/main/resources/banner.txt @@ -0,0 +1,5 @@ + _ _ ___ ____ + _ __ ___| |__ _ _| |/ _ \ _ _/ ___| + | '_ \ / _ \ '_ \| | | | | | | | | | \___ \ + | | | | __/ |_) | |_| | | |_| | |_| |___) | + |_| |_|\___|_.__/ \__,_|_|\___/ \__,_|____/ diff --git a/security-layer/src/main/resources/ca.crt b/security-layer/src/main/resources/ca.crt new file mode 100644 index 0000000..b1e4baf --- /dev/null +++ b/security-layer/src/main/resources/ca.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIC/jCCAeagAwIBAgIBADANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwprdWJl +cm5ldGVzMB4XDTIzMTEyODEwNDI0NloXDTMzMTEyNTEwNDI0NlowFTETMBEGA1UE +AxMKa3ViZXJuZXRlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKPL +sywxKS97a6JaJrFamhDq4WDRW9Av2ykESI6IhcAHijhN7I3N5kTgi/t6LLGE4qt1 +3PQVYnBljCJhu4cmbr9oo2lEC9hKshyhw6pTlazi1tHCST3G2QL6MYSTonQ6Aq6o +Q7YymbdnJcRWGEeVUZXw6uF9GKVeW5rueAXmrPkoW0S7/hq2GF1M2UqlmRVWDI87 +NdA3cPHYa5OvM8UB4nBpGkW0zb/2PjrEJ4HwtHFFhzu3BLi7twG9U7dKtKqL4dB0 +yeVRqDLPwfQABf4S2mOC8Z3OKumdqbOt+hp3n1rR40vvX3BBMaaeJG0gODOw6iyl +zYpYIYPY1OpUq3hvJgcCAwEAAaNZMFcwDgYDVR0PAQH/BAQDAgKkMA8GA1UdEwEB +/wQFMAMBAf8wHQYDVR0OBBYEFP363To2BYU4DCKoY3AodvEFVQkTMBUGA1UdEQQO +MAyCCmt1YmVybmV0ZXMwDQYJKoZIhvcNAQELBQADggEBAHdnLnFJiHe+jq7DPYGP +sJ9tsiapZLT4jzt+QMik/hi0UNLne9QT7gDPp9wVLpsfwnc1grmACjlxd/iFRGu9 +y2sWZKo4L/JhMoO6I5QDWKWlBsj+QN0aobx8KX5EgU9QUpdZWZwAqNErkAJEWOwf +aiB44Bp/nIRSKkUADmPVA7cToD7CFkSa8Q43bq4YZe9KVR9EYCn3zsGT5FaJ3YzI +FpUjd+ocbFhSmWi0Ut0qLcnufHIHNgRSL+tNm9FSKLnjLquSgtlZwMA0NpMb4mPE +1q5jWbGySBciTzjkSkDMfmnyNrblJgkRTc0fii0fHinOnNVD099dnvqAadg2+HJN +Bw0= +-----END CERTIFICATE----- diff --git a/security-layer/src/main/resources/import.sql b/security-layer/src/main/resources/import.sql new file mode 100644 index 0000000..16aa523 --- /dev/null +++ b/security-layer/src/main/resources/import.sql @@ -0,0 +1,6 @@ +-- This file allow to write SQL commands that will be emitted in test and dev. +-- The commands are commented as their support depends of the database +-- insert into myentity (id, field) values(1, 'field-1'); +-- insert into myentity (id, field) values(2, 'field-2'); +-- insert into myentity (id, field) values(3, 'field-3'); +-- alter sequence myentity_seq restart with 4; \ No newline at end of file diff --git a/security-layer/src/test/java/ubi/nebulous/ExampleResourceIT.java b/security-layer/src/test/java/ubi/nebulous/ExampleResourceIT.java new file mode 100644 index 0000000..b9acfa7 --- /dev/null +++ b/security-layer/src/test/java/ubi/nebulous/ExampleResourceIT.java @@ -0,0 +1,8 @@ +package ubi.nebulous; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +class ExampleResourceIT extends ExampleResourceTest { + // Execute the same tests but in packaged mode. +} diff --git a/security-layer/src/test/java/ubi/nebulous/ExampleResourceTest.java b/security-layer/src/test/java/ubi/nebulous/ExampleResourceTest.java new file mode 100644 index 0000000..68b2881 --- /dev/null +++ b/security-layer/src/test/java/ubi/nebulous/ExampleResourceTest.java @@ -0,0 +1,20 @@ +package ubi.nebulous; + +import io.quarkus.test.junit.QuarkusTest; +import org.junit.jupiter.api.Test; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; + +@QuarkusTest +class ExampleResourceTest { + @Test + void testHelloEndpoint() { + given() + .when().get("/hello") + .then() + .statusCode(200) + .body(is("Hello RESTEasy")); + } + +} \ No newline at end of file diff --git a/zuul.d/jobs.yaml b/zuul.d/jobs.yaml index 8594451..2a85806 100644 --- a/zuul.d/jobs.yaml +++ b/zuul.d/jobs.yaml @@ -8,15 +8,15 @@ - nebulous-security-manager-container-images description: Build the container images. files: &image_files - - ^java-spring-boot-demo/ + - ^security-layer/ vars: &image_vars promote_container_image_job: nebulous-security-manager-upload-container-images container_images: - - context: java-spring-boot-demo + - context: security-layer registry: quay.io - repository: quay.io/nebulous/security-manager-java-spring-boot-demo + repository: quay.io/nebulous/security-manager-security-layer namespace: nebulous - repo_shortname: security-manager-java-spring-boot-demo + repo_shortname: security-manager-security-layer repo_description: "" - job: @@ -44,7 +44,7 @@ description: Run Hadolint on Dockerfile(s). vars: dockerfiles: - - java-spring-boot-demo/Dockerfile + - security-layer/Dockerfile - job: name: nebulous-security-manager-helm-lint