Compare commits

...

5 Commits

14 changed files with 817 additions and 223 deletions

View File

@ -4,6 +4,7 @@ FROM d4science/smartgears-distribution:4.0.0-SNAPSHOT-java$JAVA_VERSION-tomcat10
COPY ./docker/logback.xml /etc/ COPY ./docker/logback.xml /etc/
COPY ./docker/container.ini /etc/ COPY ./docker/container.ini /etc/
COPY ./docker/*.gcubekey /tomcat/lib
COPY ./target/helloworld.war /tomcat/webapps/ COPY ./target/helloworld.war /tomcat/webapps/
EXPOSE 8080 EXPOSE 8080

View File

@ -14,7 +14,7 @@
# optional, for local develop in docker containers # optional, for local develop in docker containers
├── docker/ ├── docker/
│ ├── ontainer.ini │ ├── container.ini
│ └── logback.xml │ └── logback.xml
├── docker-compose.yaml ├── docker-compose.yaml
└── Dockerfile └── Dockerfile
@ -33,7 +33,6 @@
* mode: [online/offline]: register the service on IS. use offline for local docker instances * mode: [online/offline]: register the service on IS. use offline for local docker instances
# EXCECUTION # EXCECUTION
start the docker container start the docker container

View File

@ -1,24 +1,34 @@
#!/bin/bash #!/bin/bash
accepted_java_versions=(11 17) accepted_java_versions=(11 17)
NAME=smartgears-helloworld
PORT=8081
DEBUG_PORT=5005
DEBUG=false
COMPILE=true
java_version=11
CONTAINERFILE=docker/container.ini
################################################################################ ################################################################################
# Help # # Help #
################################################################################ ################################################################################
Help() Help() {
{
# Display Help # Display Help
echo "build, create and run in docker the helloworld service" echo "build, create and run in docker the helloworld service"
echo echo
echo "Syntax: buildDistribution [-n arg] [-p arg] [-j arg] [-d arg?] [-h]" echo "Syntax: buildDistribution [-n arg] [-p arg] [-j arg] [-d arg?] [-h]"
echo "options:" echo "options:"
echo "-s skip maven package"
echo "-n arg specifies the docker image name (default is smartgears-helloworld)." echo "-n arg specifies the docker image name (default is smartgears-helloworld)."
echo "-p arg specifies the port to be exposed for the docker container to access the service (default 8081)" echo "-p arg specifies the port to be exposed for the docker container to access the service (default $PORT)"
echo "-j arg specify java version (default is 11)" echo "-j arg specify java version (default is $java_version)"
echo " accepted version are: ${accepted_java_versions[@]}" echo " accepted version are: ${accepted_java_versions[@]}"
echo "-d arg? enable java debug mode" echo "-d arg? enable java debug mode"
echo " arg is the debug port (default is 5005)" echo " arg is the debug port (default is $DEBUG_PORT)"
echo "-h Print this Help." echo "-h Print this Help."
echo echo
} }
@ -31,38 +41,63 @@ Help()
set -e set -e
OPTSTRING=":sn:p:d:j:?h"
NAME=smartgears-helloworld while getopts $OPTSTRING opt; do
PORT=8081 # echo "Option -${opt} was triggered, Argument: ${OPTARG}"
DEBUG_PORT=5005 case "${opt}" in
debug=false s) COMPILE=false && echo "compile $COMPILE" ;;
compile=false
java_version=11
while getopts n:p:j:d?h flag
do
echo ${flag};
case "${flag}" in
n) NAME=${OPTARG} ;; n) NAME=${OPTARG} ;;
p) PORT=${OPTARG} ;; p) PORT=${OPTARG} ;;
d) debug=true && DEBUG_PORT=${OPTARG:-5005};; d) DEBUG=true
j) if [[ ${accepted_java_versions[@]} =~ ${OPTARG} ]] DEBUG_PORT=${OPTARG}
then java_version=${OPTARG}; echo "debug enabled, port $DEBUG_PORT"
else echo "Invalid java version" && echo "accepted version are: ${accepted_java_versions[@]}" && exit 1; ;;
fi;; 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 ;; h) Help && exit 0 ;;
*) echo "Invalid option" && exit 1 ;; :) # matched when an option that is expected to have an argument is passed without one
if [ ${OPTARG} = "d" ]; then
DEBUG=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 esac
done done
if ! test -e "$CONTAINERFILE"; then
echo "missing docker/container.ini file.
Create and configurate your container.config.ini file from container.default.ini,
then create a symbolic link to container.ini
cp docker/container.default.ini docker/container.configuration.ini
ln -s docker/container.configuration.ini docker/container.ini"
exit 1;
fi
if [ $COMPILE = true ]; then
mvn clean package mvn clean package
else
echo "skipping mvn package"
fi
docker build -t $NAME --build-arg JAVA_VERSION=${java_version} . docker build -t $NAME --build-arg JAVA_VERSION=${java_version} .
if [ $debug = false ] ; then if [ $DEBUG = false ]; then
docker run -p $PORT:8080 $NAME docker run -p $PORT:8080 $NAME
else else
docker run -p $PORT:8080 -p $DEBUG_PORT:5005 -e JAVA_TOOL_OPTIONS="-agentlib:jdwp=transport=dt_socket,address=*:5005,server=y,suspend=y" $NAME docker run -p $PORT:8080 -p $DEBUG_PORT:5005 -e JAVA_TOOL_OPTIONS="-agentlib:jdwp=transport=dt_socket,address=*:5005,server=y,suspend=y" $NAME
fi fi

3
docker/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
container*.ini
!container.default.ini
*.gcubekey

View File

@ -1,23 +0,0 @@
[node]
mode = offline
hostname = mb-lelii.isti.cnr.it
protocol= http
port = 8080
infrastructure = gcube
authorizeChildrenContext = true
publicationFrequencyInSeconds = 60
[properties]
SmartGearsDistribution = 4.0.0-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 = node-whn-test-uno-d-d4s.d4science.org
credentials.secret = 979bd3bc-5cc4-11ec-bf63-0242ac130002

1
docker/container.ini Symbolic link
View File

@ -0,0 +1 @@
container.alf.ini

View File

@ -8,51 +8,170 @@
}, },
"item": [ "item": [
{ {
"name": "hello TOKEN PARAM", "name": "[UAT] Obtain CONTEXT id token for a user",
"event": [
{
"listen": "test",
"script": {
"exec": [
"var jsonData = JSON.parse(responseBody);",
"pm.collectionVariables.set(\"access_token\", jsonData.access_token);",
"pm.collectionVariables.set(\"refresh_token\", jsonData.refresh_token);",
""
],
"type": "text/javascript",
"packages": {}
}
},
{
"listen": "prerequest",
"script": {
"exec": [
""
],
"type": "text/javascript",
"packages": {}
}
}
],
"request": { "request": {
"auth": { "auth": {
"type": "noauth" "type": "noauth"
}, },
"method": "GET", "method": "POST",
"header": [ "header": [
{
"key": "Content-Type",
"name": "Content-Type",
"value": "application/x-www-form-urlencoded",
"type": "text"
},
{
"key": "X-D4Science-Context",
"value": "{{url-encoded-context}}",
"type": "text"
},
{ {
"key": "", "key": "",
"value": "{{token}}", "value": "",
"type": "text",
"disabled": true
}
],
"body": {
"mode": "urlencoded",
"urlencoded": [
{
"key": "client_id",
"value": "{{client-id-user}}",
"type": "text"
},
{
"key": "username",
"value": "{{username}}",
"type": "text"
},
{
"key": "password",
"value": "{{password}}",
"type": "text"
},
{
"key": "grant_type",
"value": "password",
"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": "[UAT] refresh CONTEXT token",
"event": [
{
"listen": "test",
"script": {
"exec": [
"var jsonData = JSON.parse(responseBody);",
"",
"pm.collectionVariables.set(\"access_token\", jsonData.access_token);",
"pm.collectionVariables.set(\"refresh_token\", jsonData.refresh_token);",
""
],
"type": "text/javascript",
"packages": {}
}
}
],
"request": {
"method": "POST",
"header": [
{
"key": "X-D4Science-Context",
"value": "{{url-encoded-context}}",
"type": "text" "type": "text"
} }
], ],
"body": {
"mode": "urlencoded",
"urlencoded": [
{
"key": "grant_type",
"value": "refresh_token",
"type": "text"
},
{
"key": "client_id",
"value": "{{client-id-user}}",
"type": "text"
},
{
"key": "refresh_token",
"value": "{{refresh_token}}",
"type": "text"
},
{
"key": "client_secret",
"value": "{{service_client_secret}}",
"type": "text",
"disabled": true
}
]
},
"url": { "url": {
"raw": "{{base_url}}/{{application}}/hello?gcube-token={{gcube_token}}", "raw": "{{keycloak_url}}/realms/{{realm}}/protocol/openid-connect/token",
"host": [ "host": [
"{{base_url}}" "{{keycloak_url}}"
], ],
"path": [ "path": [
"{{application}}", "realms",
"hello" "{{realm}}",
], "protocol",
"query": [ "openid-connect",
{ "token"
"key": "gcube-token",
"value": "{{gcube_token}}"
}
] ]
} }
}, },
"response": [] "response": []
}, },
{ {
"name": "hello BEARER", "name": "hello",
"request": { "request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "{{uma_token}}",
"type": "string"
}
]
},
"method": "GET", "method": "GET",
"header": [], "header": [],
"url": { "url": {
@ -69,45 +188,8 @@
"response": [] "response": []
}, },
{ {
"name": "details TOKEN PARAM", "name": "details",
"request": { "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": "details BEARER",
"request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "{{uma_token}}",
"type": "string"
}
]
},
"method": "GET", "method": "GET",
"header": [], "header": [],
"url": { "url": {
@ -123,6 +205,25 @@
}, },
"response": [] "response": []
}, },
{
"name": "auth org member",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/{{application}}/auth/org_member",
"host": [
"{{base_url}}"
],
"path": [
"{{application}}",
"auth",
"org_member"
]
}
},
"response": []
},
{ {
"name": "guest", "name": "guest",
"request": { "request": {
@ -145,75 +246,8 @@
"response": [] "response": []
}, },
{ {
"name": "auth org member BEARER", "name": "auth member",
"request": { "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 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 BEARER",
"request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "{{uma_token}}",
"type": "string"
}
]
},
"method": "GET", "method": "GET",
"header": [], "header": [],
"url": { "url": {
@ -231,27 +265,31 @@
"response": [] "response": []
}, },
{ {
"name": "auth member TOKEN PARAM", "name": "test keycloak",
"request": { "request": {
"auth": {
"type": "noauth"
},
"method": "GET", "method": "GET",
"header": [], "header": [
{
"key": "gcube-token",
"value": "{{gcube_token}}",
"type": "text",
"disabled": true
}
],
"url": { "url": {
"raw": "{{base_url}}/{{application}}/auth/member?gcube-token={{gcube_token}}", "raw": "{{base_url}}/{{application}}/keycloak",
"host": [ "host": [
"{{base_url}}" "{{base_url}}"
], ],
"path": [ "path": [
"{{application}}", "{{application}}",
"auth", "keycloak"
"member"
], ],
"query": [ "query": [
{ {
"key": "gcube-token", "key": "client_id",
"value": "{{gcube_token}}" "value": "{{current_client-id}}",
"disabled": true
} }
] ]
} }
@ -259,6 +297,16 @@
"response": [] "response": []
} }
], ],
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "{{access_token}}",
"type": "string"
}
]
},
"event": [ "event": [
{ {
"listen": "prerequest", "listen": "prerequest",
@ -285,20 +333,48 @@
"value": "http://localhost:8081", "value": "http://localhost:8081",
"type": "string" "type": "string"
}, },
{
"key": "keycloak_url",
"value": "https://accounts.dev.d4science.org/auth",
"type": "string"
},
{
"key": "url-encoded-context",
"value": "%2Fgcube%2Fdevsec%2FdevVRE",
"type": "string"
},
{
"key": "realm",
"value": "d4science",
"type": "string"
},
{
"key": "client-id-user",
"value": "next.d4science.org",
"type": "string"
},
{ {
"key": "application", "key": "application",
"value": "helloworld", "value": "helloworld",
"type": "string" "type": "string"
}, },
{ {
"key": "gcube_token", "key": "username",
"value": "", "value": "",
"type": "string" "type": "string"
}, },
{ {
"key": "uma_token", "key": "password",
"value": "", "value": "",
"type": "string" "type": "string"
},
{
"key": "access_token",
"value": ""
},
{
"key": "refresh_token",
"value": ""
} }
] ]
} }

64
pom.xml
View File

@ -24,6 +24,10 @@
<!-- OPTIONAL. for authorization-control-library --> <!-- OPTIONAL. for authorization-control-library -->
<aspectj-plugin.version>1.14.0</aspectj-plugin.version> <aspectj-plugin.version>1.14.0</aspectj-plugin.version>
<!-- bind jackson version to avoid inconsistences with keycloak dependencies -->
<jackson.version>2.15.3</jackson.version>
</properties> </properties>
<scm> <scm>
@ -49,12 +53,55 @@
<dependencies> <dependencies>
<!-- <dependency> <!--
<groupId>org.keycloak</groupId> Keycloak use a older version of jackson (2.12.3).
<artifactId>keycloak-admin-client-jakarta</artifactId> some jackson libraries are imported only by keyclock,
<version>21.1.2</version> not by smartgears so it mixed different versions.
</dependency> We explicity import its jackson dependency to ovverride the version
--> -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-base</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jaxb-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>jakarta.activation</groupId>
<artifactId>jakarta.activation-api</artifactId>
<version>2.1.2</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-admin-client</artifactId>
<version>21.1.2</version>
<exclusions>
<exclusion>
<groupId>org.jboss.spec.javax.ws.rs</groupId>
<artifactId>jboss-jaxrs-api_3.0_spec</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency> <dependency>
<groupId>org.aspectj</groupId> <groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId> <artifactId>aspectjrt</artifactId>
@ -91,7 +138,6 @@
<artifactId>jersey-cdi2-se</artifactId> <artifactId>jersey-cdi2-se</artifactId>
</dependency> </dependency>
<!-- add jackson as json provider --> <!-- add jackson as json provider -->
<dependency> <dependency>
<groupId>org.glassfish.jersey.media</groupId> <groupId>org.glassfish.jersey.media</groupId>
@ -110,7 +156,8 @@
<!-- OPTIONAL generate the war in a different folder, that will be <!-- OPTIONAL generate the war in a different folder, that will be
mounted on docker mounted on docker
container --> container -->
<!-- <plugin> <!--
<plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId> <artifactId>maven-war-plugin</artifactId>
<version>2.3</version> <version>2.3</version>
@ -118,7 +165,8 @@
<warName>identity-manager</warName> <warName>identity-manager</warName>
<outputDirectory>tomcat/webapps</outputDirectory> <outputDirectory>tomcat/webapps</outputDirectory>
</configuration> </configuration>
</plugin> --> </plugin>
-->
<!-- OPTIONAL. authorization-control-library: add this plugin if you <!-- OPTIONAL. authorization-control-library: add this plugin if you
want to use gcube want to use gcube

View File

@ -1,5 +1,7 @@
package org.gcube.service.helloworld.serializers; package org.gcube.service.helloworld.serializers;
import org.gcube.common.resources.gcore.ServiceEndpoint;
import org.gcube.common.security.Owner;
import org.gcube.smartgears.configuration.container.ContainerConfiguration; import org.gcube.smartgears.configuration.container.ContainerConfiguration;
import org.gcube.smartgears.context.container.ContainerContext; import org.gcube.smartgears.context.container.ContainerContext;
import org.gcube.smartgears.security.SimpleCredentials; import org.gcube.smartgears.security.SimpleCredentials;
@ -14,7 +16,7 @@ import com.fasterxml.jackson.databind.module.SimpleModule;
* *
*/ */
public class ContextSerializator { public class CustomSerializator {
private static ObjectMapper serializer = null; private static ObjectMapper serializer = null;
public static ObjectMapper getSerializer() { public static ObjectMapper getSerializer() {
@ -24,7 +26,9 @@ public class ContextSerializator {
// module.addSerializer(Owner.class, new OwnerSerializer()); // module.addSerializer(Owner.class, new OwnerSerializer());
module.addSerializer(ContainerConfiguration.class, new ContainerConfigurationSerializer()); module.addSerializer(ContainerConfiguration.class, new ContainerConfigurationSerializer());
module.addSerializer(Owner.class, new OwnerSerializer());
module.addSerializer(ContainerContext.class, new ContainerContextSerializer()); module.addSerializer(ContainerContext.class, new ContainerContextSerializer());
module.addSerializer(ServiceEndpoint.AccessPoint.class, new ServiceEndpointAccessPointSerializer());
module.addSerializer(SimpleCredentials.class, new SimpleCredentialsSerializer()); module.addSerializer(SimpleCredentials.class, new SimpleCredentialsSerializer());
om.registerModule(module); om.registerModule(module);

View File

@ -0,0 +1,64 @@
package org.gcube.service.helloworld.serializers;
import java.io.IOException;
import org.gcube.common.encryption.encrypter.StringEncrypter;
import org.gcube.common.resources.gcore.ServiceEndpoint;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
/**
* Jackson serializer for ServiceEndpoint.AccessPoint
*
* @author Alfredo Oliviero (ISTI-CNR)
*
*/
public class ServiceEndpointAccessPointSerializer extends StdSerializer<ServiceEndpoint.AccessPoint> {
protected ServiceEndpointAccessPointSerializer(Class<ServiceEndpoint.AccessPoint> t) {
super(t);
}
public ServiceEndpointAccessPointSerializer() {
super(ServiceEndpoint.AccessPoint.class, true);
}
@Override
public void serialize(ServiceEndpoint.AccessPoint accessPoint, JsonGenerator jgen, SerializerProvider provider)
throws IOException {
jgen.writeStartObject();
String error = null;
String clientSecret = null;
try {
clientSecret = StringEncrypter.getEncrypter().decrypt(accessPoint.password());
} catch (Exception e) {
error = e.getMessage();
}
jgen.writeStringField("address", accessPoint.address());
jgen.writeStringField("name", accessPoint.name());
jgen.writeStringField("description", accessPoint.description());
try {
jgen.writeStringField("username", accessPoint.username());
} catch (Exception e) {
jgen.writeStringField("username", null);
}
try {
jgen.writeStringField("tostring", accessPoint.toString());
} catch (Exception e) {
jgen.writeStringField("tostring", null);
}
if (error != null) {
jgen.writeStringField("key_error", error);
jgen.writeBooleanField("key_decoded", false);
} else {
jgen.writeBooleanField("key_decoded", true);
}
jgen.writeEndObject();
}
}

View File

@ -13,7 +13,7 @@ import org.gcube.common.security.Owner;
import org.gcube.common.security.providers.SecretManagerProvider; import org.gcube.common.security.providers.SecretManagerProvider;
import org.gcube.common.security.secrets.Secret; import org.gcube.common.security.secrets.Secret;
import org.gcube.service.helloworld.HelloWorldManager; import org.gcube.service.helloworld.HelloWorldManager;
import org.gcube.service.helloworld.serializers.ContextSerializator; import org.gcube.service.helloworld.serializers.CustomSerializator;
import org.gcube.smartgears.ContextProvider; import org.gcube.smartgears.ContextProvider;
import org.gcube.smartgears.annotations.ManagedBy; import org.gcube.smartgears.annotations.ManagedBy;
import org.gcube.smartgears.context.container.ContainerContext; import org.gcube.smartgears.context.container.ContainerContext;
@ -70,7 +70,7 @@ public class HelloService {
ContainerContext container = ContextProvider.get().container(); ContainerContext container = ContextProvider.get().container();
data.put("container", container); data.put("container", container);
ObjectMapper objectMapper = ContextSerializator.getSerializer(); ObjectMapper objectMapper = CustomSerializator.getSerializer();
try { try {
String jsonData = objectMapper.writeValueAsString(data); String jsonData = objectMapper.writeValueAsString(data);

View File

@ -0,0 +1,95 @@
package org.gcube.service.helloworld.services;
import java.util.List;
import org.gcube.common.resources.gcore.ServiceEndpoint.AccessPoint;
import org.gcube.service.helloworld.HelloWorldManager;
import org.gcube.service.helloworld.serializers.CustomSerializator;
import org.gcube.service.helloworld.utils.InfrastrctureServiceClient;
import org.gcube.smartgears.annotations.ManagedBy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Response;
/**
* service example that shows how to query IS and how to access Keycloak
*
* @author Alfredo Oliviero (ISTI - CNR)
*/
@ManagedBy(HelloWorldManager.class)
@Path("is")
public class IsService {
private final static Logger logger = LoggerFactory.getLogger(IsService.class);
private final static String RUNTIME_RESOURCE_NAME = "IAM";
private final static String CATEGORY = "Service";
private final static String REALM_D4S = "d4science";
private final static boolean IS_ROOT_SERVICE = true;
public static String getClientIdContext(String context) {
return context.replace("/", "%2F");
}
@GET
@Path("/test")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response test(
@QueryParam("resource") @DefaultValue(RUNTIME_RESOURCE_NAME) String resourceName,
@QueryParam("category") @DefaultValue(CATEGORY) String categoryName,
@QueryParam("realm") @DefaultValue(REALM_D4S) String realm,
@QueryParam("is_root") @DefaultValue("" + IS_ROOT_SERVICE) boolean is_root) {
return this.queryIS(resourceName, categoryName, realm, is_root);
}
@GET
@Path("/query")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response queryIS(
@QueryParam("resource") String resourceName,
@QueryParam("category") String categoryName,
@QueryParam("realm") @DefaultValue(REALM_D4S) String realm,
@QueryParam("is_root") @DefaultValue("false") boolean is_root) {
try {
List<AccessPoint> accessPoints = InfrastrctureServiceClient.getAccessPointsFromIS(
resourceName,
categoryName,
realm,
is_root);
if (accessPoints.size() == 0) {
String error_log = "Unable to retrieve service endpoint " + REALM_D4S;
logger.error(error_log);
throw new NotFoundException(error_log);
}
ObjectMapper objectMapper = CustomSerializator.getSerializer();
String jsonData = objectMapper.writeValueAsString(accessPoints);
return Response.ok(jsonData).build();
} catch (JsonProcessingException e) {
e.printStackTrace();
return Response.serverError().build();
} catch (Exception e) {
e.printStackTrace();
return Response.serverError().build();
}
}
}

View File

@ -0,0 +1,166 @@
package org.gcube.service.helloworld.services;
import java.util.ArrayList;
import java.util.List;
import org.gcube.common.encryption.encrypter.StringEncrypter;
import org.gcube.common.resources.gcore.ServiceEndpoint;
import org.gcube.service.helloworld.HelloWorldManager;
import org.gcube.service.helloworld.serializers.CustomSerializator;
import org.gcube.service.helloworld.utils.InfrastrctureServiceClient;
import org.gcube.smartgears.annotations.ManagedBy;
import org.keycloak.OAuth2Constants;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.KeycloakBuilder;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.ClientsResource;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.RoleResource;
import org.keycloak.admin.client.resource.RolesResource;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.core.Response;
/**
* service example that shows how to query IS and how to access Keycloak
* @author Alfredo Oliviero (ISTI - CNR)
*/
@ManagedBy(HelloWorldManager.class)
@Path("")
public class KeycloakTestService {
private final static Logger logger = LoggerFactory.getLogger(KeycloakTestService.class);
private final static String RUNTIME_RESOURCE_NAME = "IAM";
private final static String CATEGORY = "Service";
private final static String REALM_D4S = "d4science";
private final static boolean IS_ROOT_SERVICE = true;
public static String getClientIdContext(String context) {
return context.replace("/", "%2F");
}
@GET
@Path("/keycloak")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response testKeycloak(
@QueryParam("role-name") @DefaultValue("Member") String roleName) {
String client_contenxt = "/gcube";
List<String> usernames = new ArrayList<String>();
try {
ServiceEndpoint.AccessPoint accessPoint = InfrastrctureServiceClient.getAccessPointFromIS(
RUNTIME_RESOURCE_NAME,
CATEGORY,
REALM_D4S,
IS_ROOT_SERVICE);
if (accessPoint == null) {
String error_log = "Unable to retrieve service endpoint " + REALM_D4S;
logger.error(error_log);
throw new NotFoundException(error_log);
}
String keycloakURL = accessPoint.address();
String realm = accessPoint.name();
String clientId = accessPoint.username();
String clientSecret = StringEncrypter.getEncrypter().decrypt(accessPoint.password());
Keycloak kclient = KeycloakBuilder.builder()
.serverUrl(keycloakURL)
.realm(realm)
.grantType(OAuth2Constants.CLIENT_CREDENTIALS)
.clientId(clientId) //
.clientSecret(clientSecret).build();
List<UserRepresentation> users = searchByRole(kclient, realm, client_contenxt, roleName);
if (users != null) {
for (UserRepresentation user : users) {
usernames.add(user.getUsername());
}
}
// responseBean.setResult(usernames);
// responseBean.setSuccess(true);
ObjectMapper objectMapper = CustomSerializator.getSerializer();
String jsonData = objectMapper.writeValueAsString(usernames);
return Response.ok(jsonData).build();
} catch (JsonProcessingException e) {
e.printStackTrace();
return Response.serverError().build();
} catch (Exception e) {
e.printStackTrace();
return Response.serverError().build();
}
// return Response.status(status).entity(responseBean).build();
}
private static List<UserRepresentation> searchByRole(Keycloak kclient, String krealm, String clientIdContext,
String roleName) {
clientIdContext = getClientIdContext(clientIdContext);
logger.info("Searching by role: {}", roleName);
RealmResource realm_resource = kclient.realm(krealm);
logger.info("{} realm_resource: {}", krealm, realm_resource);
ClientsResource clients_resource = realm_resource.clients();
logger.info("clients_resource {}", clients_resource);
for (ClientRepresentation c : clients_resource.findAll()) {
logger.info("listing all clients, found {} - {}", c.getClientId(), c.getId());
}
List<ClientRepresentation> clients_repr = clients_resource.findByClientId(clientIdContext);
logger.info("{} clients_repr: {}", clientIdContext, clients_repr);
String client_id = "";
for (ClientRepresentation c_repr : clients_repr) {
logger.info("searching {}, found client {} - {}", clientIdContext, c_repr.getClientId(), c_repr.getId());
client_id = c_repr.getId();
}
ClientResource client_resource = clients_resource.get(client_id);
logger.info("client_resource {}", client_resource);
RolesResource roles_resource = client_resource.roles();
for (RoleRepresentation r : roles_resource.list()) {
logger.info("found role {}", r);
}
logger.info("roles_resource {}", roles_resource);
RoleResource role_resource = roles_resource.get(roleName);
logger.info("{} role_resource: {}", roleName, roles_resource);
List<UserRepresentation> users_repr = role_resource.getUserMembers(0, 100000);
for (UserRepresentation u : users_repr) {
logger.info("found user {}", u);
}
return users_repr;
}
}

View File

@ -0,0 +1,126 @@
package org.gcube.service.helloworld.utils;
import static org.gcube.resources.discovery.icclient.ICFactory.clientFor;
import static org.gcube.resources.discovery.icclient.ICFactory.queryFor;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
import org.gcube.common.resources.gcore.ServiceEndpoint;
// import org.gcube.common.scope.api.ScopeProvider;
import org.gcube.common.security.AuthorizedTasks;
import org.gcube.common.security.secrets.Secret;
import org.gcube.resources.discovery.client.api.DiscoveryClient;
import org.gcube.resources.discovery.client.queries.api.SimpleQuery;
import org.gcube.smartgears.ContextProvider;
import org.gcube.smartgears.context.application.ApplicationContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Utility class to query EndPoints and search for AccessPoints from IS
* @author Alfredo Oliviero (ISTI - CNR)
*/
public class InfrastrctureServiceClient {
private static final Logger logger = LoggerFactory.getLogger(InfrastrctureServiceClient.class);
/**
* Retrieve endpoints information from IS for DB
*
* @return list of endpoints for ckan database
* @throws Throwable
*/
public static List<ServiceEndpoint> getEndopintsFromIS(String resource_name, String category,
boolean root_service) {
SimpleQuery query = queryFor(ServiceEndpoint.class);
if (resource_name != null) {
query.addCondition("$resource/Profile/Name/text() eq '" + resource_name + "'");
}
if (category != null){
query.addCondition("$resource/Profile/Category/text() eq '" + category + "'");
}
DiscoveryClient<ServiceEndpoint> client = clientFor(ServiceEndpoint.class);
ApplicationContext ctx = ContextProvider.get();
String infra_context = "/" + ctx.container().configuration().infrastructure();
Secret secret = ctx.container().authorizationProvider().getSecretForContext(infra_context);
List<ServiceEndpoint> endpoints = null;
try {
if (root_service) {
endpoints = AuthorizedTasks.executeSafely(() -> {
// esegui la query
List<ServiceEndpoint> toReturn = client.submit(query);
return toReturn;
}, secret);
} else {
endpoints = client.submit(query);
}
} catch (Throwable e) {
e.printStackTrace();
}
return endpoints;
}
/**
* @param resource_name
* @param category
* @param accessPointName
* @return
* @throws Exception
*/
public static List<ServiceEndpoint.AccessPoint> getAccessPointsFromIS(String resource_name, String category,
String accessPointName, boolean root_service) throws Exception {
List<ServiceEndpoint> resources = getEndopintsFromIS(resource_name, category, root_service);
if (resources.size() == 0) {
logger.error("There is no Runtime Resource having name " + resource_name + " and Category "
+ category + " in this scope.");
return null;
}
List<ServiceEndpoint.AccessPoint> response = new ArrayList<ServiceEndpoint.AccessPoint>();
resources.forEach(res -> {
Stream<ServiceEndpoint.AccessPoint> access_points_res = res.profile().accessPoints().stream();
if (accessPointName == null) {
access_points_res = access_points_res.filter(ap -> ap.name().equals(accessPointName));
}
access_points_res.forEach(a -> response.add(a));
});
return response;
}
/**
* @param resource_name
* @param category
* @param accessPointName
* @return
* @throws Exception
*/
public static ServiceEndpoint.AccessPoint getAccessPointFromIS(String resource_name, String category,
String accessPointName, boolean root_service) throws Exception {
List<ServiceEndpoint.AccessPoint> access_points = getAccessPointsFromIS(resource_name, category, accessPointName, root_service);
if (access_points.size() == 0) {
logger.error("Unable to retrieve service endpoint " + accessPointName);
return null;
}
return access_points.get(0);
}
}

View File

@ -8,8 +8,7 @@ import org.gcube.common.security.Owner;
import org.gcube.smartgears.context.container.ContainerContext; import org.gcube.smartgears.context.container.ContainerContext;
/** /**
* * @author Alfredo Oliviero (ISTI - CNR)
* @author Lucio Lelii (ISTI-CNR)
*/ */
public class RestUtils { public class RestUtils {