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/container.ini /etc/
COPY ./docker/*.gcubekey /tomcat/lib
COPY ./target/helloworld.war /tomcat/webapps/
EXPOSE 8080

View File

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

View File

@ -1,26 +1,36 @@
#!/bin/bash
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()
{
# Display Help
echo "build, create and run in docker the helloworld service"
echo
echo "Syntax: buildDistribution [-n arg] [-p arg] [-j arg] [-d arg?] [-h]"
echo "options:"
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 "-j arg specify java version (default is 11)"
echo " accepted version are: ${accepted_java_versions[@]}"
echo "-d arg? enable java debug mode"
echo " arg is the debug port (default is 5005)"
echo "-h Print this Help."
echo
Help() {
# Display Help
echo "build, create and run in docker the helloworld service"
echo
echo "Syntax: buildDistribution [-n arg] [-p arg] [-j arg] [-d arg?] [-h]"
echo "options:"
echo "-s skip maven package"
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 $PORT)"
echo "-j arg specify java version (default is $java_version)"
echo " accepted version are: ${accepted_java_versions[@]}"
echo "-d arg? enable java debug mode"
echo " arg is the debug port (default is $DEBUG_PORT)"
echo "-h Print this Help."
echo
}
################################################################################
@ -31,38 +41,63 @@ Help()
set -e
OPTSTRING=":sn:p:d:j:?h"
NAME=smartgears-helloworld
PORT=8081
DEBUG_PORT=5005
debug=false
compile=false
java_version=11
while getopts n:p:j:d?h flag
do
echo ${flag};
case "${flag}" in
n) NAME=${OPTARG};;
p) PORT=${OPTARG};;
d) debug=true && DEBUG_PORT=${OPTARG:-5005};;
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 ;;
*) echo "Invalid option" && exit 1 ;;
while getopts $OPTSTRING opt; do
# echo "Option -${opt} was triggered, Argument: ${OPTARG}"
case "${opt}" in
s) COMPILE=false && echo "compile $COMPILE" ;;
n) NAME=${OPTARG} ;;
p) PORT=${OPTARG} ;;
d) DEBUG=true
DEBUG_PORT=${OPTARG}
echo "debug enabled, port $DEBUG_PORT"
;;
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
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
mvn clean package
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
else
echo "skipping mvn package"
fi
docker build -t $NAME --build-arg JAVA_VERSION=${java_version} .
if [ $debug = false ] ; then
docker run -p $PORT:8080 $NAME
if [ $DEBUG = false ]; then
docker run -p $PORT:8080 $NAME
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

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": [
{
"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": {
"auth": {
"type": "noauth"
},
"method": "GET",
"method": "POST",
"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": "",
"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"
}
],
"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": {
"raw": "{{base_url}}/{{application}}/hello?gcube-token={{gcube_token}}",
"raw": "{{keycloak_url}}/realms/{{realm}}/protocol/openid-connect/token",
"host": [
"{{base_url}}"
"{{keycloak_url}}"
],
"path": [
"{{application}}",
"hello"
],
"query": [
{
"key": "gcube-token",
"value": "{{gcube_token}}"
}
"realms",
"{{realm}}",
"protocol",
"openid-connect",
"token"
]
}
},
"response": []
},
{
"name": "hello BEARER",
"name": "hello",
"request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "{{uma_token}}",
"type": "string"
}
]
},
"method": "GET",
"header": [],
"url": {
@ -69,45 +188,8 @@
"response": []
},
{
"name": "details TOKEN PARAM",
"name": "details",
"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",
"header": [],
"url": {
@ -123,6 +205,25 @@
},
"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",
"request": {
@ -145,75 +246,8 @@
"response": []
},
{
"name": "auth org member BEARER",
"name": "auth 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 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",
"header": [],
"url": {
@ -231,27 +265,31 @@
"response": []
},
{
"name": "auth member TOKEN PARAM",
"name": "test keycloak",
"request": {
"auth": {
"type": "noauth"
},
"method": "GET",
"header": [],
"header": [
{
"key": "gcube-token",
"value": "{{gcube_token}}",
"type": "text",
"disabled": true
}
],
"url": {
"raw": "{{base_url}}/{{application}}/auth/member?gcube-token={{gcube_token}}",
"raw": "{{base_url}}/{{application}}/keycloak",
"host": [
"{{base_url}}"
],
"path": [
"{{application}}",
"auth",
"member"
"keycloak"
],
"query": [
{
"key": "gcube-token",
"value": "{{gcube_token}}"
"key": "client_id",
"value": "{{current_client-id}}",
"disabled": true
}
]
}
@ -259,6 +297,16 @@
"response": []
}
],
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "{{access_token}}",
"type": "string"
}
]
},
"event": [
{
"listen": "prerequest",
@ -285,20 +333,48 @@
"value": "http://localhost:8081",
"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",
"value": "helloworld",
"type": "string"
},
{
"key": "gcube_token",
"key": "username",
"value": "",
"type": "string"
},
{
"key": "uma_token",
"key": "password",
"value": "",
"type": "string"
},
{
"key": "access_token",
"value": ""
},
{
"key": "refresh_token",
"value": ""
}
]
}

66
pom.xml
View File

@ -24,6 +24,10 @@
<!-- OPTIONAL. for authorization-control-library -->
<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>
<scm>
@ -48,13 +52,56 @@
</dependencyManagement>
<dependencies>
<!-- <dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-admin-client-jakarta</artifactId>
<version>21.1.2</version>
</dependency>
<!--
Keycloak use a older version of jackson (2.12.3).
some jackson libraries are imported only by keyclock,
not by smartgears so it mixed different versions.
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>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
@ -91,7 +138,6 @@
<artifactId>jersey-cdi2-se</artifactId>
</dependency>
<!-- add jackson as json provider -->
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
@ -110,7 +156,8 @@
<!-- OPTIONAL generate the war in a different folder, that will be
mounted on docker
container -->
<!-- <plugin>
<!--
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.3</version>
@ -118,7 +165,8 @@
<warName>identity-manager</warName>
<outputDirectory>tomcat/webapps</outputDirectory>
</configuration>
</plugin> -->
</plugin>
-->
<!-- OPTIONAL. authorization-control-library: add this plugin if you
want to use gcube

View File

@ -1,5 +1,7 @@
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.context.container.ContainerContext;
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;
public static ObjectMapper getSerializer() {
@ -24,7 +26,9 @@ public class ContextSerializator {
// module.addSerializer(Owner.class, new OwnerSerializer());
module.addSerializer(ContainerConfiguration.class, new ContainerConfigurationSerializer());
module.addSerializer(Owner.class, new OwnerSerializer());
module.addSerializer(ContainerContext.class, new ContainerContextSerializer());
module.addSerializer(ServiceEndpoint.AccessPoint.class, new ServiceEndpointAccessPointSerializer());
module.addSerializer(SimpleCredentials.class, new SimpleCredentialsSerializer());
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.secrets.Secret;
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.annotations.ManagedBy;
import org.gcube.smartgears.context.container.ContainerContext;
@ -70,7 +70,7 @@ public class HelloService {
ContainerContext container = ContextProvider.get().container();
data.put("container", container);
ObjectMapper objectMapper = ContextSerializator.getSerializer();
ObjectMapper objectMapper = CustomSerializator.getSerializer();
try {
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;
/**
*
* @author Lucio Lelii (ISTI-CNR)
* @author Alfredo Oliviero (ISTI - CNR)
*/
public class RestUtils {