diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f53cebc --- /dev/null +++ b/Dockerfile @@ -0,0 +1,27 @@ +# docker build -t $DOCKER_BUILD_NAME \ +# --build-arg="MVN_FINALNAME=$MVN_FINALNAME" \ +# --build-arg="MVN_NAME=${MVN_NAME}" \ +# --build-arg="CONTAINER_INI=${CONTAINER_INI}" \ +# --build-arg="JAVA_VERSION=${JAVA_VERSION}" \ +# --build-arg="SMARTGEARS_VERSION=${SMARTGEARS_VERSION}" \ +# -f Dockerfile \ +# $PLATFORMS . + +ARG JAVA_VERSION +ARG SMARTGEARS_VERSION + +#FROM d4science/smartgears-distribution:${SMARTGEARS_VERSION}-java${JAVA_VERSION}-tomcat10.1.19 +FROM hub.dev.d4science.org/gcube/smartgears-distribution:${SMARTGEARS_VERSION}-java${JAVA_VERSION}-tomcat10.1.19 + ARG CONTAINER_INI="./dockerize/configuration/container.ini" + ARG MVN_FINALNAME + ARG MVN_NAME + + #FROM smartgears-distribution:${SMARTGEARS_VERSION}-java${JAVA_VERSION}-tomcat10.1.19 + + COPY ./dockerize/configuration/logback.xml /etc/ + COPY ./dockerize/configuration/*.gcubekey /tomcat/lib + COPY ./target/${MVN_FINALNAME}.war /tmp + RUN unzip /tmp/${MVN_FINALNAME}.war -d /tomcat/webapps/${MVN_NAME} + COPY ${CONTAINER_INI} /etc/container.ini + + EXPOSE 8080 diff --git a/dockerize/buildImageAndStart.sh b/dockerize/buildImageAndStart.sh new file mode 100755 index 0000000..3a06d1d --- /dev/null +++ b/dockerize/buildImageAndStart.sh @@ -0,0 +1,180 @@ +#!/bin/bash + +# set -x # uncomment to debug script +set -a +source ./build_conf +set +a + +ACCEPTED_JAVA_VERSIONs=(11 17) +PORT=8080 +DEBUG_PORT=5005 +DEBUG=false +EXECUTE=false +TEST=false +COMPILE=true + +GOAL="clean package" + +PUSH_DOCKER=false +PUSH_HARBOR=false +LOGIN_HARBOR=false + +################################################################################ +# Help # +################################################################################ +Help() { + # Display Help + echo "build, create and run in docker the identity manager service" + echo + echo "Syntax: buildDistribution " + echo "options:" + + echo "-e execute the docker image" + echo "-g arg specifies the maven [g]oals {package, install, deploy etc} default is $GOAL." + echo "-d arg? enable java debug mode for execution" + + echo "-r push image to d4science harbo[r] (with login already done, or -l to login)" + echo "-u p[u]sh image to dockerhub (with docker login already done)" + + echo "-p arg specifies the port to be exposed for the docker container to access the service (default $PORT)" + + echo "-c arg path of the file to deploy as container.ini (default ./docker/container.ini)" + + echo "-j arg specify java version (default is $JAVA_VERSION)" + echo " accepted version are: ${ACCEPTED_JAVA_VERSIONs[@]}" + echo " arg is the debug port (default is $DEBUG_PORT)" + + echo "-s skip maven package" + echo "-t exec also maven tests" + + echo "-n arg specifies the docker image name (default is $MVN_NAME)." + + echo "-l [l]ogin to d4science harbor" + echo "-h Print this Help." + echo + echo "to compile and push to harbor registry with a custom container.ini file: " + echo " ./buildImageAndStart.sh -r -m -l -c \"./docker/container-XXX.ini\" " + echo + echo "to debug locally: " + echo " ./buildImageAndStart.sh -d " +} + +################################################################################ +################################################################################ +# Main program # +################################################################################ +################################################################################ + +set -e + +#OPTSTRING=":sn:p:d:j:?h" +OPTSTRING=":c:n:p:g:d:?jsmulrteh" + +while getopts $OPTSTRING opt; do + # echo "Option -${opt} was triggered, Argument: ${OPTARG}" + case "${opt}" in + s) COMPILE=false && echo "compile $COMPILE" ;; + g) GOAL=${OPTARG} ;; + c) + CONTAINER_INI=${OPTARG} + echo "CONTAINER_INI: $CONTAINER_INI" + ;; + m) MULTI_PLATFORM=true ;; + n) NAME=${OPTARG} ;; + p) PORT=${OPTARG} ;; + + u) PUSH_DOCKER=true ;; + l) LOGIN_HARBOR=true ;; + r) PUSH_HARBOR=true ;; + + t) TEST=true ;; + e) EXECUTE=true ;; + d) + DEBUG=true + DEBUG_PORT=${OPTARG} + echo "debug enabled, port $DEBUG_PORT, execute $EXECUTE" + ;; + j) + if [[ ${ACCEPTED_JAVA_VERSIONs[@]} =~ ${OPTARG} ]]; then + JAVA_VERSION=${OPTARG} + else + echo "Invalid java version" && echo "accepted version are: ${ACCEPTED_JAVA_VERSIONs[@]}" && exit 1 + fi + ;; + h) Help && exit 0 ;; + + :) + # matched when an option that is expected to have an argument is passed without one + if [ ${OPTARG} = "d" ]; then + DEBUG=true + EXECUTE=true + echo "debug enabled, port $DEBUG_PORT" + else + # matched when an option that is expected to have an argument is passed without one + echo "Option -${OPTARG} requires an argument." + exit 1 + fi + ;; + ?) # match any invalid option that is passed + echo "Invalid option: -${OPTARG}." + exit 1 + ;; + esac +done + +if [ $COMPILE = true ]; then + SKIP_TEST="" + if [ $TEST = false ]; then + SKIP_TEST="-Dmaven.test.skip" + fi + + ( cd .. && mvn $GOAL $SKIP_TEST ); +else + echo "skipping mvn package" +fi + +echo "SMARTGEAR IMAGE: $SMARTGEAR_IMAGE" +docker pull $SMARTGEAR_IMAGE + +if [ $MULTI_PLATFORM ]; then + PLATFORMS="--platform=linux/amd64,linux/arm64,linux/arm/v7" + echo "build multiple platform $PLATFORMS" +fi + +echo "DOCKER_BUILD_NAME: $DOCKER_BUILD_NAME" + +( cd .. && docker build -t $DOCKER_BUILD_NAME \ + --build-arg="MVN_FINALNAME=$MVN_FINALNAME" \ + --build-arg="MVN_NAME=${MVN_NAME}" \ + --build-arg="CONTAINER_INI=${CONTAINER_INI}" \ + --build-arg="JAVA_VERSION=${JAVA_VERSION}" \ + --build-arg="SMARTGEARS_VERSION=${SMARTGEARS_VERSION}" \ + -f Dockerfile \ + $PLATFORMS . ); + +if [ ${PUSH_DOCKER} = true ]; then + docker tag $DOCKER_BUILD_NAME $DOCKER_IMAGE_NAME + docker push $DOCKER_IMAGE_NAME + echo ">>> pushed on dockerhub the image $DOCKER_IMAGE_NAME" +fi + +if [ ${LOGIN_HARBOR} = true ]; then + ./loginHarborHub.sh +fi + +if [ $PUSH_HARBOR = true ]; then + echo ">>> PUSHING on hub.dev.d4science.org the image $HARBOR_IMAGE_NAME" + + docker tag $DOCKER_BUILD_NAME $HARBOR_IMAGE_NAME + echo ">>> docker push $HARBOR_IMAGE_NAME" + docker push $HARBOR_IMAGE_NAME + echo ">>> pushed on hub.dev.d4science.org the image $HARBOR_IMAGE_NAME" +fi + +if [ ${EXECUTE} = true ]; then + if [ $DEBUG = true ]; then + docker run -p $PORT:8080 -p $DEBUG_PORT:5005 -e JAVA_TOOL_OPTIONS="-agentlib:jdwp=transport=dt_socket,address=*:5005,server=y,suspend=y" $DOCKER_BUILD_NAME + else + docker run -p $PORT:8080 $DOCKER_BUILD_NAME + fi +fi diff --git a/dockerize/build_conf b/dockerize/build_conf new file mode 100644 index 0000000..5d16972 --- /dev/null +++ b/dockerize/build_conf @@ -0,0 +1,55 @@ +MVN_VERSION=$(cd .. && mvn -q \ + -Dexec.executable=echo \ + -Dexec.args='${project.version}' \ + --non-recursive \ + exec:exec) +echo "MVN_VERSION: ${MVN_VERSION}" + +MVN_NAME=$(cd .. && mvn -q \ + -Dexec.executable=echo \ + -Dexec.args='${project.artifactId}' \ + --non-recursive \ + exec:exec) +echo "MVN_NAME: ${MVN_NAME}" + +MVN_FINALNAME=$(cd .. && mvn -q \ + -Dexec.executable=echo \ + -Dexec.args='${project.build.finalName}' \ + --non-recursive \ + exec:exec) +echo "MVN_FINALNAME: ${MVN_FINALNAME}" + +JAVA_VERSION=$(cd .. && mvn -q \ + -Dexec.executable=echo \ + -Dexec.args='${maven.compiler.target}' \ + --non-recursive \ + exec:exec) +echo "JAVA_VERSION: ${JAVA_VERSION}" + +TOMCAT_VERSION=tomcat10.1.19 +echo "TOMCAT_VERSION: ${TOMCAT_VERSION}" + +SMARTGEARS_VERSION=$(cd .. && mvn -q \ + -Dexec.executable=echo \ + -Dexec.args='${docker.smartgear.version}' \ + --non-recursive \ + exec:exec) +echo "SMARTGEARS_VERSION: ${SMARTGEARS_VERSION}" + +CONTAINER_INI="./dockerize/configuration/container.ini" +echo "CONTAINER_INI: ${CONTAINER_INI}" + +IMAGE_VERSION=${MVN_VERSION}-java${JAVA_VERSION}-smartgears${SMARTGEARS_VERSION} +echo "IMAGE_VERSION: ${IMAGE_VERSION}" + +DOCKER_BUILD_NAME=$MVN_NAME:$IMAGE_VERSION +echo "DOCKER_BUILD_NAME: ${DOCKER_BUILD_NAME}" + +SMARTGEAR_IMAGE=hub.dev.d4science.org/gcube/smartgears-distribution:${SMARTGEARS_VERSION}-java${JAVA_VERSION}-${TOMCAT_VERSION} +echo "SMARTGEAR_IMAGE: ${SMARTGEAR_IMAGE}" + +DOCKER_IMAGE_NAME=d4science/$DOCKER_BUILD_NAME +echo "DOCKER_IMAGE_NAME: ${DOCKER_IMAGE_NAME}" + +HARBOR_IMAGE_NAME=hub.dev.d4science.org/gcube/$DOCKER_BUILD_NAME +echo "HARBOR_IMAGE_NAME: ${HARBOR_IMAGE_NAME}" diff --git a/dockerize/configuration/.gitignore b/dockerize/configuration/.gitignore new file mode 100644 index 0000000..600791e --- /dev/null +++ b/dockerize/configuration/.gitignore @@ -0,0 +1,3 @@ +container*.ini +!container.default.ini +*.gcubekey \ No newline at end of file diff --git a/dockerize/configuration/container.default.ini b/dockerize/configuration/container.default.ini new file mode 100644 index 0000000..38e0373 --- /dev/null +++ b/dockerize/configuration/container.default.ini @@ -0,0 +1,26 @@ +[node] +mode = offline +hostname = localhost +protocol= http +port = 8080 +infrastructure = gcube +authorizeChildrenContext = true +publicationFrequencyInSeconds = 60 + +[properties] +SmartGearsDistribution = 4.0.1-SNAPSHOT +SmartGearsDistributionBundle = UnBundled + +[site] +country = it +location = pisa + +[authorization] +factory = org.gcube.smartgears.security.defaults.DefaultAuthorizationProviderFactory +factory.endpoint = https://accounts.dev.d4science.org/auth/realms/d4science/protocol/openid-connect/token +credentials.class = org.gcube.smartgears.security.SimpleCredentials + +credentials.clientID = +credentials.secret = + + diff --git a/dockerize/configuration/logback.xml b/dockerize/configuration/logback.xml new file mode 100644 index 0000000..4583a72 --- /dev/null +++ b/dockerize/configuration/logback.xml @@ -0,0 +1,27 @@ + + + + Ï %-5level %logger{36} - %msg%n + + + + + + + + + + + + + \ No newline at end of file diff --git a/dockerize/docker.conf b/dockerize/docker.conf new file mode 100644 index 0000000..a96b5e7 --- /dev/null +++ b/dockerize/docker.conf @@ -0,0 +1,12 @@ +MVN_VERSION: 1.0.0-SNAPSHOT +MVN_NAME: hello-world-service +MVN_FINALNAME: hello-world-service-1.0.0-SNAPSHOT +JAVA_VERSION: 11 +TOMCAT_VERSION: tomcat10.1.19 +SMARTGEARS_VERSION: 4.0.1-SNAPSHOT +CONTAINER_INI: ./dockerize/configuration/container.ini +IMAGE_VERSION: 1.0.0-SNAPSHOT-java11-smartgears4.0.1-SNAPSHOT +DOCKER_BUILD_NAME: hello-world-service:1.0.0-SNAPSHOT-java11-smartgears4.0.1-SNAPSHOT +SMARTGEAR_IMAGE: hub.dev.d4science.org/gcube/smartgears-distribution:4.0.1-SNAPSHOT-java11-tomcat10.1.19 +DOCKER_IMAGE_NAME: d4science/hello-world-service:1.0.0-SNAPSHOT-java11-smartgears4.0.1-SNAPSHOT +HARBOR_IMAGE_NAME: hub.dev.d4science.org/gcube/hello-world-service:1.0.0-SNAPSHOT-java11-smartgears4.0.1-SNAPSHOT diff --git a/dockerize/loginHarborHub.sh b/dockerize/loginHarborHub.sh new file mode 100755 index 0000000..48a84cd --- /dev/null +++ b/dockerize/loginHarborHub.sh @@ -0,0 +1,13 @@ +#!/bin/bash +REGISTRY_URL="hub.dev.d4science.org" +#USERNAME="alfredo.oliviero" +echo "to obtain Harbor username and CLI secret:" +echo "https://hub.dev.d4science.org/ -> user profile -> CLI secret" + +read -p "username:" USERNAME + +echo "" + +read -s -p "CLI secret:" ACCESS_TOKEN +echo "$ACCESS_TOKEN" | docker login $REGISTRY_URL -u $USERNAME --password-stdin +unset ACCESS_TOKEN diff --git a/dockerize/pull_docker.sh b/dockerize/pull_docker.sh new file mode 100755 index 0000000..05c16b2 --- /dev/null +++ b/dockerize/pull_docker.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +set -a +# source build_conf > docker.conf # generate current conf +# source docker.conf +source build_conf + +docker pull $HARBOR_IMAGE_NAME + +echo "Docker image $HARBOR_IMAGE_NAME" + +set +a diff --git a/dockerize/start_docker.sh b/dockerize/start_docker.sh new file mode 100755 index 0000000..2235079 --- /dev/null +++ b/dockerize/start_docker.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +set -a +# source build_conf > docker.conf # generate current conf +# source docker.conf +source build_conf + +echo "Docker image $HARBOR_IMAGE_NAME" +docker pull $HARBOR_IMAGE_NAME +docker run -d -p 9991:8080 --name $NAME $HARBOR_IMAGE_NAME + +set +a diff --git a/dockerize/stop_docker.sh b/dockerize/stop_docker.sh new file mode 100755 index 0000000..1d74417 --- /dev/null +++ b/dockerize/stop_docker.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +set -a +# source build_conf > docker.conf # generate current conf +# source docker.conf +source build_conf + +docker stop $NAME +docker rm $NAME + +set +a diff --git a/documentation/.DS_Store b/documentation/.DS_Store new file mode 100644 index 0000000..9a04531 Binary files /dev/null and b/documentation/.DS_Store differ diff --git a/documentation/dockerizing.md b/documentation/dockerizing.md new file mode 100644 index 0000000..85ca509 --- /dev/null +++ b/documentation/dockerizing.md @@ -0,0 +1,5 @@ + +[visual studio code - Dev Containers tutorial](https://code.visualstudio.com/docs/devcontainers/tutorial) + +installare su vscode estensione [Dev Containers extension](vscode:extension/ms-vscode-remote.remote-containers) + \ No newline at end of file diff --git a/documentation/enunciate.xml b/documentation/enunciate.xml new file mode 100644 index 0000000..aae5fa6 --- /dev/null +++ b/documentation/enunciate.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/documentation/postman/Hello World Env for devVRE.postman_environment.json b/documentation/postman/Hello World Env for devVRE.postman_environment.json new file mode 100644 index 0000000..02bbf3e --- /dev/null +++ b/documentation/postman/Hello World Env for devVRE.postman_environment.json @@ -0,0 +1,75 @@ +{ + "id": "ff7036fd-e12d-4dd7-9d0f-816bb5a0f06a", + "name": "Hello World Env for devVRE", + "values": [ + { + "key": "introspect_secret", + "value": "", + "type": "secret", + "enabled": true + }, + { + "key": "password", + "value": "", + "type": "secret", + "enabled": true + }, +{ + "key": "service_client_secret", + "value": "", + "type": "secret", + "enabled": true + }, + { + "key": "realm", + "value": "d4science", + "type": "any", + "enabled": true + }, + { + "key": "username", + "value": "luca.frosini", + "type": "default", + "enabled": true + }, + { + "key": "service_client_id", + "value": "id.d4science.org", + "type": "default", + "enabled": true + }, + { + "key": "context", + "value": "/gcube/devsec/devVRE", + "type": "default", + "enabled": true + }, + { + "key": "keycloak_url", + "value": "https://accounts.dev.d4science.org/auth", + "type": "any", + "enabled": true + }, + { + "key": "encoded_context", + "value": "%2Fgcube%2Fdevsec%2FdevVRE", + "type": "any", + "enabled": true + }, + { + "key": "client-id-user", + "value": "next.d4science.org", + "type": "default", + "enabled": true + }, + { + "key": "introspect_client", + "value": "token-exchange-dedicated", + "type": "default", + "enabled": true + } + ], + "_postman_variable_scope": "environment", + "_postman_exported_at": "2024-05-24T16:06:23.560Z", + "_postman_exported_using": "Postman/11.1.14" +} \ No newline at end of file diff --git a/documentation/postman/Hello World Service.postman_collection.json b/documentation/postman/Hello World Service.postman_collection.json new file mode 100644 index 0000000..d843f68 --- /dev/null +++ b/documentation/postman/Hello World Service.postman_collection.json @@ -0,0 +1,692 @@ +{ + "info": { + "_postman_id": "e152bab3-7be5-4fe2-81bd-625fec76373d", + "name": "Hello World Service", + "description": "An example collection that can be used for communicating with gcube services\n\nget TOKEN and at UMA_TOKEN from url: [https://next.dev.d4science.org/group/gcube/home](https://next.dev.d4science.org/group/gcube/home)\n\nupdate the collection's variables gcube_token and uma_token", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "3092110" + }, + "item": [ + { + "name": "LOGIN", + "item": [ + { + "name": "Clear Env", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "console.log(\"executing clear script\");", + "", + "variables = [", + " 'token',", + " 'access_token',", + " 'refresh_token',", + "", + " 'uma_token',", + " 'uma_refresh',", + "", + " 'profile_token',", + " 'gcube_token',", + " 'oidc_access_token',", + " 'oidc_refresh_token',", + " 'id_token',", + "", + " 'clientId',", + "", + " 'current_username',", + " 'current_password',", + " 'current_context',", + " 'current_url-encoded-context',", + " 'current_iam-url',", + " 'current_client-id',", + " 'current_uma-token',", + " 'current_access-token',", + "", + " 'oidc_token',", + " 'oidc_access_token',", + " 'oidc_refresh_token',", + " 'uma_token',", + " 'exchanged_token'", + "", + "]", + "", + "for (var v of variables) {", + " pm.environment.unset(v);", + " pm.globals.unset(v);", + "}", + "", + " ", + "", + "", + " ", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "/", + "path": [ + "" + ] + } + }, + "response": [] + }, + { + "name": "Get User Token For Context", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = JSON.parse(responseBody);", + "postman.setEnvironmentVariable(\"oidc_access_token\", jsonData.access_token);", + "postman.setEnvironmentVariable(\"oidc_refresh_token\", jsonData.refresh_token);", + "", + "postman.setEnvironmentVariable(\"uma_token\", jsonData.access_token);", + "", + "postman.setEnvironmentVariable(\"access_token\", jsonData.access_token);", + "postman.setEnvironmentVariable(\"refresh_token\", jsonData.refresh_token);", + "" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "auth": { + "type": "noauth" + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + }, + { + "key": "X-D4Science-Context", + "value": "{{encoded_context}}" + } + ], + "body": { + "mode": "urlencoded", + "urlencoded": [ + { + "key": "client_id", + "value": "{{service_client_id}}", + "type": "text" + }, + { + "key": "username", + "value": "{{username}}", + "type": "text" + }, + { + "key": "password", + "value": "{{password}}", + "type": "text" + }, + { + "key": "grant_type", + "value": "password", + "type": "text" + }, + { + "key": "client_secret", + "value": "{{service_client_secret}}", + "type": "text" + } + ] + }, + "url": { + "raw": "{{keycloak_url}}/realms/{{realm}}/protocol/openid-connect/token", + "host": [ + "{{keycloak_url}}" + ], + "path": [ + "realms", + "{{realm}}", + "protocol", + "openid-connect", + "token" + ] + }, + "description": "Obtain UAT = user access token from a user in realm" + }, + "response": [] + }, + { + "name": "Get ClientId Token For Context", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = JSON.parse(responseBody);", + "", + "// postman.setEnvironmentVariable(\"refresh_token\", jsonData.refresh_token);", + "// postman.setEnvironmentVariable(\"id_token\", jsonData.id_token);", + "", + "postman.setEnvironmentVariable(\"oidc_access_token\", jsonData.access_token);", + "", + "postman.setEnvironmentVariable(\"access_token\", jsonData.access_token);", + "postman.setEnvironmentVariable(\"token\", jsonData.access_token);", + "", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "auth": { + "type": "noauth" + }, + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + }, + { + "key": "X-D4Science-Context", + "value": "{{encoded_context}}" + } + ], + "body": { + "mode": "urlencoded", + "urlencoded": [ + { + "key": "client_id", + "value": "{{service_client_id}}", + "type": "text" + }, + { + "key": "client_secret", + "value": "{{service_client_secret}}", + "type": "text" + }, + { + "key": "grant_type", + "value": "client_credentials", + "type": "text" + } + ] + }, + "url": { + "raw": "{{keycloak_url}}/realms/{{realm}}/protocol/openid-connect/token", + "host": [ + "{{keycloak_url}}" + ], + "path": [ + "realms", + "{{realm}}", + "protocol", + "openid-connect", + "token" + ] + }, + "description": "Obtain SAT (service account token)" + }, + "response": [] + }, + { + "name": "Validate Access Token", + "request": { + "auth": { + "type": "noauth" + }, + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Basic YWxmcmVkby1pZG0tc2VydmljZS1kZXY6OTc5YmQzYmMtNWNjNC0xMWVjLWJmNjMtMDI0MmFjMTMwMDAy" + } + ], + "body": { + "mode": "urlencoded", + "urlencoded": [ + { + "key": "token", + "value": "{{access_token}}", + "type": "text" + }, + { + "key": "username\n", + "value": "{{introspect_client}}", + "type": "text", + "disabled": true + }, + { + "key": "password", + "value": "{{introspect_secret}}", + "type": "text", + "disabled": true + }, + { + "key": "client_id", + "value": "{{introspect_client}}", + "type": "text" + }, + { + "key": "client_secret", + "value": "{{introspect_secret}}", + "type": "text" + } + ] + }, + "url": { + "raw": "{{keycloak_url}}/realms/{{realm}}/protocol/openid-connect/token/introspect", + "host": [ + "{{keycloak_url}}" + ], + "path": [ + "realms", + "{{realm}}", + "protocol", + "openid-connect", + "token", + "introspect" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "REST", + "item": [ + { + "name": "authorized", + "item": [ + { + "name": "legacy-token", + "item": [ + { + "name": "hello TOKEN PARAM", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [ + { + "key": "", + "value": "{{token}}" + } + ], + "url": { + "raw": "{{base_url}}/{{application}}/hello?gcube-token={{gcube_token}}", + "host": [ + "{{base_url}}" + ], + "path": [ + "{{application}}", + "hello" + ], + "query": [ + { + "key": "gcube-token", + "value": "{{gcube_token}}" + } + ] + } + }, + "response": [] + }, + { + "name": "details TOKEN PARAM", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/{{application}}/details?gcube-token={{gcube_token}}", + "host": [ + "{{base_url}}" + ], + "path": [ + "{{application}}", + "details" + ], + "query": [ + { + "key": "gcube-token", + "value": "{{gcube_token}}" + } + ] + } + }, + "response": [] + }, + { + "name": "auth org member TOKEN PARAM", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/{{application}}/auth/org_member?gcube-token={{gcube_token}}", + "host": [ + "{{base_url}}" + ], + "path": [ + "{{application}}", + "auth", + "org_member" + ], + "query": [ + { + "key": "gcube-token", + "value": "{{gcube_token}}" + } + ] + } + }, + "response": [] + }, + { + "name": "auth member TOKEN PARAM", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/{{application}}/auth/member?gcube-token={{gcube_token}}", + "host": [ + "{{base_url}}" + ], + "path": [ + "{{application}}", + "auth", + "member" + ], + "query": [ + { + "key": "gcube-token", + "value": "{{gcube_token}}" + } + ] + } + }, + "response": [] + } + ] + }, + { + "name": "hello", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/{{application}}/hello", + "host": [ + "{{base_url}}" + ], + "path": [ + "{{application}}", + "hello" + ] + } + }, + "response": [] + }, + { + "name": "details", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/{{application}}/details", + "host": [ + "{{base_url}}" + ], + "path": [ + "{{application}}", + "details" + ] + } + }, + "response": [] + }, + { + "name": "auth org member", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{uma_token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/{{application}}/auth/org_member", + "host": [ + "{{base_url}}" + ], + "path": [ + "{{application}}", + "auth", + "org_member" + ] + } + }, + "response": [] + }, + { + "name": "auth member", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{uma_token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/{{application}}/auth/member", + "host": [ + "{{base_url}}" + ], + "path": [ + "{{application}}", + "auth", + "member" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "No Auth Required", + "item": [ + { + "name": "guest", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/{{application}}/guest", + "host": [ + "{{base_url}}" + ], + "path": [ + "{{application}}", + "guest" + ] + } + }, + "response": [] + }, + { + "name": "404 Not Found", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/{{application}}/guest/not-found", + "host": [ + "{{base_url}}" + ], + "path": [ + "{{application}}", + "guest", + "not-found" + ] + } + }, + "response": [] + }, + { + "name": "400 Bad Request", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "{{base_url}}/{{application}}/guest/bad-request", + "host": [ + "{{base_url}}" + ], + "path": [ + "{{application}}", + "guest", + "bad-request" + ] + } + }, + "response": [] + }, + { + "name": "PURGE with 204 No Content", + "request": { + "auth": { + "type": "noauth" + }, + "method": "PURGE", + "header": [], + "url": { + "raw": "{{base_url}}/{{application}}/guest/test-purge", + "host": [ + "{{base_url}}" + ], + "path": [ + "{{application}}", + "guest", + "test-purge" + ] + } + }, + "response": [] + } + ] + } + ] + } + ], + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{access_token}}", + "type": "string" + } + ] + }, + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "packages": {}, + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "packages": {}, + "exec": [ + "" + ] + } + } + ], + "variable": [ + { + "key": "base_url", + "value": "http://localhost:8080", + "type": "string" + }, + { + "key": "role_name", + "value": "Member" + }, + { + "key": "application", + "value": "hello-world-service", + "type": "string" + }, + { + "key": "base_url_marco", + "value": "http://146.48.85.179:9999", + "type": "string" + }, + { + "key": "base_url_local", + "value": "http://localhost:8080", + "type": "string" + } + ] +} \ No newline at end of file diff --git a/documentation/sphinx/Makefile b/documentation/sphinx/Makefile new file mode 100644 index 0000000..d4bb2cb --- /dev/null +++ b/documentation/sphinx/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/documentation/sphinx/conf.py b/documentation/sphinx/conf.py new file mode 100644 index 0000000..5b7a2d5 --- /dev/null +++ b/documentation/sphinx/conf.py @@ -0,0 +1,62 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + +_POM_PATH = "../.." + +def getMvnVariable(variable): + cmd = """cd %s && mvn -q \ + -Dexec.executable=echo \ + -Dexec.args='${project.%s}' \ + --non-recursive \ + exec:exec""" % (_POM_PATH, variable) + stream = os.popen(cmd) + return stream.read().strip() + +# -- Project information ----------------------------------------------------- +# The full version, including alpha/beta/rc tags +release = getMvnVariable("version") +project = getMvnVariable("name") + +copyright = '2024, %s' % getMvnVariable("organization.name") +author = 'Luca Frosini (ISTI-CNR)' + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'sphinxdoc' + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] \ No newline at end of file diff --git a/documentation/sphinx/index.rst b/documentation/sphinx/index.rst new file mode 100644 index 0000000..7883f7a --- /dev/null +++ b/documentation/sphinx/index.rst @@ -0,0 +1,258 @@ +*********************************************************** +Welcome to Hello World SG4 documentation +*********************************************************** + +Hello World SG4 is a RESTful application that exposes operations via REST-API. + +See the available `REST-API docs <../api-docs/index.html>`_. + +Base URL +======== + +In the production environment, its current value is + + +Authorization +============= + +D4Science adopts state-of-the-art industry standards for authentication and authorization. +Specifically, the implementation fully adopts `OIDC (OpenID Connect) `_ for authentication and UMA 2 (User-Managed Authorization) for authorization flows. +`JSON Web Token (JWT) Access token `_ are used for both authentication and authorization. + +Obtain your Bearer token here: https://dev.d4science.org/how-to-access-resources + +Service +======= + +You can call the methods of the Web Service by writing your REST client application or using existing REST client plugins. + + +HTTP Statuses +------------- + +Any successful operation returns a *200 OK* HTTP status code. +The create operation returns *201 Created*. +Any Background operation returns *202 Accepted*. +Any operation that does not provide any content returns *204 No Content*. + + + +The most common error statuses a client can obtain are: + +* **400 Bad Request** used to indicate a clients error ``_; +* **401 Unauthorized** used to indicate that the client does not provide the authorization token in the HTTP Header or the client does not have enough right to perform such request ``_; +* **404 Not Found** used to indicate that the requested instance does not exist ``_; +* **405 Method Not Allowed** the used HTTP method is not supported for the requested URL ``_. + The response contains the *Allow* HTTP Header indicating the supported HTTP method for such URL ``_; +* **409 Conflict** the request could not be completed due to a conflict with the current state of the target resource (e.g. the name of the resource already exists) ``_; +* **500 Internal Server Error** indicate a server failure ``_. + +You can find a complete list of HTTP Status at ``_ or ``_ + +If you get a *500 Internal Server Error*, please report it in the `gCube ticketing system `_. + +Please use this checklist before reporting an error: + +* Replicate the request; +* The failure could be temporal due to a network error, a server issue, and many other temporal issues. For this reason, please retry the request after a certain amount of time before reporting the issue; +* indicate how to replicate the error; +* indicate the time when the error occurred (this simplifies identifying the issue). + +HTTP Methods +------------ + +gCat is a pure RESTful service. It uses standard HTTP Methods to perform a listing of collections and CRUD (Create Read Update Delete) operations on instances. + + +.. table:: + + +--------------+-------------+----------------------------------------+---------------------+--------+------------+ + | Operation | HTTP Method | URL | Success HTTP Status | Safe | Idempotent | + +==============+=============+========================================+=====================+========+============+ + | Supported | OPTIONS | /{COLLECTION} | 204 No Content | Y | Y | + | HTTP Methods | | | [#allow]_ | | | + +--------------+-------------+----------------------------------------+---------------------+--------+------------+ + | List | GET | /{COLLECTION} | 200 OK | Y | Y | + +--------------+-------------+----------------------------------------+---------------------+--------+------------+ + | Count | GET | /{COLLECTION}?count=true | 200 OK | Y | Y | + +--------------+-------------+----------------------------------------+---------------------+--------+------------+ + | Exists | HEAD | /{COLLECTION} | 204 No Content | Y | Y | + +--------------+-------------+----------------------------------------+---------------------+--------+------------+ + | Create | POST | /{COLLECTION} | 201 Created | N | N | + +--------------+-------------+----------------------------------------+---------------------+--------+------------+ + | Supported | OPTIONS | /{COLLECTION}/{INSTANCE_ID} | 204 No Content | Y | Y | + | HTTP Methods | | | [#allow]_ | | | + +--------------+-------------+----------------------------------------+---------------------+--------+------------+ + | Exist | HEAD | /{COLLECTION}/{INSTANCE_ID} | 204 No Content | Y | Y | + +--------------+-------------+----------------------------------------+---------------------+--------+------------+ + | Read | GET | /{COLLECTION}/{INSTANCE_ID} | 200 OK | Y | Y | + +--------------+-------------+----------------------------------------+---------------------+--------+------------+ + | Update | PUT | /{COLLECTION}/{INSTANCE_ID} | 200 OK | N | Y | + +--------------+-------------+----------------------------------------+---------------------+--------+------------+ + | Patch | PATCH | /{COLLECTION}/{INSTANCE_ID} | 200 OK | N | Y | + +--------------+-------------+----------------------------------------+---------------------+--------+------------+ + | Delete | DELETE | /{COLLECTION}/{INSTANCE_ID} | 204 No Content | N | N [#del]_ | + +--------------+-------------+----------------------------------------+---------------------+--------+------------+ + | Purge | DELETE | /{COLLECTION}/{INSTANCE_ID}?purge=true | 204 No Content | N | N [#del]_ | + + +-------------+----------------------------------------+---------------------+--------+------------+ + | | PURGE | /{COLLECTION}/{INSTANCE_ID} | 204 No Content | N | N [#del]_ | + +--------------+-------------+----------------------------------------+---------------------+--------+------------+ + +.. [#allow] Supported HTTP Methods in **Allow** HTTP Header + +.. [#del] DELETE has been defined as idempotent. + + *Allamaraju* [#Allamaraju]_ argues that DELETE idempotency should be accomplished client-side. + The server should inform the client if the delete operation succeeded because the resource was deleted or it was not found, i.e., **404 Not Found** error is suggested instead of **204 No Content**. + The latter situation should be treated as idempotent by the client. + + We share the same vision. For this reason, gCat does not provide server-side idempotency for DELETE and PURGE operations. + +.. [#Allamaraju] Allamaraju S. RESTful Web Services Cookbook: Solutions for Improving Scalability and Simplicity. O’Reilly. first ed. 2010 + + +About URL +^^^^^^^^^ + +The presented URL uses the following convention: + +* **{COLLECTION}** is the plural name of the entity type; +* **{INSTANCE_ID}** is an identification that enables univocally identifying the instance in the collection. + + +About Safety and Idempotency properties +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + +* A method is *Safe* if it does not produce any side effects. + "This does not prevent an implementation from including behaviour that is potentially harmful, that is not entirely read-only, or that causes side effects while invoking a safe method" + ``_; +* A method is *Idempotent* if the same operation repeated multiple times has the same side effect as using it one time. + "repeating the request will have the same intended effect, even if the original request succeeded, though the response might differ" + ``_. + +You can find more information about HTTP Methods at ``_ + +Uncommon HTTP Methods +^^^^^^^^^^^^^^^^^^^^^ + +* PATCH method allows to perform a differential update (i.e. an update which provides only the differences and not the whole new representation); +* PURGE method is not a standard but is widely used in services that require this action + (e.g. `Varnish `_, `Squid `_). + gCat provides support for this method, but to support a wider range of clients, it also provides the Purge action via *DELETE* with the additional get parameter ``purge=true``. + + +Content-Type +------------ + +Any request must contain an indication of the interesting content type. + +The client must specify the **Accept** HTTP Header for any operation returning a result. + +.. code-block:: rest + + Accept: application/json + +For any operation sending content to the service, it is necessary to specify the **Content-Type** HTTP Header. + +.. code-block:: rest + + Content-Type: application/json + +The service accepts and returns only JSON objects. + + + +Collections +----------- + + + + +Roles +----- + + + +Java Client +=========== + +We provide the following Java Client out-of-the-box. + + .. TIP:: + If you're coding in Java, it is recommended that you use this Java Client. + +**Maven Coordinates** + +.. code:: xml + + org.gcube.service + helloworld-client + [1.0.0-SNAPSHOT, 2.0.0-SNAPSHOT) + +**Methods Result** + +The service exposes `its methods <../api-docs/index.html>`_ using a standard naming approach. Moreover, they accept (in the case of HTTP POST/PUT methods) JSON objects. + + .. IMPORTANT:: + The result of all methods is always a JSON object as per below: + +.. code:: javascript + + { + .... + } + +*Inputs are automatically validated before the request is served.* + + +**Usage examples** + +- Example 1 + +.. code:: java + + import org.gcube....; + + + + +Service Discovery on IS +======================= + +The service can be discovered in the gCore IS as gCore Endpoint with the following parameter: + +.. code:: xml + + org.gcube.service + helloworld + + +The service can be discovered in the Facet-Based IS as EService with the following JSON query: + +.. code:: json + + { + "@class": "EService", + "consistsOf": [ + { + "@class": "IsIdentifiedBy", + "target": { + "@class": "SoftwareFacet", + "group": "org.gcube.service", + "name": "helloworld" + } + } + ] + } + +Service Maven Coordinates +========================= + +The maven coordinates of the Hello World SG4 service are: + +.. code:: xml + + org.gcube.service + helloworld + \ No newline at end of file diff --git a/documentation/sphinx/make.bat b/documentation/sphinx/make.bat new file mode 100644 index 0000000..153be5e --- /dev/null +++ b/documentation/sphinx/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/documentation/sphinx/tests.rst b/documentation/sphinx/tests.rst new file mode 100644 index 0000000..fca9245 --- /dev/null +++ b/documentation/sphinx/tests.rst @@ -0,0 +1,23 @@ +Dev and Pre Users used for moderation tests +======== + +To perform moderation tests in dev and preproduction infrastructure we use different users with the indicated roles. + +.. table:: + + +---------------+---------------+----------------------------------------+ + | User | Username | Role | + +===============+===============+========================================+ + | Mister Blonde | mister.blonde | Catalogue-Admin + Catalogue-Moderator | + +---------------+---------------+----------------------------------------+ + | Mister Blue | mister.blue | Catalogue-Admin | + +---------------+---------------+----------------------------------------+ + | Mister Brown | mister.brown | Catalogue-Moderator | + +---------------+---------------+----------------------------------------+ + | Mister Orange | mister.orange | Catalogue-Editor | + +---------------+---------------+----------------------------------------+ + | Mister Pink | mister.pink | NO ROLE (means Catalogue-Member) | + +---------------+---------------+----------------------------------------+ + | Mister White | mister.white | Catalogue-Manager | + +---------------+---------------+----------------------------------------+ + diff --git a/pom.xml b/pom.xml index fad40eb..ecfdd98 100644 --- a/pom.xml +++ b/pom.xml @@ -1,23 +1,49 @@ - - 4.0.0 - - org.gcube.tools - maven-parent - 1.2.0 - - org.gcube.resource-management - resource-manager - 1.0.0-SNAPSHOT - war - Resource Manager - - - 11 - UTF-8 + + 4.0.0 + + org.gcube.tools + maven-parent + 1.2.0 + + org.gcube.resource-management + resource-manager + 1.0.0-SNAPSHOT + war + Resource Manager + + + gCube System + https://www.gcube-system.org/ + + + + + luca.frosini + Luca Frosini + luca.frosini@isti.cnr.it + https://www.isti.cnr.it/en/about/people-detail/141/Frosini_Luca + ISTI-CNR + https://www.isti.cnr.it/ + + researcher + developer + + Europe/Rome + + + + + 11 ${java.version} ${java.version} - ${project.basedir}${file.separator}src${file.separator}main${file.separator}webapp${file.separator}WEB-INF + UTF-8 + + ${project.basedir}${file.separator}src${file.separator}main${file.separator}webapp${file.separator}WEB-INF + 2.14.0 + 4.0.1-SNAPSHOT @@ -79,7 +105,6 @@ org.glassfish.jersey.containers jersey-container-servlet - org.gcube.common gxHTTP @@ -93,6 +118,7 @@ org.gcube.core common-smartgears + com.webcohesion.enunciate @@ -107,10 +133,10 @@ provided - javax.servlet - javax.servlet-api - 3.1.0 - provided + javax.servlet + javax.servlet-api + 3.1.0 + provided @@ -130,17 +156,21 @@ - + kr.motd.maven sphinx-maven-plugin 2.10.0 - ${project.build.directory}/${project.artifactId}-${project.version}/docs + + ${project.build.directory}${file.separator}${project.build.finalName}${file.separator}docs html - ${basedir}/docs - ${basedir}/docs + ${project.basedir}${file.separator}documentation${file.separator}sphinx + ${project.basedir}${file.separator}documentation${file.separator}sphinx + + + file:/opt/homebrew/opt/sphinx-doc/bin/sphinx-build @@ -151,45 +181,53 @@ - + - - - - - - - - - - - - - + + com.webcohesion.enunciate + enunciate-maven-plugin + ${enunciate.version} + + + assemble + + assemble + + + + + + ${project.basedir}${file.separator}documentation${file.separator}enunciate.xml + + + - - - - - - - - - - - - - - - - - - - - - - - + + org.apache.maven.plugins + maven-resources-plugin + + + copy-enunciate-docs + process-resources + + copy-resources + + + target + + + ${project.build.directory}${file.separator}${project.artifactId}-${project.version}${file.separator}api-docs + ${project.build.directory}/api-docs + true + + + + + + + \ No newline at end of file diff --git a/src/main/java/org/gcube/resourcemanagement/ResourceInitializer.java b/src/main/java/org/gcube/resourcemanagement/RMInitializer.java similarity index 85% rename from src/main/java/org/gcube/resourcemanagement/ResourceInitializer.java rename to src/main/java/org/gcube/resourcemanagement/RMInitializer.java index c000d47..4954e03 100644 --- a/src/main/java/org/gcube/resourcemanagement/ResourceInitializer.java +++ b/src/main/java/org/gcube/resourcemanagement/RMInitializer.java @@ -12,9 +12,9 @@ import jakarta.ws.rs.ApplicationPath; */ @ApplicationPath("/") @ManagedBy(ResourceManager.class) -public class ResourceInitializer extends ResourceConfig { +public class RMInitializer extends ResourceConfig { - public ResourceInitializer() { + public RMInitializer() { packages(BaseREST.class.getPackage().toString()); packages(Configuration.class.getPackage().toString()); } diff --git a/src/main/java/org/gcube/resourcemanagement/ResourceManager.java b/src/main/java/org/gcube/resourcemanagement/ResourceManager.java index 1eff703..1ca107c 100644 --- a/src/main/java/org/gcube/resourcemanagement/ResourceManager.java +++ b/src/main/java/org/gcube/resourcemanagement/ResourceManager.java @@ -38,7 +38,8 @@ public class ResourceManager implements ApplicationManager { context); ApplicationContext applicationContext = ContextProvider.get(); - String rrEServiceID = applicationContext.id(); + String rmEServiceID = applicationContext.id(); + logger.info("Resource Manager ahs the following ID {}", rmEServiceID); logger.trace( diff --git a/src/main/java/org/gcube/resourcemanagement/rest/BaseREST.java b/src/main/java/org/gcube/resourcemanagement/rest/BaseREST.java index 7a2d1d2..ece3a48 100644 --- a/src/main/java/org/gcube/resourcemanagement/rest/BaseREST.java +++ b/src/main/java/org/gcube/resourcemanagement/rest/BaseREST.java @@ -20,7 +20,7 @@ import jakarta.ws.rs.core.UriInfo; }) public class BaseREST { - private final Logger logger = LoggerFactory.getLogger(this.getClass()); + protected final Logger logger = LoggerFactory.getLogger(this.getClass()); public static final String APPLICATION_JSON_CHARSET_UTF_8 = "application/json;charset=UTF-8"; public static final String APPLICATION_JSON_API = "application/vnd.api+json"; @@ -35,11 +35,6 @@ public class BaseREST { protected static final String LOCATION_HEADER = "Location"; - protected void setCalledMethod(String method) { - InnerMethodName.set(method); - logger.info("{}", uriInfo.getAbsolutePath()); - } - protected ResponseBuilder addLocation(ResponseBuilder responseBuilder, String id) { return responseBuilder.header(LOCATION_HEADER, String.format("%s/%s", uriInfo.getAbsolutePath().toString(), id)); @@ -55,4 +50,33 @@ public class BaseREST { return stringBuilder.toString(); } + protected void setAccountingMethod(String method) { + InnerMethodName.set(method); + logger.info("{}", uriInfo.getAbsolutePath()); + } + +// protected void setAccountingMethod(Method method, String type) { +// StringBuffer accountingMethod = new StringBuffer(); +// accountingMethod.append(method.getPrefix()); +// accountingMethod.append(type); +// accountingMethod.append(method.getSuffix()); +// setAccountingMethod(accountingMethod.toString()); +// } +// +// private ServerRequestInfo initRequestInfo(ServerRequestInfo requestInfo) { +// requestInfo.setUriInfo(uriInfo); +// RequestUtility.getRequestInfo().set(requestInfo); +// return requestInfo; +// } +// +// protected ServerRequestInfo initRequestInfo(int offset, int limit) { +// ServerRequestInfo requestInfo = new ServerRequestInfo(offset, limit); +// return initRequestInfo(requestInfo); +// } +// +// protected ServerRequestInfo initRequestInfo() { +// ServerRequestInfo requestInfo = new ServerRequestInfo(); +// return initRequestInfo(requestInfo); +// } + } diff --git a/src/main/java/org/gcube/resourcemanagement/rest/ContextManager.java b/src/main/java/org/gcube/resourcemanagement/rest/ContextManager.java new file mode 100644 index 0000000..b0d6e6e --- /dev/null +++ b/src/main/java/org/gcube/resourcemanagement/rest/ContextManager.java @@ -0,0 +1,114 @@ +package org.gcube.resourcemanagement.rest; + +import org.gcube.informationsystem.contexts.reference.entities.Context; +import org.gcube.informationsystem.resourceregistry.api.exceptions.ResourceRegistryException; +import org.gcube.informationsystem.resourceregistry.api.exceptions.contexts.ContextNotFoundException; +import org.gcube.informationsystem.resourceregistry.api.rest.ContextPath; + +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.Response.Status; + +/** + * @author Luca Frosini (ISTI - CNR) + */ +@Path(ContextManager.CONTEXTS_PATH_PART) +public class ContextManager extends BaseREST { + + public static final String CONTEXTS_PATH_PART = "contexts"; + + public static final String CONTEXT_UUID_PATH_PARAMETER = "CONTEXT_UUID"; + + public static final String CURRENT_CONTEXT_PATH_PART = "CURRENT_CONTEXT"; + + public ContextManager() { + super(); + } + + /* + * Create a new Context suing the provided definition + * + * POST /contexts + * + * BODY: {...} + * + */ + @POST + @Path("{" + ContextManager.CONTEXT_UUID_PATH_PARAMETER + "}") + @Consumes({MediaType.TEXT_PLAIN, BaseREST.APPLICATION_JSON_CHARSET_UTF_8}) + @Produces(BaseREST.APPLICATION_JSON_CHARSET_UTF_8) + public String create(String json) + throws ResourceRegistryException { + logger.info("Requested to update/create {} with json {} ", Context.NAME, json); +// setAccountingMethod(Method.UPDATE, Context.NAME); + + return ""; + } + + /* + * Allow to read the context definition + * GET /contexts/{UUID} + * e.g. GET /contexts/c0f314e7-2807-4241-a792-2a6c79ed4fd0 + * + */ + @GET + @Path("{" + ContextManager.CONTEXT_UUID_PATH_PARAMETER + "}") + @Consumes({MediaType.TEXT_PLAIN, BaseREST.APPLICATION_JSON_CHARSET_UTF_8}) + @Produces(BaseREST.APPLICATION_JSON_CHARSET_UTF_8) + public String read(@PathParam(ContextManager.CONTEXT_UUID_PATH_PARAMETER) String uuid) + throws ContextNotFoundException, ResourceRegistryException { + if(uuid.compareTo(ContextPath.CURRENT_CONTEXT_PATH_PART)==0){ + //uuid = ContextUtility.getCurrentSecurityContext().getUUID().toString(); + // TODO get from IS + } + logger.info("Requested to read {} with id {} ", Context.NAME, uuid); +// setAccountingMethod(Method.READ, Context.NAME); + + return ""; + } + + /* + * Update an existing Context definition + * + * PUT /contexts/{UUID} + * e.g. PUT /contexts/c0f314e7-2807-4241-a792-2a6c79ed4fd0 + * + * BODY: {...} + * + */ + @PUT + @Path("{" + ContextManager.CONTEXT_UUID_PATH_PARAMETER + "}") + @Consumes({MediaType.TEXT_PLAIN, BaseREST.APPLICATION_JSON_CHARSET_UTF_8}) + @Produces(BaseREST.APPLICATION_JSON_CHARSET_UTF_8) + public String update(@PathParam(ContextManager.CONTEXT_UUID_PATH_PARAMETER) String uuid, String json) + throws ResourceRegistryException { + logger.info("Requested to update/create {} with json {} ", Context.NAME, json); +// setAccountingMethod(Method.UPDATE, Context.NAME); + + return ""; + } + + /* + * DELETE /contexts/{UUID} + * e.g. DELETE /contexts/c0f314e7-2807-4241-a792-2a6c79ed4fd0 + */ + @DELETE + @Consumes({MediaType.TEXT_PLAIN, BaseREST.APPLICATION_JSON_CHARSET_UTF_8}) + @Path("{" + ContextManager.CONTEXT_UUID_PATH_PARAMETER + "}") + public Response delete(@PathParam(ContextManager.CONTEXT_UUID_PATH_PARAMETER) String uuid) + throws ContextNotFoundException, ResourceRegistryException { + logger.info("Requested to delete {} with id {} ", Context.NAME, uuid); + setAccountingMethod("deleteContext"); + + return Response.status(Status.NO_CONTENT).build(); + } + +} diff --git a/src/main/java/org/gcube/resourcemanagement/rest/ResourceManagerExceptionMapper.java b/src/main/java/org/gcube/resourcemanagement/rest/RMExceptionMapper.java similarity index 92% rename from src/main/java/org/gcube/resourcemanagement/rest/ResourceManagerExceptionMapper.java rename to src/main/java/org/gcube/resourcemanagement/rest/RMExceptionMapper.java index 5ae3d8a..0fc004f 100644 --- a/src/main/java/org/gcube/resourcemanagement/rest/ResourceManagerExceptionMapper.java +++ b/src/main/java/org/gcube/resourcemanagement/rest/RMExceptionMapper.java @@ -11,7 +11,7 @@ import jakarta.ws.rs.ext.Provider; * @author Luca Frosini (ISTI - CNR) */ @Provider -public class ResourceManagerExceptionMapper implements ExceptionMapper { +public class RMExceptionMapper implements ExceptionMapper { @Override public Response toResponse(Exception exception) { diff --git a/src/main/java/org/gcube/resourcemanagement/rest/administration/Configuration.java b/src/main/java/org/gcube/resourcemanagement/rest/administration/Configuration.java index 8303e3a..4ab568a 100644 --- a/src/main/java/org/gcube/resourcemanagement/rest/administration/Configuration.java +++ b/src/main/java/org/gcube/resourcemanagement/rest/administration/Configuration.java @@ -32,10 +32,10 @@ import jakarta.ws.rs.core.Response.Status; import jakarta.xml.ws.WebServiceException; /** - * The catalogue configuration for the context of the request + * The Resource Manager configuration for the context of the request * (i.e. the context where the token has been generated). * - * Only Catalogue-Managers are able to invoke non-safe methods. + * Only Managers are able to invoke non-safe methods. * * @author Luca Frosini (ISTI - CNR) */ @@ -100,18 +100,18 @@ public class Configuration extends BaseREST { } /** - * This API allows to read the RR configuration for the + * This API allows to read the RM configuration for the * current context (i.e. the context where the token has been generated).
*/ @GET @Path("/{" + CONTEXT_FULLNAME_PARAMETER + "}") @Produces(BaseREST.APPLICATION_JSON_CHARSET_UTF_8) -// @AuthorizationControl(allowedRoles={Role.CATALOGUE_EDITOR, Role.CATALOGUE_ADMIN, Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class) @StatusCodes ({ @ResponseCode(code = 200, condition = "Resource Manager configuration successfully read."), @ResponseCode(code = 401, condition = "Only User with role Manager above can read the configuration."), - @ResponseCode(code = 500, condition = "Error while reading catalogue configuration."), + @ResponseCode(code = 500, condition = "Error while reading the configuration."), }) + /** * @param context * @return @@ -153,11 +153,10 @@ public class Configuration extends BaseREST { @Path("/{" + CONTEXT_FULLNAME_PARAMETER + "}") @Consumes(BaseREST.APPLICATION_JSON_CHARSET_UTF_8) @Produces(BaseREST.APPLICATION_JSON_CHARSET_UTF_8) -// @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class) @StatusCodes ({ - @ResponseCode(code = 200, condition = "Catalogue configuration successfully created/updated."), - @ResponseCode(code = 401, condition = "Only Catalogue-Managers can create/update catalogue configuration."), - @ResponseCode(code = 500, condition = "Error while creating/updating catalogue configuration."), + @ResponseCode(code = 200, condition = "Resource Manager configuration successfully created/updated."), + @ResponseCode(code = 401, condition = "Only User with role Manager above can create/update the configuration."), + @ResponseCode(code = 500, condition = "Error while creating/updating the configuration."), }) @OperationId("Create or Update") public String createOrUpdate(@PathParam(CONTEXT_FULLNAME_PARAMETER) String context, String json) throws WebServiceException { @@ -187,7 +186,7 @@ public class Configuration extends BaseREST { } /** - * This API allows to patch the catalogue configuration for the + * This API allows to patch the Resource Manager configuration for the * context of the request (i.e. the context where the token has been generated) * using the json provided as request body.
*/ @@ -195,11 +194,10 @@ public class Configuration extends BaseREST { @Path("/{" + CONTEXT_FULLNAME_PARAMETER + "}") @Consumes(BaseREST.APPLICATION_JSON_CHARSET_UTF_8) @Produces(BaseREST.APPLICATION_JSON_CHARSET_UTF_8) -// @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class) @StatusCodes ({ - @ResponseCode(code = 200, condition = "Catalogue configuration successfully updated."), - @ResponseCode(code = 401, condition = "Only Catalogue-Managers can update catalogue configuration."), - @ResponseCode(code = 500, condition = "Error while updating catalogue configuration."), + @ResponseCode(code = 200, condition = "Resource Manager configuration successfully patched."), + @ResponseCode(code = 401, condition = "Only User with role Manager above can patch the configuration."), + @ResponseCode(code = 500, condition = "Error while patching the configuration."), }) public Response patch(@PathParam(CONTEXT_FULLNAME_PARAMETER) String context, String json) throws WebServiceException { try { @@ -233,11 +231,11 @@ public class Configuration extends BaseREST { * context of the request (i.e. the context where the token has been generated).
* * This API forces the service to read again from the Information System (IS) - * the resource manager configuration for the context of the request.
+ * the Resource Manager configuration for the context of the request.
* * If the user specifies the purge query parameter this API * remove the configuration from the IS. Please note that this implies that - * the resource manager is no more configured for the context of the request. + * the Resource Manager is no more configured for the context of the request. * * * @param context context must contains the context of the request @@ -253,9 +251,9 @@ public class Configuration extends BaseREST { @Path("/{" + CONTEXT_FULLNAME_PARAMETER + "}") // @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class) @StatusCodes ({ - @ResponseCode(code = 200, condition = "Catalogue configuration successfully deleted."), - @ResponseCode(code = 401, condition = "Only Catalogue-Managers can delete catalogue configuration."), - @ResponseCode(code = 500, condition = "Error while deleting catalogue configuration."), + @ResponseCode(code = 200, condition = "Resource Manager configuration successfully deleted."), + @ResponseCode(code = 401, condition = "Only User with role Manager above can delete the configuration."), + @ResponseCode(code = 500, condition = "Error while deleting the configuration."), }) public Response delete(@PathParam(CONTEXT_FULLNAME_PARAMETER) String context, @QueryParam(BaseREST.PURGE_QUERY_PARAMETER) @DefaultValue("false") Boolean purge) throws WebServiceException { @@ -304,9 +302,9 @@ public class Configuration extends BaseREST { @Path("/{" + CONTEXT_FULLNAME_PARAMETER + "}") // @AuthorizationControl(allowedRoles={Role.CATALOGUE_MANAGER}, exception=NotAuthorizedException.class) @StatusCodes ({ - @ResponseCode(code = 200, condition = "Catalogue configuration successfully deleted."), - @ResponseCode(code = 401, condition = "Only Catalogue-Managers can delete catalogue configuration."), - @ResponseCode(code = 500, condition = "Error while deleting catalogue configuration."), + @ResponseCode(code = 200, condition = "Resource Manager configuration purged deleted."), + @ResponseCode(code = 401, condition = "Only User with role Manager above can purge the configuration."), + @ResponseCode(code = 500, condition = "Error while purging the configuration."), }) public Response purge(@PathParam(CONTEXT_FULLNAME_PARAMETER) String context) throws WebServiceException { try { diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index 67c7bc7..996a73d 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -3,20 +3,19 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> - Hello World - - A gcube HelloWorld service - smartgears 4 - - HelloWorld - org.glassfish.jersey.servlet.ServletContainer - - jersey.config.server.provider.packages - org.gcube.service.helloworld.services - - - - HelloWorld - /* + org.gcube.resourcemanagement.RMInitializer + + + default + /docs/* + + default + /api-docs/* + + + org.gcube.resourcemanagement.RMInitializer + /* + \ No newline at end of file