Compare commits

...

52 Commits

Author SHA1 Message Date
Alfredo Oliviero 4ea0762f7f moved to common library 2024-05-08 17:59:00 +02:00
Alfredo Oliviero 881a26c5e7 push on harbor and docker 2024-05-08 17:52:47 +02:00
Alfredo Oliviero 0a930c2ba1 removed unused dependency 2024-05-08 17:51:21 +02:00
Alfredo Oliviero 5fe4909050 moved rest examples 2024-05-07 15:09:23 +02:00
Alfredo Oliviero a478ab08c4 implemented idm-common library 2024-05-02 17:00:44 +02:00
Alfredo Oliviero 6ccc1cd9ba moved beans to commons 2024-04-22 18:04:59 +02:00
Alfredo Oliviero 5ad8978d15 included idm-common 2024-04-22 17:54:09 +02:00
Alfredo Oliviero febe596ff7 rename 2024-04-22 17:53:06 +02:00
Alfredo Oliviero 82e3012296 rename 2024-04-22 17:52:43 +02:00
Alfredo Oliviero 37d0426b77 removed unused utils classes 2024-04-22 17:24:06 +02:00
Alfredo Oliviero 13ee1b5e91 clients 2024-04-22 15:32:53 +02:00
Alfredo Oliviero 5785c5a712 moved implementation 2024-04-19 18:02:05 +02:00
Alfredo Oliviero 1d4dc2c27d format of users, roles, groups, clients. implementation of ClientAPIs 2024-04-19 17:22:37 +02:00
Alfredo Oliviero 866c9b2d9a code format 2024-04-17 20:59:23 +02:00
Alfredo Oliviero ae67b28d16 revieved implementation, javadoc and enunciate documentation 2024-04-17 20:53:55 +02:00
Alfredo Oliviero cd3875360c enunviate v2.16.1
enunviate v2.16.1
2024-04-17 16:37:03 +02:00
Alfredo Oliviero 35689417c0 api rest for users, clients, social. implemented the integration of liferay client for user profile 2024-04-17 16:05:10 +02:00
Alfredo Oliviero d995edb5cd config 2024-04-17 16:04:10 +02:00
Alfredo Oliviero f07ba0e2a5 gitignore 2024-04-17 16:03:02 +02:00
Alfredo Oliviero 02d1e3df06 enunciate 2024-04-17 15:56:52 +02:00
Alfredo Oliviero a9073c1f60 configurations 2024-04-17 15:55:10 +02:00
Alfredo Oliviero 971011fa56 config container 2024-04-08 12:29:58 +02:00
Alfredo Oliviero 921bcd45cf implemented Users and Roles Rest APIs 2024-03-29 18:20:41 +01:00
Alfredo Oliviero bc534cc6cb config keycloak client 2024-03-28 15:40:09 +01:00
Alfredo Oliviero 9508b7329c code formatting 2024-03-26 17:41:52 +01:00
Alfredo Oliviero c4786572ab paginated bean result 2024-03-26 17:40:21 +01:00
Alfredo Oliviero 745e3b7425 jwt decode service 2024-03-26 17:37:21 +01:00
Alfredo Oliviero 365716ba77 working client 2024-03-19 14:00:34 +01:00
Alfredo Oliviero c5e9810fc0 reorganized packages 2024-03-19 14:00:34 +01:00
Alfredo Oliviero 87500802bf removed metadata-complete=true from web.xml \n\nfixed https://support.d4science.org/issues/26888 2024-03-19 14:00:34 +01:00
Alfredo Oliviero c016637b38 aligned to hello-world-sg4-service 2024-03-19 14:00:34 +01:00
Alfredo Oliviero d6257cf520 reorganized packages 2024-03-19 14:00:34 +01:00
Alfredo Oliviero 2fc47d8d21 code format 2024-03-19 14:00:33 +01:00
Alfredo Oliviero 051c448e87 updated smartgears libraries 2024-03-19 14:00:33 +01:00
lucio ba1db21254 web.xml moved in WEB-INF lib 2024-03-19 14:00:33 +01:00
Alfredo Oliviero 322dd3c8f4 modificato con lucio 2024-03-19 14:00:33 +01:00
Alfredo Oliviero 8f97369935 config 2024-03-19 14:00:33 +01:00
Alfredo Oliviero dff1bb1485 semplificato 2024-03-19 14:00:33 +01:00
Alfredo Oliviero 3ba601922d deploy docker 2024-02-16 16:39:34 +01:00
Alfredo Oliviero 4bd8a871b4 working app 2024-02-16 16:38:15 +01:00
Alfredo Oliviero 5ca788b801 docker config 2024-02-16 12:24:02 +01:00
Alfredo Oliviero 753828ff25 minimized code to resolve resteasy conflict 2024-02-16 12:20:40 +01:00
Alfredo Oliviero 165cec80b2 semplificato al massimo per provare a far partire. continua a non esporre l-unico metodo rest,e a non ottenere un context collegato a clientid 2024-02-14 11:32:09 +01:00
Alfredo Oliviero 8b5450e5ea remote debug 2024-02-13 14:51:31 +01:00
Alfredo Oliviero 9c6fa54d97 updated path and config 2024-02-13 14:50:45 +01:00
Alfredo Oliviero 182f890e9c config per debug in docker 2024-02-12 17:43:30 +01:00
Alfredo Oliviero 8650ed6451 documentaione 2024-02-12 17:42:49 +01:00
Alfredo Oliviero 0f4283a1b5 fixed rest paths 2024-02-12 17:41:50 +01:00
Alfredo Oliviero 7d2e3e3fc1 documentazione e postman 2024-02-12 17:41:03 +01:00
Alfredo Oliviero 5b9bc3d446 fixed typo, keycloack insted of keycloak 2024-02-08 17:44:40 +01:00
Alfredo Oliviero 6ffd1975db postman collection 2024-02-08 17:21:24 +01:00
Alfredo Oliviero 4fb92dff90 idm service configured 2024-02-08 11:57:26 +01:00
87 changed files with 16897 additions and 2113 deletions

5
.gitignore vendored
View File

@ -1,4 +1,7 @@
# originale
#
ignore_me
tomcat
target
.classpath
.visual

18
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,18 @@
{
// Usare IntelliSense per informazioni sui possibili attributi.
// Al passaggio del mouse vengono visualizzate le descrizioni degli attributi esistenti.
// Per altre informazioni, visitare: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "java",
"name": "Debug (Attach)",
"projectName": "idm-service",
"request": "attach",
"hostName": "localhost",
"port": 5005
}
]
}

20
.vscode/settings.json vendored
View File

@ -1,4 +1,22 @@
{
"java.compile.nullAnalysis.mode": "automatic",
"java.configuration.updateBuildConfiguration": "automatic"
"java.configuration.updateBuildConfiguration": "automatic",
"maven.view": "hierarchical",
"maven.terminal.favorites": [
{
"command": "clean package",
"debug": false
}
],
"java.dependency.packagePresentation": "hierarchical",
"Workspace_Formatter.excludePattern": [
"**/build",
"**/.*",
"**/.vscode",
"**/docs/*",
"**/documentazione/*",
"**/target/*",
"**/tomcat/*",
"**/webapps/*"
]
}

View File

@ -3,7 +3,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
# Changelog for Identity Manager Service
## [v1.0.0-SNAPSHOT]
## [v0.0.1-SNAPSHOT]
- First Version
- First Prototype

View File

@ -1,19 +1,14 @@
#FROM d4science/smartgears-distribution:4.0.0-SNAPSHOT-java11-tomcat9
FROM smartgears-distribution:4.0.0-java11-tomcat9
ARG JAVA_VERSION=11
ARG SMARTGEARS_VERSION=4.0.0-SNAPSHOT
#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
#FROM smartgears-distribution:4.0.0-SNAPSHOT-java$JAVA_VERSION-tomcat10.1.19
ARG CONTAINER_INI="./docker/container.ini"
ARG REPOUSER=admin
ARG REPOPWD=admin
# COPY ./target/identity-manager.war /tomcat/webapps/
COPY ./docker/logback.xml /etc/
COPY ${CONTAINER_INI} /etc/container.ini
COPY ./docker/*.gcubekey /tomcat/lib
COPY ./target/idm-service.war /tomcat/webapps/
COPY ./target/identity-manager.war /usr/local/tomcat/webapps/
# COPY ./docker/storagehub.xml /tomcat/conf/Catalina/localhost/
COPY ./docker/logback.xml /etc/
COPY ./docker/container.ini /etc/
# RUN unzip /tomcat/webapps/identity-manager.war -d /tomcat/webapps/identity-manager
RUN unzip /usr/local/tomcat/webapps/identity-manager.war -d /usr/local/tomcat/webapps/identity-manager
RUN rm /usr/local/tomcat/webapps/identity-manager.war
EXPOSE 8080
# COPY ./docker/storage-settings.properties /tomcat/webapps/identity-manager/WEB-INF/classes/
# RUN sed -i "s/{{adminId}}/$REPOUSER/g; s/{{adminPwd}}/$REPOPWD/g" /tomcat/webapps/storagehub/WEB-INF/web.xml
EXPOSE 8080

View File

@ -11,6 +11,8 @@ This service allows any client to publish on the gCube Catalogue.
[Identity Manager Service](https://wiki.gcube-system.org/gcube/SmartGears)
configuration: inserte
## Change log
See [CHANGELOG.md](CHANGELOG.md).
@ -18,7 +20,6 @@ See [CHANGELOG.md](CHANGELOG.md).
## Authors
* **Alfredo Oliviero** [ISTI-CNR Infrascience Group](http://nemis.isti.cnr.it/groups/infrascience)
* **Luca Frosini** ([ORCID](https://orcid.org/0000-0003-3183-2291)) - [ISTI-CNR Infrascience Group](http://nemis.isti.cnr.it/groups/infrascience)
## How to Cite this Software

21
api.md
View File

@ -1,21 +0,0 @@
<!-- // da implementare rispetto al contesto -->
/2/users/get-profile // profilo utente corrente
/2/users/get-email // utente corrente
/2/users/get-fullname // utente corrente
/2/users/get-all-usernames
/2/users/get-all-fullnames-and-usernames
/2/users/get-usernames-by-role
/2/users/user-exists
// attenzione al risultato. vedere in seguito
/2/users/get-oauth-profile
// eventualemente in seguito. da approfondire
/2/users/get-custom-attribute
/2/users/get-usernames-by-global-role
rif. https://api.dev.d4science.org/social-networking-library-ws/api-docs/index.html
implementazione social: https://code-repo.d4science.org/gCubeSystem/social-networking-library-ws/src/branch/master/src/main/java/org/gcube/portal/social/networking/ws/methods/v2/Users.java

162
buildImageAndStart.sh Executable file
View File

@ -0,0 +1,162 @@
#!/bin/bash
# set -x # uncomment to debug script
ACCEPTED_JAVA_VERSIONs=(11 17)
NAME=idm-service
BUILD_VERSION=0.0.1-SNAPSHOT
SMARTGEARS_VERSION=4.0.1-SNAPSHOT
CONTAINER_INI="./docker/container.ini"
IMAGE_VERSION=${BUILD_VERSION}-java${JAVA_VERSION}-smartgears${SMARTGEARS_VERSION}
PORT=8080
DEBUG_PORT=5005
DEBUG=false
EXECUTE=false
TEST=false
COMPILE=true
JAVA_VERSION=11
PUSH_DOCKER=false
PUSH_HARBOR=false
LOGIN_HARBOR=false
BUILD_NAME=$NAME:$IMAGE_VERSION
echo "BUILD_NAME=$BUILD_NAME"
################################################################################
# Help #
################################################################################
Help() {
# Display Help
echo "build, create and run in docker the identity manager service"
echo
echo "Syntax: buildDistribution [-n arg] [-p arg] [-j arg] [-d arg?] [-h]"
echo "options:"
echo "-s skip maven package"
echo "-t exec also maven tests"
echo "-n arg specifies the docker image name (default is identity-manager)."
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 "-e execute the image"
echo "-d arg? enable java debug mode for execution"
echo " arg is the debug port (default is $DEBUG_PORT)"
echo "-r push image to d4science harbo[r] (with login already done, or -l to login)"
echo "-l [l]ogin to d4science harbor"
echo "-u p[u]sh image to dockerhub (with docker login already done)"
echo "-c arg path of the file to deploy as container.ini (default ./docker/container.ini)"
echo "-h Print this Help."
echo
}
################################################################################
################################################################################
# Main program #
################################################################################
################################################################################
set -e
OPTSTRING=":slmurn:p:c:ed:j:?h"
while getopts $OPTSTRING opt; do
# echo "Option -${opt} was triggered, Argument: ${OPTARG}"
case "${opt}" in
s) COMPILE=false && echo "compile $COMPILE" ;;
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)
EXECUTE=true
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
if [ $COMPILE = true ]; then
if [ $TEST = false ]; then
mvn clean package -Dmaven.test.skip
else
mvn clean package
fi
else
echo "skipping mvn package"
fi
if [ -z $MULTI_PLATFORM ]; then
docker build -t $BUILD_NAME --build-arg="CONTAINER_INI=$CONTAINER_INI" --build-arg="JAVA_VERSION=${JAVA_VERSION}" --build-arg="SMARTGEARS_VERSION=${SMARTGEARS_VERSION}" .
else
docker build -t $BUILD_NAME --build-arg="CONTAINER_INI=$CONTAINER_INI" --build-arg="JAVA_VERSION=${JAVA_VERSION}" --build-arg="SMARTGEARS_VERSION=${SMARTGEARS_VERSION}" --platform=linux/amd64,linux/arm64,linux/arm/v7 .
# docker manifest create hub.dev.d4science.org/gcube/$BUILD_NAME \
# hub.dev.d4science.org/gcube/$NAME-amd64-linux:$IMAGE_VERSION \
# hub.dev.d4science.org/gcube/$NAME-arm-linux:$IMAGE_VERSION \
# hub.dev.d4science.org/gcube/$NAME-arm-linux:$IMAGE_VERSION
fi
if [ ${PUSH_DOCKER} = true ]; then
DOCKER_NAME=d4science/$BUILD_NAME
docker tag $BUILD_NAME $DOCKER_NAME
docker push $DOCKER_NAME
echo ">>> pushed on dockerhub the image $DOCKER_NAME"
fi
if [ ${LOGIN_HARBOR} = true ]; then
./loginHarborHub.sh
fi
if [ $PUSH_HARBOR = true ]; then
HARBOR_NAME=hub.dev.d4science.org/gcube/$BUILD_NAME
echo ">>> PUSHING on hub.dev.d4science.org the image $HARBOR_NAME"
docker tag $BUILD_NAME $HARBOR_NAME
docker push $HARBOR_NAME
echo ">>> pushed on hub.dev.d4science.org the image $HARBOR_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" $BUILD_NAME
else
docker run -p $PORT:8080 $BUILD_NAME
fi
fi

2
deploy_tomcat_manager.sh Executable file
View File

@ -0,0 +1,2 @@
echo "deploying tomcat with tomcat/manager applications"
cp -a tomcat/manager/* tomcat/webappsgit

View File

@ -1,7 +1,17 @@
version: '0.1'
version: '3.7'
services:
identity-manager:
platform: linux/amd64
build: .
volumes:
# montiamo "target" sul percorso delle applicazioni web di Tomcat, permettendo così aggiornamenti automatici del WAR senza necessità di ricostruire il container
# - ./target:/usr/local/tomcat/webapps/
- ./tomcat/webapps:/usr/local/tomcat/webapps/
ports:
- '8080:8080'
- 8080:8080
environment:
- ADMINISTRATION_PORT_ENABLED=true
- DOMAIN_NAME=docker_domain

View File

@ -1,10 +1,9 @@
mvn clean package
docker build -t idm .
# docker build -t idm .
docker compose up -d --build --force-recreate
docker run -it -d -p 8080:8080 --name idm idm
docker run -it -d -p 8080:8080 --entrypoint=/bin/bash --name idm idm
# senza composer
#docker run -it -d -p 8080:8080 --name idm idm
docker compose up
docker compose up -d --build --force-recreate
# controllare servizio
curl http://localhost:8080/identity-manager/gcube/resource/health

3
docker/.gitignore vendored Normal file
View File

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

View File

@ -0,0 +1,24 @@
[node]
mode = online
hostname = idm.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.cloud-dev.d4science.org/auth/realms/d4science/protocol/openid-connect/token
credentials.class = org.gcube.smartgears.security.SimpleCredentials
credentials.clientID =
credentials.secret =

View File

@ -1,6 +1,6 @@
[node]
mode = online
hostname = alfredo-idm-service-dev
hostname = idm.isti.cnr.it
protocol= http
port = 8080
infrastructure = gcube
@ -19,7 +19,8 @@ location = pisa
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 = alfredo-idm-service-dev
credentials.secret = 979bd3bc-5cc4-11ec-bf63-0242ac130002
; credentials.clientID = node-whn-test-uno-d-d4s.d4science.org
; credentials.secret = 979bd3bc-5cc4-11ec-bf63-0242ac130002
credentials.clientID = idm-service-hosting-node-client
credentials.secret =

23
docker/container.prod.ini Normal file
View File

@ -0,0 +1,23 @@
[node]
mode = online
hostname = idm.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.d4science.org/auth/realms/d4science/protocol/openid-connect/token
credentials.class = org.gcube.smartgears.security.SimpleCredentials
credentials.clientID =
credentials.secret =

28
docker/logback.ori.xml Normal file
View File

@ -0,0 +1,28 @@
<configuration scan="true" debug="true">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>Ï <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="org.gcube.service.idm" level="DEBUG" />
<logger name="org.gcube.service.rest" level="DEBUG" />
<logger name="org.gcube.smartgears" level="DEBUG" />
<!--
<logger name="org.gcube" level="DEBUG" />
<logger name="org.gcube.smartgears" level="TRACE" />
<logger name="org.gcube.smartgears.handlers" level="TRACE" />
<logger name="org.gcube.common.events" level="WARN" />
<logger name="org.gcube.data.publishing" level="ERROR" />
<logger name="org.gcube.documentstore" level="ERROR" />
<logger name="org.gcube.common.core.publisher.is.legacy" level="TRACE" />
<logger name="org.gcube.data.access" level="TRACE" />
<logger name="org.gcube.data.access.storagehub.handlers" level="DEBUG" />
-->
<root level="WARN">
<appender-ref ref="STDOUT" />
</root>
</configuration>

View File

@ -1,23 +1,28 @@
<configuration scan="true" debug="true">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>Ï
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
<encoder>Ï <pattern>%-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="org.gcube.service.idm" level="DEBUG" />
<logger name="org.gcube.service.rest" level="DEBUG" />
<logger name="org.gcube.smartgears" level="DEBUG" />
<!--
<logger name="org.gcube" level="DEBUG" />
<logger name="org.gcube.smartgears" level="TRACE" />
<logger name="org.gcube.smartgears.handlers" level="TRACE"/>
<logger name="org.gcube.smartgears.handlers" level="TRACE" />
<logger name="org.gcube.common.events" level="WARN" />
<logger name="org.gcube.data.publishing" level="ERROR" />
<logger name="org.gcube.documentstore" level="ERROR" />
<logger name="org.gcube.common.core.publisher.is.legacy" level="TRACE" />
<logger name="org.gcube.data.access" level="TRACE" />
<logger name="org.gcube.data.access.storagehub.handlers" level="DEBUG"/>
<root level="WARN">
<appender-ref ref="STDOUT" />
</root>
<logger name="org.gcube.data.access.storagehub.handlers" level="DEBUG" />
-->
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>

88
docker/maven-settings.xml Normal file
View File

@ -0,0 +1,88 @@
<settings xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
<servers>
<server>
<id>gcube-snapshots</id>
<username>gcube-user</username>
<password>807d1549e3f98d9</password>
</server>
</servers>
<mirrors>
<mirror>
<id>osgeo-release</id>
<name>OSGeo Release Repository</name>
<url>https://repo.osgeo.org/repository/geotools-releases</url>
<mirrorOf>opengeo,OpenGEO</mirrorOf>
</mirror>
<mirror>
<id>osgeo-release</id>
<name>OSGeo Repository</name>
<url>https://repo.osgeo.org/repository/geotools-releases</url>
<mirrorOf>osgeo</mirrorOf>
</mirror>
<mirror>
<id>geoserver-releases</id>
<name>Boundless Repository</name>
<url>https://repo.osgeo.org/repository/Geoserver-releases</url>
<mirrorOf>boundless</mirrorOf>
</mirror>
<mirror>
<id>central</id>
<name>Maven Repository Switchboard</name>
<url>http://repo1.maven.org/maven2</url>
<mirrorOf>central.maven.org</mirrorOf>
</mirror>
</mirrors>
<profiles>
<profile>
<id>gcube-developer</id>
<repositories>
<repository>
<id>gcube-snapshots</id>
<name>gCube Snapshots</name>
<url>https://nexus.d4science.org/nexus/content/repositories/gcube-snapshots</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</repository>
<repository>
<id>gcube-releases</id>
<name>gCube Releases</name>
<url>https://nexus.d4science.org/nexus/content/repositories/gcube-releases</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases>
<enabled>true</enabled>
</releases>
</repository>
<repository>
<id>gcube-externals</id>
<name>gCube Externals</name>
<url>https://nexus.d4science.org/nexus/content/repositories/gcube-externals</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>
</profile>
</profiles>
<activeProfiles>
<activeProfile>gcube-developer</activeProfile>
</activeProfiles>
</settings>

12
docker/tomcat-users.xml Normal file
View File

@ -0,0 +1,12 @@
<tomcat-users>
<role rolename="manager-gui" />
<role rolename="manager-script" />
<role rolename="manager-jmx" />
<role rolename="manager-status" />
<role rolename="admin-gui" />
<role rolename="admin-script" />
<user username="my_user" password="my_pass"
roles="manager-gui,manager-script,manager-jmx,manager-status,admin-gui,admin-script" />
</tomcat-users>

9
docker_start.sh Executable file
View File

@ -0,0 +1,9 @@
mkdir -p tomcat/logs
mkdir -p tomcat/webapps
rm -fr tomcat/logs/*
rm -fr tomcat/webapps/*
mvn clean package
docker compose up -d --build --force-recreate
# curl http://localhost:8080/identity-manager/gcube/resource/healthx

4
docker_stop_clean.sh Executable file
View File

@ -0,0 +1,4 @@
docker compose stop
rm -fr tomcat/logs/*
rm -fr tomcat/webapps/*

View File

@ -90,21 +90,21 @@ on instances.
:widths: grid
| Operation | HTTP Method | URL | Success HTTP Status | Safe | Idempotent |
|-----------|-------------|-----|---------------------|------|------------|
| **Supported<br/>HTTP Methods** | OPTIONS | /{COLLECTION} | 204 No Content | Y | Y |
| **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<br/>HTTP Methods** | OPTIONS | /{COLLECTION}/{INSTANCE_ID} | 204 No Content | Y | Y |
| **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 |
| **Purge** | PURGE | /{COLLECTION}/{INSTANCE_ID} | 204 No Content | N | N |
| **Purge** | DELETE | /{COLLECTION}/{INSTANCE_ID}?purge=true | 204 No Content | N | N |
| Operation | HTTP Method | URL | Success HTTP Status | Safe | Idempotent |
| ------------------------------ | ----------- | -------------------------------------- | ------------------- | ---- | ---------- |
| **Supported<br/>HTTP Methods** | OPTIONS | /{COLLECTION} | 204 No Content | Y | Y |
| **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<br/>HTTP Methods** | OPTIONS | /{COLLECTION}/{INSTANCE_ID} | 204 No Content | Y | Y |
| **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 |
| **Purge** | PURGE | /{COLLECTION}/{INSTANCE_ID} | 204 No Content | N | N |
| **Purge** | DELETE | /{COLLECTION}/{INSTANCE_ID}?purge=true | 204 No Content | N | N |
### About URL

File diff suppressed because it is too large Load Diff

37
documentazione/api.md Normal file
View File

@ -0,0 +1,37 @@
<!-- // da implementare rispetto al contesto -->
* /2/users/get-profile // profilo utente corrente
* /2/users/get-email // utente corrente
* /2/users/get-fullname // utente corrente
* /2/users/get-all-usernames
* /2/users/get-all-fullnames-and-usernames
* /2/users/get-usernames-by-role
* /2/users/user-exists // https://howtodoinjava.com/devops/search-keycloak-users/
* // attenzione al risultato. vedere in seguito
* /2/users/get-oauth-profile
* /2/users/get-custom-attribute
* /2/users/get-usernames-by-global-role
* /2/people/profile
# REF:
https://www.keycloak.org/docs-api/22.0.1/rest-api/index.html#_users
https://howtodoinjava.com/devops/search-keycloak-users/
# Altro
* implementare anche restified
* aggiungere api e credenziali x servizi
* nel token trovo se la richesta arriva da un utente o un servizio
* possono richiedere info e profili utente
* bisogna esporre anche il loro profilo, ora è su ids non su keycloak
*
*
rif. https://api.dev.d4science.org/social-networking-library-ws/api-docs/index.html
implementazione social: https://code-repo.d4science.org/gCubeSystem/social-networking-library-ws/src/branch/master/src/main/java/org/gcube/portal/social/networking/ws/methods/v2/Users.java

View File

@ -0,0 +1,36 @@
# installare java
## aggiungere repo temurin
brew install --cask temurin
brew tap homebrew/cask-versions
# installare le versioni
brew install --cask temurin11
brew install --cask temurin8
brew install --cask temurin21
#
# https://stackoverflow.com/a/68105964/2473953
# lista JVM installate
# /usr/libexec/java_home -V
# java home versions
export JAVA_8_HOME=$(/usr/libexec/java_home -v1.8)
export JAVA_11_HOME=$(/usr/libexec/java_home -v11)
export JAVA_17_HOME=$(/usr/libexec/java_home -v17)
#export JAVA_18_HOME=$(/usr/libexec/java_home -v18)
#export JAVA_19_HOME=$(/usr/libexec/java_home -v19)
#export JAVA_21_HOME=$(/usr/libexec/java_home -v21)
# alias to set defaults
alias java8='export JAVA_HOME=$JAVA_8_HOME'
alias java11='export JAVA_HOME=$JAVA_11_HOME'
alias java17='export JAVA_HOME=$JAVA_17_HOME'
#alias java18='export JAVA_HOME=$JAVA_18_HOME'
#alias java19='export JAVA_HOME=$JAVA_19_HOME'
#alias java21='export JAVA_HOME=$JAVA_21_HOME'
# set default to java11
java11

View File

@ -0,0 +1,3 @@
* gestione dei diritti per le chiamate da service
* usare roles o permissions?
* definire i permessi associati

View File

@ -0,0 +1 @@
# https://code.visualstudio.com/docs/containers/debug-common

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,58 @@
# Keycloack
## ambiente di sviluppo:
keycloack: https://next.dev.d4science.org/
liferay: https://next.dev.d4science.org/group/control_panel?refererPlid=41831713&p_p_id=134
site:
* /gcube
* /gcube/devNext
* /gcube/devsec/devVRE (usare questa)
realm: usare d4science
gruppi: https://accounts.dev.d4science.org/auth/admin/master/console/#/d4science/groups
usiamo /gcube/devsec/devVRE https://accounts.dev.d4science.org/auth/admin/master/console/#/d4science/groups/7097b82d-8bab-456b-a0c6-691bd303367b/32104d02-96b7-4726-839e-874089b52e2a/27840d29-a476-445a-b64a-21e25fa65be5
membri:
* liferay https://next.dev.d4science.org/group/control_panel/manage?p_p_auth=j7NeN5EX&p_p_id=174&p_p_lifecycle=0&p_p_state=maximized&p_p_mode=view&doAsGroupId=21678&refererPlid=41831713&controlPanelCategory=sites
* keycloack https://accounts.dev.d4science.org/auth/admin/master/console/#/d4science/groups/7097b82d-8bab-456b-a0c6-691bd303367b/32104d02-96b7-4726-839e-874089b52e2a/27840d29-a476-445a-b64a-21e25fa65be5
contesti
* su su keycloack, clients (usa %2f al posto di /)
* su liferay, sites liferay
ruoli:
* Accounting-Manager False Accounting-Manager role
* Catalogue-Admin False Catalogue-Admin role
* Catalogue-Editor False Catalogue-Editor role
* Catalogue-Manager False Catalogue-Manager role
* Catalogue-Moderator False Catalogue-Moderator role
* Data-Editor False —
* Data-Manager False Data-Manager role
* DataMiner-Manager False DataMiner-Manager role
* Infrastructure-Manager False Infrastructure-Manager role
* Member False Member role
* VO-Admin False VO-Admin role
* VRE-Designer False VRE-Designer role
* VRE-Manager False VRE-Manager role
1 - 15
postman api
clientid id.d4science.org => https://accounts.dev.d4science.org/auth/admin/master/console/#/d4science/clients/db98093f-3c91-4d6f-ab20-249b17cf25b2/settings
service client access:
* url https://accounts.dev.d4science.org/auth
* client_id id.d4science.org
* client secret 09c26f24-3c65-4039-9fa0-e5cc4f4032cd
* grant type: client_credentials
09c26f24-3c65-4039-9fa0-e5cc4f4032cd
usare /auth/realm

View File

@ -2,20 +2,21 @@
<enunciate
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://enunciate.webcohesion.com/schemas/enunciate-2.14.0.xsd">
<description package="org.gcube.acme.rest"/>
<description package="org.gcube.service.idm" />
<api-classes>
<include pattern="org.gcube.acme.rest.*" />
<exclude pattern="org.gcube.acme.*" />
<include pattern="org.gcube.service.idm.rest.*" />
<exclude pattern="org.gcube.service.idm.*" />
</api-classes>
<modules>
<gwt-json-overlay disabled="true " />
<gwt-json-overlay disabled="true" />
<php-json-client disabled="true" />
<ruby-json-client disabled="true" />
<java-json-client disabled="true" />
<javascript-client disabled="true" />
<docs docsDir="${project.build.directory}" docsSubdir="api-docs" />
<swagger basePath="/${project.artifactId}" />
<docs freemarkerTemplate="${project.basedir}/src/main/resources/META-INF/enunciate/d4science_docs.fmt">
<docs
freemarkerTemplate="${project.basedir}/src/main/resources/META-INF/enunciate/d4science_docs.fmt">
<additional-css
file="css/d4science_enunciate_custom.css" />
</docs>

View File

@ -1,6 +1,10 @@
name: IdentityManager
group: DataAccess
version: ${version}
description: ${description}
name: identity-manager
group: org.gcube.auth
version: 1.0.0
description: Identity Manager Service
excludes:
- path: /workspace/api-docs/*
- path: /excluded
- path: /guest
- path: /jwt/decode
- path: /jwt/introspection
- path: /api-docs/*

View File

@ -1,14 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app>
<context-param>
<param-name>resolver-basepath</param-name>
<param-value>https://data-d.d4science.org/shub</param-value>
</context-param>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
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">
<display-name>Identity Manager Service</display-name>
<description>
A gcube service - smartgears 4
</description>
<servlet>
<servlet-name>org.gcube.idm.IdentityManagerResourceInitializer</servlet-name>
<servlet-name>idm</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>org.gcube.service.idm.rest</param-value>
</init-param>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>
org.gcube.service.idm.mappers
</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>org.gcube.idm.IdentityManagerResourceInitializer</servlet-name>
<url-pattern>/workspace/*</url-pattern>
<servlet-name>idm</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>

13
loginHarborHub.sh Executable file
View File

@ -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

323
pom.xml
View File

@ -1,26 +1,37 @@
<project xmlns="https://maven.apache.org/POM/4.0.0"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.gcube.idm</groupId>
<artifactId>idm-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Identity Manager (IDM) Service</name>
<description>Identity Manager (IDM) Service - Smartgears</description>
<packaging>war</packaging>
<parent>
<groupId>org.gcube.tools</groupId>
<artifactId>maven-parent</artifactId>
<version>1.2.0</version>
</parent>
<groupId>org.gcube.idm</groupId>
<artifactId>identity-manager</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>Identity Manager Service</name>
<description>Identity Manager Smargears Service</description>
<properties>
<java.version>11</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<webappDirectory>
${project.basedir}${file.separator}src${file.separator}main${file.separator}webapp${file.separator}WEB-INF</webappDirectory>
<enunciate.version>2.14.0</enunciate.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<!-- 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>
<enunciate.version>2.16.1</enunciate.version>
<keycloak.client.version>21.1.2</keycloak.client.version>
</properties>
<scm>
@ -28,7 +39,8 @@
scm:git:https://code-repo.d4science.org/gCubeSystem/${project.artifactId}.git</connection>
<developerConnection>
scm:git:https://code-repo.d4science.org/gCubeSystem/${project.artifactId}.git</developerConnection>
<url>https://code-repo.d4science.org/gCubeSystem/${project.artifactId}</url>
<url>
https://code-repo.d4science.org/gCubeSystem/${project.artifactId}</url>
</scm>
<dependencyManagement>
@ -45,62 +57,153 @@
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<groupId>org.gcube.idm</groupId>
<artifactId>idm-common-library</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<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>
<!-- https://mvnrepository.com/artifact/com.auth0/java-jwt -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson.version}</version>
</dependency>
<!--
usermanagement-core depends on older httpcommon and gives this error on keycloak client:
java.lang.NoClassDefFoundError: org/apache/http/ssl/TrustStrategy
https://stackoverflow.com/questions/44750124/classnotfoundexception-on-org-apache-http-ssl-truststrategy
solution: bind version, or exclude them in usermanagement-core
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.13</version>
</dependency>
-->
<!-- https://mvnrepository.com/artifact/org.keycloak/keycloak-admin-client -->
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-admin-client</artifactId>
<version>${keycloak.client.version}</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>
</dependency>
<!-- smartgears -->
<dependency>
<groupId>org.gcube.core</groupId>
<artifactId>common-smartgears</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.common</groupId>
<artifactId>common-security</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.common</groupId>
<artifactId>keycloak-client</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-admin-client</artifactId>
<version>21.0.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<artifactId>common-authorization</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.common</groupId>
<artifactId>authorization-control-library</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.core</groupId>
<artifactId>common-smartgears-app</artifactId>
</dependency>
<!-- jersey -->
<dependency>
<groupId>org.gcube.core</groupId>
<artifactId>common-smartgears</artifactId>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.core</groupId>
<artifactId>common-scope</artifactId>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<scope>compile</scope>
</dependency>
<!--
https://mvnrepository.com/artifact/org.glassfish.jersey.inject/jersey-cdi2-se -->
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-cdi2-se</artifactId>
</dependency>
<!-- add jackson as json provider -->
<dependency>
<groupId>org.gcube.core</groupId>
<artifactId>common-scope-maps</artifactId>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
</dependency>
<dependency>
<groupId>org.gcube.core</groupId>
<artifactId>common-encryption</artifactId>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<!-- Required for Enunciate plugin -->
<!-- https://mvnrepository.com/artifact/com.webcohesion.enunciate/enunciate-core-annotations -->
<dependency>
<groupId>com.webcohesion.enunciate</groupId>
<artifactId>enunciate-core-annotations</artifactId>
@ -113,12 +216,7 @@
<version>${enunciate.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- END Required for Enunciate plugin -->
<!-- Test libraries -->
@ -133,18 +231,117 @@
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.gcube.portlets.user</groupId>
<artifactId>uri-resolver-manager</artifactId>
<version>[1.0.0, 2.0.0-SNAPSHOT)</version>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.7</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.liferay.portal</groupId>
<artifactId>portal-service</artifactId>
<version>6.2.5</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
<scope>compile</scope>
</dependency>
<!-- needed by com.liferay.portal -->
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
</dependency>
<!--
usermanagement-core depends on older httpcommon and gives this error on keycloak client:
java.lang.NoClassDefFoundError: org/apache/http/ssl/TrustStrategy
https://stackoverflow.com/questions/44750124/classnotfoundexception-on-org-apache-http-ssl-truststrategy
-->
<dependency>
<groupId>org.gcube.dvos</groupId>
<artifactId>usermanagement-core</artifactId>
<version>[2.0.0-SNAPSHOT, 3.0.0-SNAPSHOT)</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--
<dependency>
<groupId>org.gcube.resources.discovery</groupId>
<artifactId>ic-client</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.gcube.core</groupId>
<artifactId>common-scope</artifactId>
<scope>provided</scope>
</dependency> -->
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<!-- Sphinx plugin' -->
<!-- OPTIONAL generate the war in a different folder, that will be
mounted on docker
container -->
<!--
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.3</version>
<configuration>
<warName>idm-service</warName>
<outputDirectory>tomcat/webapps</outputDirectory>
</configuration>
</plugin>
-->
<!-- OPTIONAL. authorization-control-library: add this plugin if you
want to use gcube
authorization control funzionalities -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>${aspectj-plugin.version}</version>
<configuration>
<complianceLevel>11</complianceLevel>
<source>11</source>
<target>11</target>
<aspectLibraries>
<aspectLibrary>
<groupId>org.gcube.common</groupId>
<artifactId>authorization-control-library</artifactId>
</aspectLibrary>
</aspectLibraries>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Sphinx plugin'
<plugin>
<groupId>kr.motd.maven</groupId>
<artifactId>sphinx-maven-plugin</artifactId>
@ -164,8 +361,7 @@
</goals>
</execution>
</executions>
</plugin>
</plugin> -->
<!-- Enunciate Maven plugin -->
<plugin>
@ -181,6 +377,7 @@
</execution>
</executions>
</plugin>
<!-- Copy Enunciate Documentation from your-application/api-docs
into your war -->
<plugin>
@ -209,16 +406,6 @@
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<attachClasses>true</attachClasses>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,38 +0,0 @@
package org.gcube.idm;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
/**
* @author Alfredo Oliviero (ISTI - CNR)
*/
@Provider
public class IdentityManagerExceptionMapper implements ExceptionMapper<Exception> {
@Override
public Response toResponse(Exception exception) {
Status status = Status.INTERNAL_SERVER_ERROR;
String exceptionMessage = exception.getMessage();
try {
if(exception.getCause() != null) {
exceptionMessage = exception.getCause().getMessage();
}
} catch(Exception e) {
exceptionMessage = exception.getMessage();
}
MediaType mediaType = MediaType.TEXT_PLAIN_TYPE;
if(WebApplicationException.class.isAssignableFrom(exception.getClass())) {
Response gotResponse = ((WebApplicationException) exception).getResponse();
status = Status.fromStatusCode(gotResponse.getStatusInfo().getStatusCode());
}
return Response.status(status).entity(exceptionMessage).type(mediaType).build();
}
}

View File

@ -1,27 +0,0 @@
package org.gcube.idm;
import javax.ws.rs.ApplicationPath;
import org.gcube.idm.rest.GreetingsRest;
import org.gcube.idm.rest.UsersRest;
import org.gcube.smartgears.annotations.ManagedBy;
import org.glassfish.jersey.server.ResourceConfig;
/**
* @author Alfredo Oliviero (ISTI - CNR)
*/
// SMARTGEARS
// legge i parametri del service da application.yaml
@ApplicationPath("/")
@ManagedBy(IdentityManagerdInitializator.class)
public class IdentityManagerResourceInitializer extends ResourceConfig {
public IdentityManagerResourceInitializer() {
packages(GreetingsRest.class.getPackage().toString());
packages(UsersRest.class.getPackage().toString());
}
}

View File

@ -1,68 +0,0 @@
package org.gcube.idm;
import org.gcube.common.security.providers.SecretManagerProvider;
import org.gcube.smartgears.ApplicationManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Alfredo Oliviero (ISTI - CNR)
*/
public class IdentityManagerdInitializator implements ApplicationManager {
/**
* Logger
*/
private static Logger logger = LoggerFactory.getLogger(IdentityManagerdInitializator.class);
public static boolean initialised;
/**
* {@inheritDoc}
*/
@Override
public synchronized void onInit() {
String context = SecretManagerProvider.instance.get().getContext();
logger.trace(
"\n-------------------------------------------------------\n"
+ "Identity Manager Service is Starting on context {}\n"
+ "-------------------------------------------------------",
context);
// ApplicationContext applicationContext = ContextProvider.get();
// String helloWorldEServiceID = applicationContext.id();
logger.trace(
"\n-------------------------------------------------------\n"
+ "Identity Manager Service Started Successfully on context {}\n"
+ "-------------------------------------------------------",
context);
}
/**
* {@inheritDoc}
*/
@Override
public synchronized void onShutdown(){
String context = SecretManagerProvider.instance.get().getContext();
logger.trace(
"\n-------------------------------------------------------\n"
+ "Identity Manager Service is Stopping on context {}\n"
+ "-------------------------------------------------------",
context);
logger.trace(
"\n-------------------------------------------------------\n"
+ "Identity Manager Service Stopped Successfully on context {}\n"
+ "-------------------------------------------------------",
context);
}
}

View File

@ -1,69 +0,0 @@
package org.gcube.idm.rest;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import com.webcohesion.enunciate.metadata.rs.RequestHeader;
import com.webcohesion.enunciate.metadata.rs.RequestHeaders;
import com.webcohesion.enunciate.metadata.rs.ResourceGroup;
import com.webcohesion.enunciate.metadata.rs.ResourceLabel;
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
@Path("greetings")
@ResourceGroup("Greetings APIs")
@ResourceLabel("Greetings APIs")
@RequestHeaders ({
@RequestHeader( name = "Authorization", description = "Bearer token, see <a href=\"https://dev.d4science.org/how-to-access-resources\">https://dev.d4science.org/how-to-access-resources</a>")
})
public class GreetingsRest {
@GET
@Produces({"application/json;charset=UTF-8", "application/vnd.api+json"})
public String list(@QueryParam("limit") @DefaultValue("10") int limit,
@QueryParam("offset") @DefaultValue("0") int offset) {
return "[\"saluti\",\"saluti_volgari\"]";
}
@POST
@Produces("application/json;charset=UTF-8")
@Consumes("application/json;charset=UTF-8")
public String create(String json) {
//Greeting g = new Greeting();
//return g.create(json);
return "{\"text\":\"hi\"}";
}
@PUT
@Path("/{greeting_name}")
@Consumes("application/json;charset=UTF-8")
@Produces("application/json;charset=UTF-8")
@StatusCodes ({
@ResponseCode ( code = 200, condition = "The greeting has been updated successfully.")
})
// @AuthorizationControl(allowedRoles={"boss"}, exception=NotAuthorizedException.class)
public String update(@PathParam("greeting_name") String name, String json) {
return "{}";
}
@DELETE
@Path("/{greeting_name}")
@StatusCodes ({
@ResponseCode ( code = 204, condition = "The item has been deleted successfully."),
@ResponseCode ( code = 404, condition = "The item was not found.")
})
public String delete(@PathParam("greeting_name") String name) {
return "{}";
}
}

View File

@ -1,153 +0,0 @@
package org.gcube.idm.rest;
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.gcube.common.authorization.library.policies.Users;
import org.gcube.common.security.Owner;
import org.gcube.common.security.providers.SecretManagerProvider;
import org.gcube.keycloack.KeycloackApiClient;
import org.gcube.keycloack.KeycloakAPIFactory;
import org.gcube.rest.ResponseBean;
import org.gcube.smartgears.ContextProvider;
import org.gcube.smartgears.context.application.ApplicationContext;
import org.gcube.smartgears.security.SimpleCredentials;
import org.gcube.smartgears.security.defaults.DefaultAuthorizationProvider;
import org.gcube.smartgears.utils.InnerMethodName;
import org.jboss.resteasy.spi.NotImplementedYetException;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.slf4j.LoggerFactory;
import com.webcohesion.enunciate.metadata.rs.RequestHeader;
import com.webcohesion.enunciate.metadata.rs.RequestHeaders;
import com.webcohesion.enunciate.metadata.rs.ResourceGroup;
import com.webcohesion.enunciate.metadata.rs.ResourceLabel;
@Path("2/users")
@ResourceGroup("Users APIs")
@ResourceLabel("Greetings APIs")
@RequestHeaders({
@RequestHeader(name = "Authorization", description = "Bearer token, see <a href=\"https://dev.d4science.org/how-to-access-resources\">https://dev.d4science.org/how-to-access-resources</a>")
})
public class UsersRest {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(Users.class);
@GET
@Path("/get-usernames-by-role")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response getUsernamesByRole(
@QueryParam("role-name") String roleName) {
Status status = Status.OK;
ResponseBean responseBean = new ResponseBean();
List<String> usernames = new ArrayList<String>();
try {
String ctx = SecretManagerProvider.instance.get().getContext();
KeycloackApiClient keycloackApiClient = KeycloakAPIFactory.getSingleton().createtKeycloakInstance(ctx);
List<UserRepresentation> users = searchByRole(keycloackApiClient, roleName);
if (users != null) {
for (UserRepresentation user : users) {
usernames.add(user.getUsername());
}
}
responseBean.setResult(usernames);
responseBean.setSuccess(true);
} catch (Exception e) {
logger.error("Unable to retrieve user with the requested role", e);
responseBean.setMessage(e.getMessage());
status = Status.INTERNAL_SERVER_ERROR;
}
return Response.status(status).entity(responseBean).build();
}
private static List<UserRepresentation> searchByRole(KeycloackApiClient keycloackApiClient, String roleName) {
logger.info("Searching by role: {}", roleName);
List<ClientRepresentation> clients = keycloackApiClient.kclient.realm(keycloackApiClient.realmName)
.clients().findByClientId(keycloackApiClient.clientIdContext);
String id = "";
for (ClientRepresentation client : clients) {
logger.info("found client =" + client.getClientId());
logger.info("found client id=" + client.getId());
id = client.getId();
}
List<UserRepresentation> users = keycloackApiClient.kclient.realm(keycloackApiClient.realmName)
.clients()
.get(id).roles().get(roleName)
.getUserMembers(0, 100000);
return users;
}
@GET
@Path("/{get-profile}")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public String getCurrentProfile() {
// SMARTGEARS Specializza il tracciamento della chiamata su Accounting
InnerMethodName.instance.set("getCurrentProfile");
Owner owner = SecretManagerProvider.instance.get().getOwner();
ApplicationContext appContext = ContextProvider.get();
SimpleCredentials credentials = ((DefaultAuthorizationProvider) appContext.container().authorizationProvider())
.getCredentials();
String ctx = SecretManagerProvider.instance.get().getContext();
KeycloackApiClient keycloackApiClient = KeycloakAPIFactory.getSingleton().createtKeycloakInstance(ctx);
return null;
}
@GET
@Path("/{get-email}")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public String getCurrentEmail() {
throw new NotImplementedYetException();
}
@GET
@Path("/{get-fullname}")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public String getCurrentFullname() {
throw new NotImplementedYetException();
}
@GET
@Path("/{get-all-usernames}")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public String getAllUsernames() {
throw new NotImplementedYetException();
}
@GET
@Path("/{get-all-fullnames-and-usernames}")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public String getAllUsernamesFullnames() {
throw new NotImplementedYetException();
}
@GET
@Path("/{user-exists}")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public boolean checkUserExists() {
throw new NotImplementedYetException();
}
@GET
@Path("/{get-oauth-profile}")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public boolean getCurrentOAuthProfile() {
throw new NotImplementedYetException();
}
}

View File

@ -1,34 +0,0 @@
package org.gcube.keycloack;
public class ErrorMessages {
protected static final String NOT_USER_TOKEN_CONTEXT_USED = "User's information can only be retrieved through a user token (not qualified)";
protected static final String CANNOT_RETRIEVE_SERVICE_ENDPOINT_INFORMATION = "Unable to retrieve such service endpoint information";
private static final String NO_RUNTIME_RESOURCE_TEMPLATE_NAME_CATEGORY = "There is no Runtime Resource having name %s and Category %s in this scope";
protected static final String no_runtime_category(String runtime, String category) {
return String.format(NO_RUNTIME_RESOURCE_TEMPLATE_NAME_CATEGORY, runtime, category);
}
// public static final String MISSING_TOKEN = "Missing token.";
// public static final String MISSING_PARAMETERS = "Missing request
// parameters.";
// public static final String INVALID_TOKEN = "Invalid token.";
// public static final String TOKEN_GENERATION_APP_FAILED = "Token generation
// failed.";
// public static final String NOT_APP_TOKEN = "Invalid token: not belonging to
// an application.";
// public static final String NOT_APP_ID = "Invalid application id: it doesn't
// belong to an application.";
// public static final String NO_APP_PROFILE_FOUND = "There is no application
// profile for this app id/scope.";
// public static final String BAD_REQUEST = "Please check the parameter you
// passed, it seems a bad request";
// public static final String ERROR_IN_API_RESULT = "The error is reported into
// the 'message' field of the returned object";
// public static final String POST_OUTSIDE_VRE = "A post cannot be written into
// a context that is not a VRE";
// public static final String DEPRECATED_METHOD = "This method is deprecated,
// must use version 2";
}

View File

@ -1,102 +0,0 @@
package org.gcube.keycloack;
import java.io.InputStream;
import java.net.URL;
import java.util.AbstractMap.SimpleEntry;
import java.util.Map.Entry;
import java.util.Properties;
import javax.ws.rs.InternalServerErrorException;
import org.gcube.common.security.providers.SecretManagerProvider;
import org.gcube.common.keycloak.DefaultKeycloakClient;
import org.gcube.common.keycloak.KeycloakClientException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class KeycloackClientParams_UNUSED {
private static final Logger logger = LoggerFactory.getLogger(KeycloackClientParams_UNUSED.class);
public static final String CATALOGUE_NAME = "IDM";
protected static final String CLIENT_ID_SECRET_FILENAME = "config.properties";
protected static final String CLIENT_ID_PROPERTY_NAME = "clientId";
public String context;
public String clientId;
public String clientSecret;
public DefaultKeycloakClient gcubeKeycloakClient;
// Reads the property file and extracts the keycloack configuration params
protected static Entry<String, String> getClientIdAndClientSecret(String context) {
try {
Properties properties = new Properties();
ClassLoader classLoader = KeycloackClientParams_UNUSED.class.getClassLoader();
URL url = classLoader.getResource(CLIENT_ID_SECRET_FILENAME);
logger.trace("Going to read {} at {}", CLIENT_ID_SECRET_FILENAME, url.toString());
InputStream input = classLoader.getResourceAsStream(CLIENT_ID_SECRET_FILENAME);
properties.load(input);
String clientId = "IDM";
if (properties.containsKey(CLIENT_ID_PROPERTY_NAME)) {
clientId = properties.getProperty(CLIENT_ID_PROPERTY_NAME);
}
int index = context.indexOf('/', 1);
String root = context.substring(0, index == -1 ? context.length() : index);
String clientSecret = properties.getProperty(root);
SimpleEntry<String, String> entry = new SimpleEntry<String, String>(clientId, clientSecret);
return entry;
} catch (Exception e) {
throw new InternalServerErrorException(
"Unable to retrieve Application Token for context "
+ SecretManagerProvider.instance.get().getContext(),
e);
}
}
// TODO: VERIFICARE
public URL getRealmBaseURL() throws KeycloakClientException {
return this.gcubeKeycloakClient.getRealmBaseURL(this.context);
}
public URL getRealmBaseURL(String realm) throws KeycloakClientException {
return this.gcubeKeycloakClient.getRealmBaseURL(this.context, realm);
}
public URL getServerURL() {
try {
return this.getRealmBaseURL();
} catch (KeycloakClientException e) {
// That should be almost impossible
logger.warn("Cannot create base URL", e);
return null;
}
}
public String getClientid() {
return clientId;
}
// TODO: serve? implementare
public String getPassword() {
return null;
}
// TODO: VERIFICARE
public String getRealm() {
return this.context;
}
public KeycloackClientParams_UNUSED(String context) {
this.context = context;
Entry<String, String> params = getClientIdAndClientSecret(context);
this.clientId = params.getKey();
this.clientSecret = params.getKey();
this.gcubeKeycloakClient = new DefaultKeycloakClient();
}
}

View File

@ -1,147 +0,0 @@
package org.gcube.keycloack;
import static org.gcube.resources.discovery.icclient.ICFactory.clientFor;
import static org.gcube.resources.discovery.icclient.ICFactory.queryFor;
import java.util.Iterator;
import java.util.List;
import org.gcube.common.encryption.encrypter.StringEncrypter;
import org.gcube.common.resources.gcore.ServiceEndpoint;
import org.gcube.common.resources.gcore.ServiceEndpoint.AccessPoint;
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.keycloak.OAuth2Constants;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.KeycloakBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class KeycloakAPIFactory {
private static final Logger logger = LoggerFactory.getLogger(KeycloakAPIFactory.class);
private final static String RUNTIME_RESOURCE_NAME = "IAM";
private final static String CATEGORY = "Service";
// the singleton obj
private static KeycloakAPIFactory singleton = new KeycloakAPIFactory();
// properties that it contains
private String keycloakURL;
private String realm;
private String clientid;
private String password;
/**
* Private constructor
*/
private KeycloakAPIFactory() {
logger.info("Building KeycloakAPICredentials object");
lookupPropertiesFromIs();
logger.info("KeycloakAPICredentials object built");
}
/**
* Read the properties from the infrastructure
*/
private void lookupPropertiesFromIs() {
logger.info("Starting creating KeycloakAPICredentials");
// String ctx = SecretManagerProvider.instance.get().getContext();
// TODO: verificare che sia contesto corretto
ApplicationContext ctx = ContextProvider.get(); // get this info from SmartGears
logger.info("Discovering liferay user's credentials in context "
+ ctx.container().configuration().infrastructure());
try {
List<ServiceEndpoint> resources = getConfigurationFromIS();
if (resources.size() == 0) {
logger.error("There is no Runtime Resource having name " + RUNTIME_RESOURCE_NAME + " and Category "
+ CATEGORY + " in this scope.");
throw new Exception("There is no Runtime Resource having name " + RUNTIME_RESOURCE_NAME
+ " and Category " + CATEGORY + " in this scope.");
} else {
for (ServiceEndpoint res : resources) {
Iterator<AccessPoint> accessPointIterator = res.profile().accessPoints().iterator();
while (accessPointIterator.hasNext()) {
ServiceEndpoint.AccessPoint accessPoint = (ServiceEndpoint.AccessPoint) accessPointIterator
.next();
if (accessPoint.name().equals("d4science")) {
keycloakURL = accessPoint.address();
realm = accessPoint.name();
clientid = accessPoint.username();
password = StringEncrypter.getEncrypter().decrypt(accessPoint.password());
logger.info("Found accesspoint URL = " + keycloakURL);
}
}
}
}
} catch (Exception e) {
logger.error("Unable to retrieve such service endpoint information!", e);
return;
// }finally{
// if(oldContext != null)
// ScopeProvider.instance.set(oldContext);
}
logger.info("Bean built " + toString());
}
/**
* Retrieve endpoints information from IS for DB
*
* @return list of endpoints for ckan database
* @throws Exception
*/
private List<ServiceEndpoint> getConfigurationFromIS() throws Exception {
SimpleQuery query = queryFor(ServiceEndpoint.class);
query.addCondition("$resource/Profile/Name/text() eq '" + RUNTIME_RESOURCE_NAME + "'");
query.addCondition("$resource/Profile/Category/text() eq '" + CATEGORY + "'");
DiscoveryClient<ServiceEndpoint> client = clientFor(ServiceEndpoint.class);
List<ServiceEndpoint> toReturn = client.submit(query);
return toReturn;
}
public static KeycloakAPIFactory getSingleton() {
if (singleton == null)
singleton = new KeycloakAPIFactory();
return singleton;
}
public String getServerURL() {
return keycloakURL;
}
public String getClientid() {
return clientid;
}
public String getPassword() {
return password;
}
public String getRealm() {
return realm;
}
public KeycloackApiClient createtKeycloakInstance(String context) {
// String clientIdContext = KeycloackUtils.getClientIdContext(context);
String realm = this.getRealm();
Keycloak keycloak = KeycloakBuilder.builder()
.serverUrl(this.getServerURL())
.realm(realm)
.grantType(OAuth2Constants.CLIENT_CREDENTIALS)
.clientId(this.getClientid()) //
.clientSecret(this.getPassword()).build();
return new KeycloackApiClient(keycloak, realm, context);
}
}

View File

@ -1,17 +0,0 @@
package org.gcube.rest.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.ws.rs.HttpMethod;
/**
* @author Luca Frosini (ISTI - CNR)
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@HttpMethod("PATCH")
public @interface PATCH {
}

View File

@ -1,17 +0,0 @@
package org.gcube.rest.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.ws.rs.HttpMethod;
/**
* @author Luca Frosini (ISTI - CNR)
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@HttpMethod("PURGE")
public @interface PURGE {
}

View File

@ -1,66 +0,0 @@
package org.gcube.rest.utils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response.Status;
import org.gcube.common.gxhttp.request.GXHTTPStringRequest;
import org.gcube.keycloack.KeycloackClientParams_UNUSED;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public class HTTPUtility {
private static final Logger logger = LoggerFactory.getLogger(HTTPUtility.class);
public static StringBuilder getStringBuilder(InputStream inputStream) throws IOException {
StringBuilder result = new StringBuilder();
try(BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
String line;
while((line = reader.readLine()) != null) {
result.append(line);
}
}
return result;
}
public static GXHTTPStringRequest createGXHTTPStringRequest(String url, String path, boolean post)
throws UnsupportedEncodingException {
GXHTTPStringRequest gxhttpStringRequest = GXHTTPStringRequest.newRequest(url);
gxhttpStringRequest.from(KeycloackClientParams_UNUSED.CATALOGUE_NAME);
if(post) {
gxhttpStringRequest.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
}
gxhttpStringRequest.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON);
gxhttpStringRequest.path(path);
return gxhttpStringRequest;
}
public static String getResultAsString(HttpURLConnection httpURLConnection) throws IOException {
int responseCode = httpURLConnection.getResponseCode();
if(responseCode >= Status.BAD_REQUEST.getStatusCode()) {
Status status = Status.fromStatusCode(responseCode);
InputStream inputStream = httpURLConnection.getErrorStream();
StringBuilder result = getStringBuilder(inputStream);
logger.trace(result.toString());
throw new WebApplicationException(result.toString(), status);
}
InputStream inputStream = httpURLConnection.getInputStream();
String ret = getStringBuilder(inputStream).toString();
logger.trace("Got Respose is {}", ret);
return ret;
}
}

View File

@ -0,0 +1,58 @@
package org.gcube.service.idm;
import org.gcube.common.security.providers.SecretManagerProvider;
import org.gcube.common.security.secrets.Secret;
import org.gcube.smartgears.ApplicationManager;
import org.gcube.smartgears.ContextProvider;
import org.gcube.smartgears.configuration.Mode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* This class is use to Manage the application initialization and shutdown per
* context; The init and shutdown methods are called one per context in which
* the app is running respectively at init and a shutdown time. It is connected
* to the app declaring it via the @ManagedBy annotation. (@see HelloWorld
* class)
*
* @author Alfredo gOliviero (ISTI - CNR)
*/
public class IdMManager implements ApplicationManager {
Logger logger = LoggerFactory.getLogger(IdMManager.class);
/**
* {@inheritDoc}
*/
@Override
public void onInit() {
if (ContextProvider.get().container().configuration().mode() == Mode.offline) {
logger.debug("init called in offline mode");
} else {
Secret secret = SecretManagerProvider.get();
if (secret != null) {
logger.debug("init called in context {}", secret.getContext());
} else {
logger.debug("init called in null context");
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void onShutdown() {
if (ContextProvider.get().container().configuration().mode() == Mode.offline) {
logger.debug("shutDown called in offline mode");
} else {
Secret secret = SecretManagerProvider.get();
if (secret != null) {
logger.debug("shutDown called in context {}", secret.getContext());
} else {
logger.debug("shutDown called in null context");
}
}
}
}

View File

@ -0,0 +1,214 @@
package org.gcube.service.idm.controller;
import java.rmi.ServerException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.gcube.service.idm.keycloack.KkClientFactory;
import org.keycloak.admin.client.CreatedResponseUtil;
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.UserResource;
import org.keycloak.admin.client.resource.UsersResource;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.slf4j.LoggerFactory;
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.WebApplicationException;
public class AdminKeycloakController {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(AdminKeycloakController.class);
// TODO: Using Keycloak Admin Client to create user with roles (Realm and Client
// level)
// https://gist.github.com/thomasdarimont/c4e739c5a319cf78a4cff3b87173a84b
public static UserRepresentation createUser(String username, String email, String password, String firstName,
String lastName, Map<String, List<String>> attributes, List<String> roles)
throws WebApplicationException, ServerException {
UserRepresentation newUser = new UserRepresentation();
newUser.setEnabled(true);
newUser.setUsername(username);
newUser.setFirstName(firstName);
newUser.setLastName(lastName);
newUser.setEmail(email);
if (attributes != null) {
// user.setAttributes(Collections.singletonMap("origin",
// Arrays.asList("demo")));
newUser.setAttributes(attributes);
}
RealmResource realmResource = KkClientFactory.getSingleton().getKKRealm();
UsersResource usersRessource = realmResource.users();
// Response response = usersRessource.create(user);
// System.out.printf("Repsonse: %s %s%n", response.getStatus(),
// response.getStatusInfo());
// System.out.println(response.getLocation());
// String userId = CreatedResponseUtil.getCreatedId(response);
String newUserId = null;
// throws exception if creationResponse is failed
try {
newUserId = CreatedResponseUtil.getCreatedId(usersRessource.create(newUser));
} catch (WebApplicationException e) {
logger.error("error creating user {} - {}", newUser, e.getMessage());
e.printStackTrace();
throw new WebApplicationException(e.getMessage());
}
logger.info("created user {}", newUser);
CredentialRepresentation passwordCred = new CredentialRepresentation();
passwordCred.setTemporary(false);
passwordCred.setType(CredentialRepresentation.PASSWORD);
passwordCred.setValue(password);
UserResource userResource = usersRessource.get(newUserId);
userResource.resetPassword(passwordCred);
// // Get realm role "tester" (requires view-realm role)
RoleRepresentation testerRealmRole = realmResource.roles()//
.get("tester").toRepresentation();
//
// // Assign realm role tester to user
userResource.roles().realmLevel() //
.add(Arrays.asList(testerRealmRole));
//
ClientResource client = KkClientFactory.getSingleton().getKKClient();
if (roles != null) {
List<RoleRepresentation> listRolesRepr = new ArrayList<>();
roles.forEach(role -> listRolesRepr.add(client.roles().get(role).toRepresentation()));
//
// // // Assign client level role to user
// userResource.roles() //
// .clientLevel(client.get.getId()).add(roles);
}
return userResource.toRepresentation();
}
// https://gist.github.com/thomasdarimont/c4e739c5a319cf78a4cff3b87173a84b
public static ClientRepresentation createClient(
String id,
String clientId,
String name,
String description,
String rootUrl,
String adminUrl,
String baseUrl,
Boolean surrogateAuthRequired,
Boolean enabled,
Boolean alwaysDisplayInConsole,
String clientAuthenticatorType,
String secret,
String registrationAccessToken,
// @Deprecated String[] defaultRoles,
List<String> redirectUris,
List<String> webOrigins,
Integer notBefore,
Boolean bearerOnly,
Boolean consentRequired,
Boolean standardFlowEnabled,
Boolean implicitFlowEnabled,
Boolean directAccessGrantsEnabled,
Boolean serviceAccountsEnabled,
// Boolean oauth2DeviceAuthorizationGrantEnabled, no getter/setter
Boolean authorizationServicesEnabled,
// @Deprecated Boolean directGrantsOnly,
Boolean publicClient,
Boolean frontchannelLogout,
String protocol,
Map<String, String> attributes,
Map<String, String> authenticationFlowBindingOverrides,
Boolean fullScopeAllowed,
Integer nodeReRegistrationTimeout,
Map<String, Integer> registeredNodes,
List<ProtocolMapperRepresentation> protocolMappers,
// @Deprecated String clientTemplate,
// @Deprecated private Boolean useTemplateConfig,
// @Deprecated private Boolean useTemplateScope,
// @Deprecated private Boolean useTemplateMappers,
List<String> defaultClientScopes,
List<String> optionalClientScopes,
// private ResourceServerRepresentation authorizationSettings,
// private Map<String, Boolean> access,
String origin
) {
ClientRepresentation newClient = new ClientRepresentation();
newClient.setId(id);
newClient.setClientId(clientId);
newClient.setName(name);
newClient.setDescription(description);
newClient.setRootUrl(rootUrl);
newClient.setAdminUrl(adminUrl);
newClient.setBaseUrl(baseUrl);
newClient.setSurrogateAuthRequired(surrogateAuthRequired);
newClient.setEnabled(enabled);
newClient.setAlwaysDisplayInConsole(alwaysDisplayInConsole);
newClient.setClientAuthenticatorType(clientAuthenticatorType);
newClient.setSecret(secret);
newClient.setRegistrationAccessToken(registrationAccessToken);
newClient.setRedirectUris(redirectUris);
newClient.setWebOrigins(webOrigins);
newClient.setNotBefore(notBefore);
newClient.setConsentRequired(consentRequired);
newClient.setStandardFlowEnabled(standardFlowEnabled);
newClient.setImplicitFlowEnabled(implicitFlowEnabled);
newClient.setDirectAccessGrantsEnabled(directAccessGrantsEnabled);
newClient.setServiceAccountsEnabled(serviceAccountsEnabled);
newClient.setAuthorizationServicesEnabled(authorizationServicesEnabled);
newClient.setPublicClient(publicClient);
newClient.setFrontchannelLogout(frontchannelLogout);
newClient.setProtocol(protocol);
newClient.setAttributes(attributes);
newClient.setAuthenticationFlowBindingOverrides(authenticationFlowBindingOverrides);
newClient.setFullScopeAllowed(fullScopeAllowed);
newClient.setNodeReRegistrationTimeout(nodeReRegistrationTimeout);
newClient.setRegisteredNodes(registeredNodes);
newClient.setProtocolMappers(protocolMappers);
newClient.setDefaultClientScopes(defaultClientScopes);
newClient.setOptionalClientScopes(optionalClientScopes);
newClient.setOrigin(origin);
return createClient(newClient);
}
public static ClientRepresentation createClient(ClientRepresentation newClient) {
RealmResource realmResource = KkClientFactory.getSingleton().getKKRealm();
ClientsResource clientsResource = realmResource.clients();
String newClientId = null;
// throws exception if creationResponse is failed
try {
newClientId = CreatedResponseUtil.getCreatedId(clientsResource.create(newClient));
} catch (WebApplicationException e) {
logger.error("error creating client {} - {}", newClient, e.getMessage());
e.printStackTrace();
throw new WebApplicationException(e.getMessage());
}
logger.info("created user {}", newClient);
ClientResource client = clientsResource.get(newClientId);
return client.toRepresentation();
}
}

View File

@ -0,0 +1,100 @@
package org.gcube.service.idm.controller;
import java.util.List;
import java.util.Map;
import javax.ws.rs.ForbiddenException;
import org.gcube.common.keycloak.model.ModelUtils;
import org.gcube.common.security.Owner;
import org.gcube.common.security.providers.SecretManagerProvider;
import org.gcube.common.security.secrets.Secret;
public class AuthController {
public final static String IDM_SERVICE_READ = "idm-service-read";
// can admin current context
public final static String IDM_SERVICE_ADMIN = "idm-service-admin";
// can admin all realm, not only current context
public final static String IDM_SERVICE_REALM = "idm-service-realm";
public final static List<String> ACCESS_READ_ROLES = List.of(IDM_SERVICE_READ, IDM_SERVICE_ADMIN,
IDM_SERVICE_REALM);
public final static List<String> ACCESS_ADMIN_ROLES = List.of(IDM_SERVICE_ADMIN, IDM_SERVICE_REALM);
public final static List<String> ACCESS_ADMIN_REALM_ROLES = List.of(IDM_SERVICE_REALM);
public static String getAccessToken() {
Map<String, String> authorizations = SecretManagerProvider.get().getHTTPAuthorizationHeaders();
String access_token = authorizations.get("Authorization").replace("Bearer", "").trim();
return access_token;
}
public static Owner getOwner() {
Secret secret = SecretManagerProvider.get();
Owner owner = secret.getOwner();
return owner;
}
public static boolean checkRealmRole(String realm_role) {
String access_token = getAccessToken();
return checkRealmRole(realm_role, access_token);
}
public static boolean checkRealmRole(String realm_role, String access_token) {
try {
return ModelUtils.getAccessTokenFrom(access_token).getRealmAccess().getRoles().contains(realm_role);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
}
public static boolean checkContextRole(String context_role) {
Owner owner = getOwner();
return checkContextRole(context_role, owner);
}
public static boolean checkContextRole(String context_role, Owner owner) {
return owner.getRoles().contains(context_role);
}
public static boolean checkRole(String role) {
return checkContextRole(role) || checkRealmRole(role);
}
public static boolean checkAnyRole(List<String> roles) {
String access_token = getAccessToken();
Owner owner = getOwner();
for (String role : roles) {
if (checkContextRole(role, owner) || checkRealmRole(role, access_token)) {
return true;
}
}
return false;
}
public static boolean userIsMe(String username) {
Owner owner = getOwner();
return userIsMe(username, owner);
}
public static boolean userIsMe(String username, Owner owner) {
return !owner.isApplication() && owner.getId().equals(username);
}
public static void checkIsRealmAdmin(String message) throws ForbiddenException {
if (!checkAnyRole(ACCESS_ADMIN_ROLES)) {
throw new ForbiddenException(message);
}
}
public static void checkIsContextmAdmin(String message) throws ForbiddenException {
if (!checkAnyRole(ACCESS_ADMIN_REALM_ROLES)) {
throw new ForbiddenException(message);
}
}
}

View File

@ -0,0 +1,35 @@
package org.gcube.service.idm.controller;
import java.util.HashMap;
import java.util.Map;
import org.gcube.service.idm.serializers.IdmObjectSerializator;
import com.auth0.jwt.JWT;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
public class JWTController {
public static Map<String, Object> decodeJwtToken(String token)
throws JsonMappingException, JsonProcessingException {
DecodedJWT decodedJWT = JWT.decode(token);
String headerJson = IdmObjectSerializator.decodeBase64String(decodedJWT.getHeader());
String payloadJson = IdmObjectSerializator.decodeBase64String(decodedJWT.getPayload());
// String signatureJson =
// ContextSerializator.decodeBase64String(decodedJWT.getSignature());
Map<String, Object> decoded = new HashMap<String, Object>();
decoded.put("jwt_token", token);
decoded.put("token", decodedJWT.getToken());
decoded.put("header", IdmObjectSerializator.jsonStringToHasmap(headerJson));
decoded.put("payload", IdmObjectSerializator.jsonStringToHasmap(payloadJson));
// decoded.put("signature",
// ContextSerializator.jsonStringToHasmap(signatureJson));
decoded.put("decodedJWT", decodedJWT);
return decoded;
}
}

View File

@ -0,0 +1,147 @@
package org.gcube.service.idm.controller;
import java.rmi.ServerException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import org.gcube.service.idm.keycloack.KkClientFactory;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.GroupResource;
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.GroupRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.slf4j.LoggerFactory;
import jakarta.ws.rs.NotFoundException;
public class KCClientsController {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(KCClientsController.class);
public enum REPR {
full, compact, name, id, client_id, none
}
public static Object formatRepr(ClientRepresentation client, REPR format) {
if (client == null || format.equals(REPR.none)) {
return null;
}
if (format.equals(REPR.name)) {
return client.getName();
}
if (format.equals(REPR.id)) {
return client.getId();
}
if (format.equals(REPR.client_id)) {
return client.getClientId();
}
if (format.equals(REPR.compact)) {
HashMap<String, Object> result = new HashMap<String, Object>();
result.put("id", client.getId());
result.put("clientId", client.getClientId());
result.put("name", client.getName());
result.put("description", client.getDescription());
return result;
}
return client;
}
public static List<Object> formatList(List<ClientRepresentation> clients, REPR format) {
return clients.stream().map(x -> formatRepr(x, format)).filter(Objects::nonNull).collect(Collectors.toList());
}
public static List<RoleRepresentation> getRolesCurrent() {
logger.info("Searching users for context");
ClientResource client = KkClientFactory.getSingleton().getKKClient();
RolesResource roles_resource = client.roles();
List<RoleRepresentation> roles = roles_resource.list();
return roles;
}
public static RoleRepresentation getRoleByNameCurrent(String name) {
return getRoleByName(null, name);
}
public static RoleRepresentation getRoleByName(String clientId, String name) {
logger.info("Searching users for context");
ClientResource clientResource = KkClientFactory.getSingleton().getKKClientById(clientId);
RolesResource roles_resource = clientResource.roles();
RoleResource role = roles_resource.get(name);
if (role == null) {
throw new NotFoundException("cannot retrieve role " + name);
}
return role.toRepresentation();
}
public static List<UserRepresentation> getContextUsersByRoleCurrent(String role_name) {
return getContextUsersByRoleCurrent(role_name, null, null);
}
public static List<UserRepresentation> getContextUsersByRoleCurrent(String role_name, Integer firstResult,
Integer maxResults) {
return getContextUsersByRole(null, role_name, firstResult, maxResults);
}
public static List<UserRepresentation> getContextUsersByRole(String clientId, String role_name,
Integer firstResult,
Integer maxResults) {
ClientResource clientResource = KkClientFactory.getSingleton().getKKClientById(clientId);
RolesResource roles_resource = clientResource.roles();
RoleResource r = roles_resource.get(role_name);
List<UserRepresentation> users = r.getUserMembers(firstResult, maxResults);
return users;
}
/**
* returns the list of users of the client
* users list is a subset of members list, it's obtained from the group named as
* the context
*
* @param clientId null for current context
* @throws ServerException
* @throws NotFoundException
*/
public static List<UserRepresentation> getMemberGroupUsersCurrent() {
return getMemberGroupUsers(null, null, null);
}
/**
* returns the list of users of the client
* users list is a subset of members list, it's obtained from the group named as
* the context
*
* @param clientId null for current context
* @param firstResult
* @param maxResults
* @throws ServerException
* @throws NotFoundException
*/
public static List<UserRepresentation> getMemberGroupUsers(String clientId, Integer firstResult,
Integer maxResults) {
RealmResource realmResource = KkClientFactory.getSingleton().getKKRealm();
ClientResource clientResource = KkClientFactory.getSingleton().getKKClientById(clientId);
ClientRepresentation client = clientResource.toRepresentation();
GroupRepresentation g_repr = realmResource.getGroupByPath(client.getName());
GroupResource group = realmResource.groups().group(g_repr.getId());
List<UserRepresentation> user_members = group.members(firstResult, maxResults);
return user_members;
}
}

View File

@ -0,0 +1,49 @@
package org.gcube.service.idm.controller;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import org.keycloak.representations.idm.GroupRepresentation;
import org.slf4j.LoggerFactory;
public class KCGroupsController {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(KCGroupsController.class);
public enum REPR {
full, compact, name, id, path, none
}
public static Object formatRepr(GroupRepresentation group, REPR format) {
if (group == null || format.equals(REPR.none)) {
return null;
}
if (format.equals(REPR.name)) {
return group.getName();
}
if (group.equals(REPR.id)) {
return group.getId();
}
if (group.equals(REPR.path)) {
return group.getPath();
}
if (format.equals(REPR.compact)) {
HashMap<String, Object> result = new HashMap<String, Object>();
result.put("id", group.getId());
result.put("name", group.getName());
result.put("path", group.getPath());
return result;
}
return group;
}
public static List<Object> formatList(List<GroupRepresentation> groups, REPR format) {
return groups.stream().map(x -> formatRepr(x, format)).filter(Objects::nonNull).collect(Collectors.toList());
}
}

View File

@ -0,0 +1,93 @@
package org.gcube.service.idm.controller;
import java.rmi.ServerException;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import org.gcube.service.idm.keycloack.KkClientFactory;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.RoleResource;
import org.keycloak.admin.client.resource.RolesResource;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.slf4j.LoggerFactory;
import jakarta.ws.rs.NotFoundException;
public class KCRolesController {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(KCRolesController.class);
public enum REPR {
full, compact, name, id, none
}
public static Object formatRepr(RoleRepresentation role, REPR format) {
if (role == null || format.equals(REPR.none)) {
return null;
}
if (format.equals(REPR.name)) {
return role.getName();
}
if (format.equals(REPR.id)) {
return role.getId();
}
return role;
}
public static List<Object> formatList(List<RoleRepresentation> roles, REPR format) {
return roles.stream().map(x -> formatRepr(x, format)).filter(Objects::nonNull).collect(Collectors.toList());
}
public static List<Object> getFormattedRoles(REPR format) {
Boolean briefRepresentation = !format.equals(REPR.full);
List<RoleRepresentation> roles = getRolesForContext(briefRepresentation);
return formatList(roles, format);
}
public static List<RoleRepresentation> getRolesForContext() {
return getRolesForContext(true);
}
public static List<RoleRepresentation> getRolesForContext(Boolean compact) {
logger.info("Searching users for context");
ClientResource client = KkClientFactory.getSingleton().getKKClient();
RolesResource roles_resource = client.roles();
List<RoleRepresentation> roles = roles_resource.list(compact);
return roles;
}
public static RoleRepresentation getRoleByNameCurrent(String name) {
logger.info("Searching users for context");
ClientResource client = KkClientFactory.getSingleton().getKKClient();
RolesResource roles_resource = client.roles();
RoleResource role = roles_resource.get(name);
if (role == null) {
throw new NotFoundException("cannot retrieve role " + name);
}
return role.toRepresentation();
}
public static List<UserRepresentation> getUsersByRoleForContext(String role_name) {
return getUsersByRoleForContext(role_name, null, null);
}
public static List<UserRepresentation> getUsersByRoleForContext(String role_name, Integer firstResult,
Integer maxResults) {
ClientResource client = KkClientFactory.getSingleton().getKKClient();
RolesResource roles_resource = client.roles();
RoleResource role_resource = roles_resource.get(role_name);
List<UserRepresentation> users = role_resource.getUserMembers(firstResult, maxResults);
return users;
}
}

View File

@ -0,0 +1,255 @@
package org.gcube.service.idm.controller;
import java.rmi.ServerException;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import org.gcube.common.security.providers.SecretManagerProvider;
import org.gcube.service.idm.keycloack.KeycloackApiClient;
import org.gcube.service.idm.keycloack.KkClientFactory;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.RoleResource;
import org.keycloak.admin.client.resource.RolesResource;
import org.keycloak.admin.client.resource.UserResource;
import org.keycloak.admin.client.resource.UsersResource;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.slf4j.LoggerFactory;
import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.NotFoundException;
public class KCUserController {
public static String MEMBER_ROLE_NAME = "Member";
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(KCUserController.class);
public enum REPR {
full, compact, username, email, id, username_email, username_user, fullname, none
}
public static UsersResource realmUsersResource() {
RealmResource realm = KkClientFactory.getSingleton().getKKRealm();
UsersResource users = realm.users();
return users;
}
public static List<UserRepresentation> realmUser(
Integer firstResult,
Integer maxResults) {
UsersResource uresource = KCUserController.realmUsersResource();
List<UserRepresentation> users = uresource.list(firstResult, maxResults);
return users;
}
public static List<UserRepresentation> contextUsers(
Integer firstResult,
Integer maxResults) {
ClientResource client = KkClientFactory.getSingleton().getKKClient();
RolesResource roles_resource = client.roles();
RoleResource r = roles_resource.get(MEMBER_ROLE_NAME);
List<UserRepresentation> users = r.getUserMembers(firstResult, maxResults);
return users;
}
public static Object formatRepr(UserRepresentation user, REPR format) {
if (user == null || format.equals(REPR.none)) {
return null;
}
if (format.equals(REPR.username)) {
return user.getUsername();
}
if (format.equals(REPR.email)
|| format.equals(REPR.username_email)) {
return user.getEmail();
}
if (format.equals(REPR.id)) {
return user.getId();
}
if (format.equals(REPR.fullname)) {
return user.getFirstName() + " " + user.getLastName();
}
return user;
}
public static Object formatList(List<UserRepresentation> users, REPR format) {
if (format.equals(REPR.username_email)
|| format.equals(REPR.username_user)
) {
HashMap<String, Object> usernamesAndFullnames = new HashMap<String, Object>();
users.forEach(user -> usernamesAndFullnames.put(user.getUsername(), formatRepr(user, format)));
return usernamesAndFullnames;
}
// https://stackoverflow.com/questions/3907394/java-is-there-a-map-function/3907448#3907448
return users.stream().map(x -> formatRepr(x, format)).filter(Objects::nonNull).collect(Collectors.toList());
}
/**
* Search for users based on the given filters.
*
* @param username a value contained in username
* @param firstName a value contained in first name
* @param lastName a value contained in last name
* @param email a value contained in email
* @param emailVerified whether the email has been verified
* @param idpAlias the alias of the Identity Provider
* @param idpUserId the userId at the Identity Provider
* @param firstResult the position of the first result to retrieve
* @param maxResults the maximum number of results to retrieve
* @param enabled only return enabled or disabled users
* @param briefRepresentation Only return basic information (only guaranteed to
* return id, username, created, first
* and last name, email, enabled state, email
* verification state, federation link, and access.
* Note that it means that namely user attributes,
* required actions, and not before are not
* returned.)
* @return a list of {@link UserRepresentation}
*/
public static List<UserRepresentation> search(String username,
String firstName,
String lastName,
String email,
Boolean emailVerified,
String idpAlias,
String idpUserId,
Integer firstResult,
Integer maxResults,
Boolean enabled,
Boolean briefRepresentation) {
logger.info("Searching users with params");
RealmResource realm = KkClientFactory.getSingleton().getKKRealm();
List<UserRepresentation> users = realm.users().search(username, firstName, lastName, email, emailVerified,
idpAlias, idpUserId, firstResult, maxResults, enabled, briefRepresentation);
return users;
}
public static UserRepresentation getUserByEmail(String email) {
logger.info("Searching user by email: {}", email);
RealmResource realm = KkClientFactory.getSingleton().getKKRealm();
UserRepresentation user = realm.users()
.searchByEmail(email, true).stream().findFirst().orElse(null);
if (user == null) {
throw new NotFoundException("cannot retrieve user for email" + email);
}
return user;
}
public static UserRepresentation getUserById(String username) {
logger.info("Searching user by username: {}", username);
RealmResource realm = KkClientFactory.getSingleton().getKKRealm();
UserRepresentation user = realm.users().search(username).stream().findFirst().orElse(null);
if (user == null) {
throw new NotFoundException("cannot retrieve user " + username);
}
return user;
}
public static UserRepresentation getUserByUsername(String username) {
logger.info("Searching user by username: {}", username);
RealmResource realm = KkClientFactory.getSingleton().getKKRealm();
UserRepresentation user = realm.users()
.search(username, true).stream().findFirst().orElse(null);
if (user == null) {
throw new NotFoundException("cannot retrieve user " + username);
}
return user;
}
public static UserResource getUserResourceByUsername(String username) {
logger.info("Searching user by username: {}", username);
RealmResource realm = KkClientFactory.getSingleton().getKKRealm();
UserRepresentation user = realm.users()
.search(username, true).stream().findFirst().orElse(null);
if (user == null) {
throw new NotFoundException("cannot retrieve user " + username);
}
UserResource userRes = realm.users().get(user.getId());
if (userRes == null) {
throw new NotFoundException("cannot retrieve user " + username);
}
return userRes;
}
public static List<UserRepresentation> searchUsersByRole(String roleName, Integer firstResult, Integer maxResults) {
logger.info("Searching users by role: {}", roleName);
ClientResource client = KkClientFactory.getSingleton().getKKClient();
List<UserRepresentation> users = client.roles().get(roleName)
.getUserMembers(firstResult, maxResults);
return users;
}
public static List<ClientRepresentation> clients() {
RealmResource realm = KkClientFactory.getSingleton().getKKRealm();
String ctx = SecretManagerProvider.get().getContext();
KeycloackApiClient keycloackApiClient = KkClientFactory.getSingleton()
.createtKeycloakInstance(ctx);
List<ClientRepresentation> clients = realm.clients().findByClientId(keycloackApiClient.clientIdContext);
return clients;
}
public static Object getUserParameter(String username, String parameter) {
UserRepresentation user = getUserByUsername(username);
if (user == null) {
throw new NotFoundException("cannot retrieve user " + username);
}
if (parameter == null)
return user;
else if (parameter.equals("email"))
return user.getEmail();
else if (parameter.equals("roles_realm"))
return user.getRealmRoles();
else if (parameter.equals("roles_clients"))
return user.getClientRoles();
else if (parameter.equals("groups"))
return user.getGroups();
else if (parameter.equals("id"))
return user.getId();
else if (parameter.equals("username"))
return user.getUsername();
else if (parameter.equals("name"))
return user.getFirstName() + " " + user.getLastName();
else if (parameter.equals("attributes"))
return user.getAttributes();
else
throw new BadRequestException("unknow parameter " + parameter);
}
}

View File

@ -0,0 +1,95 @@
package org.gcube.service.idm.controller;
import java.rmi.ServerException;
import org.gcube.service.idm.liferay.LiferayClientFactory;
import org.gcube.vomanagement.usermanagement.exception.UserManagementSystemException;
import org.gcube.vomanagement.usermanagement.exception.UserRetrievalFault;
import org.gcube.vomanagement.usermanagement.impl.ws.LiferayWSUserManager;
import org.gcube.vomanagement.usermanagement.model.GCubeUser;
import org.slf4j.LoggerFactory;
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.ServiceUnavailableException;
public class LiferayProfileClient {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(LiferayProfileClient.class);
public static GCubeUser getUserProfile(long id) {
String errormsg = "cannot retrieve user with id " + id;
try {
LiferayWSUserManager client = LiferayClientFactory.getSingleton().getClient();
GCubeUser user = client.getUserById(id);
if (user == null) {
throw new NotFoundException(errormsg);
}
return user;
} catch (UserManagementSystemException e) {
e.printStackTrace();
throw new ServiceUnavailableException(errormsg);
} catch (UserRetrievalFault e) {
logger.warn(errormsg);
e.printStackTrace();
throw new NotFoundException(errormsg);
}
}
public static GCubeUser getUserProfileByUsername(String username) {
String errormsg = "cannot retrieve user with username " + username;
try {
LiferayWSUserManager client = LiferayClientFactory.getSingleton().getClient();
GCubeUser user = client.getUserByUsername(username);
if (user == null) {
throw new NotFoundException(errormsg);
}
return user;
} catch (UserManagementSystemException e) {
logger.error(errormsg);
e.printStackTrace();
throw new ServiceUnavailableException(errormsg);
} catch (UserRetrievalFault e) {
logger.error(errormsg);
e.printStackTrace();
throw new NotFoundException(errormsg);
} catch (Exception e) {
logger.error(errormsg);
e.printStackTrace();
throw new ServiceUnavailableException(errormsg);
}
}
public static GCubeUser getUserProfileByEmail(String email) {
String errormsg = "cannot retrieve user with email " + email;
try {
LiferayWSUserManager client = LiferayClientFactory.getSingleton().getClient();
GCubeUser user = client.getUserByEmail(email);
if (user == null) {
throw new NotFoundException(errormsg);
}
return user;
} catch (UserManagementSystemException e) {
e.printStackTrace();
throw new ServiceUnavailableException(errormsg);
} catch (UserRetrievalFault e) {
logger.warn(errormsg);
e.printStackTrace();
throw new NotFoundException(errormsg);
} catch (Exception e) {
logger.warn(errormsg);
e.printStackTrace();
throw new ServiceUnavailableException(errormsg);
}
}
}

View File

@ -0,0 +1,45 @@
package org.gcube.service.idm.health;
import java.io.File;
import java.io.IOException;
import org.gcube.common.health.api.HealthCheck;
import org.gcube.common.health.api.ReadinessChecker;
import org.gcube.common.health.api.response.HealthCheckResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* this class is used to add checks about components used by the service (DB,
* external FS, other services etc. )
* and automatically exposed using the REST method webapp/gcube/resources/health
*
*
* @author lucio
*
*/
@ReadinessChecker
public class CreateTempFileCheck implements HealthCheck {
private static Logger log = LoggerFactory.getLogger(CreateTempFileCheck.class);
@Override
public String getName() {
return "create temp file";
}
@Override
public HealthCheckResponse check() {
try {
File.createTempFile("exampleTest", "txt");
return HealthCheckResponse.builder(getName()).up().info("health check example").build();
} catch (IOException e) {
log.error("error checking defaultStorage", e);
return HealthCheckResponse.builder(getName()).down().error(e.getMessage()).build();
}
}
}

View File

@ -1,4 +1,4 @@
package org.gcube.keycloack;
package org.gcube.service.idm.keycloack;
import org.keycloak.admin.client.Keycloak;
@ -8,17 +8,16 @@ public class KeycloackApiClient {
public String clientIdContext;
public String context;
public static String getClientIdContext(String context){
return context.replace("/", "%2F");
}
public static String encodeClientIdContext(String context) {
return context.replace("/", "%2F");
}
public KeycloackApiClient(Keycloak kclient, String realmName, String context) {
this.clientIdContext = getClientIdContext(context);
this.clientIdContext = encodeClientIdContext(context);
this.context = context;
this.kclient = kclient;
this.realmName = realmName;
//ClientsResource clients = kclient.realm(realmName).clients().get*
//clients.get(context);
// ClientsResource clients = kclient.realm(realmName).clients().get*
// clients.get(context);
}
}

View File

@ -0,0 +1,169 @@
package org.gcube.service.idm.keycloack;
import java.rmi.ServerException;
import java.util.List;
import org.gcube.common.security.providers.SecretManagerProvider;
import org.gcube.common.security.secrets.Secret;
import org.gcube.idm.common.is.InfrastrctureServiceClient;
import org.gcube.idm.common.is.IsServerConfig;
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.RealmResource;
import org.keycloak.representations.idm.ClientRepresentation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.ws.rs.InternalServerErrorException;
import jakarta.ws.rs.NotFoundException;
public class KkClientFactory {
private static final Logger logger = LoggerFactory.getLogger(KkClientFactory.class);
private final static String RUNTIME_RESOURCE_NAME = "IAM";
private final static String CATEGORY = "Service";
private final static String END_POINT_NAME = "d4science";
private final static boolean IS_ROOT_SERVICE = true;
// the singleton obj
private IsServerConfig config;
private Secret secret;
private static KkClientFactory singleton = new KkClientFactory();
public static KkClientFactory getSingleton() {
if (singleton == null)
singleton = new KkClientFactory();
return singleton;
}
/**
* keycloak configuration obtained from IS in the private constructor
* using the singleton pattern, it's retrieved from IS only for the first
* access, then kept in the singleton object
*/
public Secret getSecret() {
return secret;
}
public void setSecret(Secret secret) {
this.secret = secret;
this.config = fetchIsConfig(this.secret);
}
// allows to configure the factory
// e.g. from external configuration file
public void setConfig(IsServerConfig configuration) {
config = configuration;
}
public IsServerConfig fetchIsConfig(Secret secret) throws InternalServerErrorException {
try {
if (this.secret == null)
this.secret = InfrastrctureServiceClient.getSecretForInfrastructure();
IsServerConfig cfg = InfrastrctureServiceClient.serviceConfigFromIS(RUNTIME_RESOURCE_NAME, CATEGORY,
END_POINT_NAME, IS_ROOT_SERVICE, secret);
logger.info("KeycloakAPICredentials object built {} - {}", cfg.getServerUrl(), cfg.getName());
return cfg;
} catch (Exception e) {
e.printStackTrace();
throw new InternalServerErrorException(e);
}
}
public KeycloackApiClient createtKeycloakInstance(String context) {
if (this.config == null) {
this.config = fetchIsConfig(this.secret);
}
return createtKeycloakInstance(this.config, context);
}
public static KeycloackApiClient createtKeycloakInstance(IsServerConfig config, String context) {
Keycloak kclient = KeycloakBuilder.builder()
.serverUrl(config.getServerUrl())
.realm(config.getName())
.grantType(config.getGrantType())
.clientId(config.getClientId()) //
.clientSecret(config.getClientSecret()).build();
return new KeycloackApiClient(kclient, config.getName(), context);
}
public RealmResource getKKRealm() {
String ctx = SecretManagerProvider.get().getContext();
return getKKRealm(ctx);
}
public RealmResource getKKRealm(String ctx) {
logger.info("Searching client for contex");
KeycloackApiClient keycloackApiClient = createtKeycloakInstance(ctx);
RealmResource realm = keycloackApiClient.kclient.realm(keycloackApiClient.realmName);
return realm;
}
public ClientResource getKKClient() {
String ctx = SecretManagerProvider.get().getContext();
return getKKClient(ctx);
}
public ClientResource getKKClient(String ctx) {
logger.info("Searching client for contex");
RealmResource realm = getKKRealm(ctx);
List<ClientRepresentation> clients = realm.clients().findByClientId(encodeClientIdContext(ctx));
if (clients.size() == 0) {
return null;
}
String id = clients.get(0).getId();
return realm.clients().get(id);
}
/**
* select the ClientResource by name, or current client if clientId parameter is
* null;
*
* @param clientId
* @return
* @throws ServerException
* @throws NotFoundException
*/
public ClientResource getKKClientById(String clientId) {
if (clientId == null)
return KkClientFactory.getSingleton().getKKClient();
RealmResource realmResource = getKKRealm();
List<ClientRepresentation> clients = realmResource.clients().findByClientId(clientId);
if (clients.size() == 0) {
throw new NotFoundException();
}
String id = clients.get(0).getId();
return realmResource.clients().get(id);
}
// TODO: REMOVE
// static IsServerConfig getTestConfig() {
// String serverUrl = "https://accounts.dev.d4science.org/auth";
// String realm = "d4science";
// String clientId = "id.d4science.org";
// String clientSecret = "";
// return new IsServerConfig(serverUrl, realm, clientId, clientSecret);
// }
public static String encodeClientIdContext(String context) {
return context.replace("/", "%2F");
}
}

View File

@ -0,0 +1,105 @@
package org.gcube.service.idm.liferay;
import org.gcube.common.security.secrets.Secret;
import org.gcube.idm.common.is.InfrastrctureServiceClient;
import org.gcube.idm.common.is.IsServerConfig;
import org.gcube.vomanagement.usermanagement.impl.ws.LiferayWSUserManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.ws.rs.InternalServerErrorException;
public class LiferayClientFactory {
private static final Logger logger = LoggerFactory.getLogger(LoggerFactory.class);
// Service endpoint properties
private final static String RUNTIME_RESOURCE_NAME = "D4Science Infrastructure Gateway";
private final static String CATEGORY = "Portal";
private final static String END_POINT_NAME = "JSONWSUser";
private final static boolean IS_ROOT_SERVICE = true;
LiferayWSUserManager client = null;
private IsServerConfig config;
private Secret secret;
private static LiferayClientFactory singleton = new LiferayClientFactory();
public static LiferayClientFactory getSingleton() {
if (singleton == null)
singleton = new LiferayClientFactory();
return singleton;
}
public Secret getSecret() {
return secret;
}
public void setSecret(Secret secret) {
this.secret = secret;
this.config = fetchIsConfig(this.secret);
}
public IsServerConfig fetchIsConfig(Secret secret) throws InternalServerErrorException {
try {
if (this.secret == null)
this.secret = InfrastrctureServiceClient.getSecretForInfrastructure();
IsServerConfig cfg = InfrastrctureServiceClient.serviceConfigFromIS(RUNTIME_RESOURCE_NAME, CATEGORY,
END_POINT_NAME, IS_ROOT_SERVICE, secret);
logger.info("KeycloakAPICredentials object built {} - {}", cfg.getServerUrl(), cfg.getName());
return cfg;
} catch (Exception e) {
e.printStackTrace();
throw new InternalServerErrorException(e);
}
}
public LiferayWSUserManager createtLiferayClientInstance() {
if (this.config == null) {
this.config = fetchIsConfig(this.secret);
}
return createtLiferayClientInstance(this.config);
}
public static LiferayWSUserManager createtLiferayClientInstance(IsServerConfig config) {
String host = config.getServerUrl();
String schema = config.getProperty("schema");
String user = config.getProperty("username");
String password = config.getProperty("password");
Integer port = Integer.valueOf(config.getProperty("port"));
LiferayWSUserManager client = null;
try {
client = new LiferayWSUserManager(user, password, host, schema, port);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw new InternalServerErrorException("cannot create Liferay client");
}
if (client == null) {
throw new InternalServerErrorException("cannot create Liferay client");
}
logger.info("Liferay object built {} - {}", config.getServerUrl(), config.getName());
return client;
}
// public IsServerConfig getConfig() {
// if (this.config == null) {
// this.config = fetchIsConfig();
// }
// return this.config;
// }
public LiferayWSUserManager getClient() {
if (this.client == null) {
this.client = createtLiferayClientInstance();
}
return this.client;
}
}

View File

@ -0,0 +1,56 @@
package org.gcube.service.idm.mappers;
import org.gcube.service.utils.beans.ResponseBean;
import jakarta.ws.rs.ForbiddenException;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;
import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;
/**
* @author Alfredo Oliviero (ISTI - CNR)
*/
@Provider
public class ForbiddenExceptionMapper implements ExceptionMapper<ForbiddenException> {
@Override
public Response toResponse(ForbiddenException exception) {
Status status = Status.INTERNAL_SERVER_ERROR;
String exceptionMessage = exception.getMessage();
ResponseBean responseBean = null;
try {
if (exception.getCause() != null) {
exceptionMessage = exception.getCause().getMessage();
}
} catch (Exception e) {
exceptionMessage = exception.getMessage();
}
MediaType mediaType = MediaType.TEXT_PLAIN_TYPE;
if (WebApplicationException.class.isAssignableFrom(exception.getClass())) {
Response gotResponse = ((WebApplicationException) exception).getResponse();
Object entity = gotResponse.getEntity();
if (entity != null && ResponseBean.class.isAssignableFrom(entity.getClass())) {
responseBean = (ResponseBean) entity;
}
status = Status.fromStatusCode(gotResponse.getStatusInfo().getStatusCode());
}
if (responseBean == null) {
responseBean = new ResponseBean();
}
responseBean.setSuccess(false);
responseBean.setMessage(exceptionMessage);
// responseBean.set
return Response.status(status).entity(responseBean).type(mediaType).build();
}
}

View File

@ -0,0 +1,55 @@
package org.gcube.service.idm.mappers;
import org.gcube.service.utils.beans.ResponseBean;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;
import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;
/**
* @author Luca Frosini (ISTI - CNR)
*/
@Provider
public class IDMExceptionMapper implements ExceptionMapper<Exception> {
@Override
public Response toResponse(Exception exception) {
Status status = Status.INTERNAL_SERVER_ERROR;
String exceptionMessage = exception.getMessage();
ResponseBean responseBean = null;
try {
if (exception.getCause() != null) {
exceptionMessage = exception.getCause().getMessage();
}
} catch (Exception e) {
exceptionMessage = exception.getMessage();
}
MediaType mediaType = MediaType.TEXT_PLAIN_TYPE;
if (WebApplicationException.class.isAssignableFrom(exception.getClass())) {
Response gotResponse = ((WebApplicationException) exception).getResponse();
Object entity = gotResponse.getEntity();
if (entity != null && ResponseBean.class.isAssignableFrom(entity.getClass())) {
responseBean = (ResponseBean) entity;
}
status = Status.fromStatusCode(gotResponse.getStatusInfo().getStatusCode());
}
if (responseBean == null) {
responseBean = new ResponseBean();
}
responseBean.setSuccess(false);
responseBean.setMessage(exceptionMessage);
// responseBean.set
return Response.status(status).entity(responseBean).type(mediaType).build();
}
}

View File

@ -0,0 +1,300 @@
package org.gcube.service.idm.models;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.gcube.idm.common.models.IdmProfileInterface;
public class IdmUser implements IdmProfileInterface {
protected String self; // link
protected String id;
// protected String origin;
protected Long createdTimestamp;
protected String username;
protected Boolean enabled;
// protected Boolean totp;
protected Boolean emailVerified;
protected String firstName;
protected String lastName;
protected String email;
protected String federationLink;
protected String serviceAccountClientId; // For rep, it points to clientId (not DB ID)
// @JsonDeserialize(using = StringListMapDeserializer.class)
protected Map<String, List<String>> attributes;
// protected List<CredentialRepresentation> credentials;
// protected Set<String> disableableCredentialTypes;
protected List<String> requiredActions;
// protected List<FederatedIdentityRepresentation> federatedIdentities;
protected List<String> realmRoles;
protected Map<String, List<String>> clientRoles;
// protected List<UserConsentRepresentation> clientConsents;
protected Integer notBefore;
// @Deprecated
// protected Map<String, List<String>> applicationRoles;
// @Deprecated
// protected List<SocialLinkRepresentation> socialLinks;
protected List<String> groups;
private Map<String, Boolean> access;
public String getSelf() {
return self;
}
public void setSelf(String self) {
this.self = self;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Long getCreatedTimestamp() {
return createdTimestamp;
}
public void setCreatedTimestamp(Long createdTimestamp) {
this.createdTimestamp = createdTimestamp;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Boolean isEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
// @Deprecated
// public Boolean isTotp() {
// return totp;
// }
// @Deprecated
// public void setTotp(Boolean totp) {
// this.totp = totp;
// }
public Boolean isEmailVerified() {
return emailVerified;
}
public void setEmailVerified(Boolean emailVerified) {
this.emailVerified = emailVerified;
}
public Map<String, List<String>> getAttributes() {
return attributes;
}
public void setAttributes(Map<String, List<String>> attributes) {
this.attributes = attributes;
}
// public UserRepresentation singleAttribute(String name, String value) {
// if (this.attributes == null)
// this.attributes = new HashMap<>();
// attributes.put(name, (value == null ? new ArrayList<String>() :
// Arrays.asList(value)));
// return this;
// }
// public String firstAttribute(String key) {
// return this.attributes == null ? null
// : this.attributes.get(key) == null ? null
// : this.attributes.get(key).isEmpty() ? null :
// this.attributes.get(key).get(0);
// }
// public List<CredentialRepresentation> getCredentials() {
// return credentials;
// }
// public void setCredentials(List<CredentialRepresentation> credentials) {
// this.credentials = credentials;
// }
public List<String> getRequiredActions() {
return requiredActions;
}
public void setRequiredActions(List<String> requiredActions) {
this.requiredActions = requiredActions;
}
// public List<FederatedIdentityRepresentation> getFederatedIdentities() {
// return federatedIdentities;
// }
// public void setFederatedIdentities(List<FederatedIdentityRepresentation>
// federatedIdentities) {
// this.federatedIdentities = federatedIdentities;
// }
// public List<SocialLinkRepresentation> getSocialLinks() {
// return socialLinks;
// }
// public void setSocialLinks(List<SocialLinkRepresentation> socialLinks) {
// this.socialLinks = socialLinks;
// }
public List<String> getRealmRoles() {
return realmRoles;
}
public void setRealmRoles(List<String> realmRoles) {
this.realmRoles = realmRoles;
}
public Map<String, List<String>> getClientRoles() {
return clientRoles;
}
public void setClientRoles(Map<String, List<String>> clientRoles) {
this.clientRoles = clientRoles;
}
// public List<UserConsentRepresentation> getClientConsents() {
// return clientConsents;
// }
// public void setClientConsents(List<UserConsentRepresentation> clientConsents)
// {
// this.clientConsents = clientConsents;
// }
public Integer getNotBefore() {
return notBefore;
}
public void setNotBefore(Integer notBefore) {
this.notBefore = notBefore;
}
// @Deprecated
// public Map<String, List<String>> getApplicationRoles() {
// return applicationRoles;
// }
public String getFederationLink() {
return federationLink;
}
public void setFederationLink(String federationLink) {
this.federationLink = federationLink;
}
public String getServiceAccountClientId() {
return serviceAccountClientId;
}
public void setServiceAccountClientId(String serviceAccountClientId) {
this.serviceAccountClientId = serviceAccountClientId;
}
public List<String> getGroups() {
return groups;
}
public void setGroups(List<String> groups) {
this.groups = groups;
}
// /**
// * Returns id of UserStorageProvider that loaded this user
// *
// * @return NULL if user stored locally
// */
// public String getOrigin() {
// return origin;
// }
// public void setOrigin(String origin) {
// this.origin = origin;
// }
// public Set<String> getDisableableCredentialTypes() {
// return disableableCredentialTypes;
// }
// public void setDisableableCredentialTypes(Set<String>
// disableableCredentialTypes) {
// this.disableableCredentialTypes = disableableCredentialTypes;
// }
public Map<String, Boolean> getAccess() {
return access;
}
public void setAccess(Map<String, Boolean> access) {
this.access = access;
}
public Map<String, List<String>> toAttributes() {
Map<String, List<String>> attrs = new HashMap<>();
if (getAttributes() != null)
attrs.putAll(getAttributes());
if (getUsername() != null)
attrs.put("username", Collections.singletonList(getUsername()));
else
attrs.remove("username");
if (getEmail() != null)
attrs.put("email", Collections.singletonList(getEmail()));
else
attrs.remove("email");
if (getLastName() != null)
attrs.put("lastName", Collections.singletonList(getLastName()));
if (getFirstName() != null)
attrs.put("firstName", Collections.singletonList(getFirstName()));
return attrs;
}
}

View File

@ -0,0 +1,499 @@
package org.gcube.service.idm.rest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import org.gcube.service.idm.IdMManager;
import org.gcube.service.idm.controller.AdminKeycloakController;
import org.gcube.service.idm.controller.AuthController;
import org.gcube.service.idm.controller.KCClientsController;
import org.gcube.service.idm.controller.KCGroupsController;
import org.gcube.service.idm.controller.KCRolesController;
import org.gcube.service.idm.controller.KCUserController;
import org.gcube.service.idm.keycloack.KkClientFactory;
import org.gcube.service.idm.serializers.IdmObjectSerializator;
import org.gcube.service.utils.ErrorMessages;
import org.gcube.service.utils.beans.ResponseBean;
import org.gcube.service.utils.beans.ResponseBeanMap;
import org.gcube.smartgears.annotations.ManagedBy;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.GroupResource;
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.GroupRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.webcohesion.enunciate.metadata.rs.RequestHeader;
import com.webcohesion.enunciate.metadata.rs.RequestHeaders;
import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.InternalServerErrorException;
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Response;
@ManagedBy(IdMManager.class)
@RequestHeaders({
@RequestHeader(name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources"),
@RequestHeader(name = "Content-Type", description = "application/json")
})
@Path("clients")
public class ClientsAPI {
/**
* returns the list of members of the client with the given role
*
* default client is the current client, optional clientId to show a different
* one
*
* default showed role is "Member"
*
* output format can be customized using format_users
*
* @param client_id optional clientId (aka encoded context). if absent,
* uses
* current context
*
* @param format_users response format for users. use "none" for skip
*
* @param role role to inspect, default Member.
*
* @param firstResult pagination offset
* @param maxResults maximum results size
*/
@GET
@Path("members")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response clientMembers(
@QueryParam("first") @DefaultValue("0") int firstResult,
@QueryParam("max") @DefaultValue("100") int maxResults,
@QueryParam("format_users") @DefaultValue("username") KCUserController.REPR format_users,
@QueryParam("role") @DefaultValue("Member") String role_name,
@QueryParam("client_id") String clientId) {
ResponseBean responseBean = new ResponseBean();
if (clientId != null) {
AuthController.checkIsRealmAdmin(ErrorMessages.RESERVED_PARAMETER + "client_id");
}
// RealmResource realmResource = KkClientFactory.getSingleton().getKKRealm();
// ClientResource clientResource = null;
// // select the client by name, or current client if client_name parameter is
// // null;
// if (clientId == null) {
// clientResource = KkClientFactory.getSingleton().getKKClient();
// } else {
// List<ClientRepresentation> clients =
// realmResource.clients().findByClientId(clientId);
// if (clients.size() == 0) {
// throw new NotFoundException();
// }
// String id = clients.get(0).getId();
// clientResource = realmResource.clients().get(id);
// }
// RolesResource roles_resource = clientResource.roles();
// RoleResource role_resource = roles_resource.get(role_name);
// List<UserRepresentation> user_members =
// role_resource.getUserMembers(firstResult, maxResults);
List<UserRepresentation> user_members = KCClientsController.getContextUsersByRole(clientId, role_name,
firstResult, maxResults);
Object result = KCUserController.formatList(user_members, format_users);
responseBean.setResult(result);
try {
responseBean.setSuccess(true);
ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (
JsonProcessingException e) {
e.printStackTrace();
throw new InternalServerErrorException(e);
}
}
/**
* returns the list of users of the client
* users list is a subset of members list, it's obtained from the group named as
* the context
*
* default client is the current client, optional clientId to show a different
* one
*
* output format can be customized using format_users
*
* @param client_id optional clientId (aka encoded context). if absent,
* uses
* current context
*
* @param format_users response format for users. use "none" for skip
*
* @param role list of roles to inspect, default Member. can use
* multiple times. use '__all__' to get all roles in the
* client
* @param firstResult pagination offset
* @param maxResults maximum results size
*/
@GET
@Path("users")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response clientUsers(
@QueryParam("first") @DefaultValue("0") int firstResult,
@QueryParam("max") @DefaultValue("100") int maxResults,
@QueryParam("format_users") @DefaultValue("username") KCUserController.REPR format_users,
@QueryParam("client_id") String clientId) {
ResponseBean responseBean = new ResponseBean();
if (clientId != null) {
AuthController.checkIsRealmAdmin(ErrorMessages.RESERVED_PARAMETER + "client_id");
}
List<UserRepresentation> user_members = KCClientsController.getMemberGroupUsers(clientId, firstResult,
maxResults);
Object result = KCUserController.formatList(user_members, format_users);
responseBean.setResult(result);
try {
responseBean.setSuccess(true);
ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (
JsonProcessingException e) {
e.printStackTrace();
throw new InternalServerErrorException(e);
}
}
/**
* returns an inspection of the client, showing roles and related members
* default client is the current client, optional clientId to show a different
* one
*
* output format can be customized using format_client, format_users,
* format_roles, format_groups
*
* default showed role is "Member", use role="__all__" to show all roles
*
* @param client_id optional clientId (aka encoded context). if absent,
* uses
* current context
*
* @param format_users response format for users. use "none" for skip
* @param format_roles response format for users. use "none" for skip
* @param format_client response format for client. use "none" for skip
* @param format_groups response format for group. use "none" for skip
*
* @param role list of roles to inspect, default Member. can use
* multiple times. use '__all__' to get all roles in the
* client
*/
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(ClientsAPI.class);
@GET
@Path("info")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response client(
@QueryParam("format_client") @DefaultValue("compact") KCClientsController.REPR format_client,
@QueryParam("format_users") @DefaultValue("username") KCUserController.REPR format_users,
@QueryParam("format_roles") @DefaultValue("compact") KCRolesController.REPR format_roles,
@QueryParam("format_groups") @DefaultValue("name") KCGroupsController.REPR format_group,
@QueryParam("role") @DefaultValue("Member") final List<String> roles,
@QueryParam("client_id") String clientId) {
ResponseBeanMap responseBean = new ResponseBeanMap();
if (clientId != null) {
AuthController.checkIsRealmAdmin(ErrorMessages.RESERVED_PARAMETER + "client_id");
}
// String role_name = "Member";
boolean show_groups = !format_group.equals(KCGroupsController.REPR.none);
boolean show_client = !format_client.equals(KCClientsController.REPR.none);
boolean show_users = !format_users.equals(KCUserController.REPR.none);
boolean show_roles = !format_roles.equals(KCRolesController.REPR.none);
RealmResource realmResource = KkClientFactory.getSingleton().getKKRealm();
ClientResource clientResource = null;
ClientRepresentation client = null;
// select the client by name, or current client if client_name parameter is
// null;
if (clientId == null) {
clientResource = KkClientFactory.getSingleton().getKKClient();
client = clientResource.toRepresentation();
} else {
List<ClientRepresentation> clients = realmResource.clients().findByClientId(clientId);
if (clients.size() == 0) {
throw new NotFoundException();
}
String id = clients.get(0).getId();
clientResource = realmResource.clients().get(id);
client = clientResource.toRepresentation();
}
if (show_client)
responseBean.putResult("client", KCClientsController.formatRepr(client,
format_client));
if (format_client.equals(KCClientsController.REPR.full)) {
UserRepresentation service_account_user = clientResource.getServiceAccountUser();
responseBean.putResult("service_account_user",
KCUserController.formatRepr(service_account_user, format_users));
}
HashMap<String, Object> roles_dict = new HashMap<String, Object>();
responseBean.putResult("roles", roles_dict);
RolesResource roles_resource = clientResource.roles();
List<RoleRepresentation> roles_list = null;
if (roles == null || roles.isEmpty() || roles.contains("__all__")) {
roles_list = roles_resource.list();
} else {
roles_list = new ArrayList<RoleRepresentation>();
for (String role_name : roles) {
try {
RoleResource role_resource = roles_resource.get(role_name);
RoleRepresentation role_repr = role_resource.toRepresentation();
roles_list.add(role_repr);
} catch (Exception e) {
roles_dict.put(role_name, null);
continue;
}
}
}
for (RoleRepresentation role_repr : roles_list) {
String role_name = role_repr.getName();
RoleResource role_resource = roles_resource.get(role_name);
HashMap<String, Object> role_dict = new HashMap<String, Object>();
roles_dict.put(role_name, role_dict);
if (show_roles) {
role_dict.put("role", KCRolesController.formatRepr(role_repr, format_roles));
}
if (show_users) {
List<UserRepresentation> user_members = role_resource.getUserMembers();
role_dict.put("members",
KCUserController.formatList(user_members, format_users));
}
if (show_groups) {
HashMap<String, HashMap<String, Object>> groups_hash = new HashMap<String, HashMap<String, Object>>();
role_dict.put("groups", groups_hash);
Set<GroupRepresentation> groups_members = clientResource.roles().get(role_name).getRoleGroupMembers();
for (GroupRepresentation g_repr : groups_members) {
HashMap<String, Object> group_dict = new HashMap<String, Object>();
groups_hash.put(g_repr.getPath(), group_dict);
group_dict.put("group", KCGroupsController.formatRepr(g_repr, format_group));
if (show_users) {
GroupResource g = realmResource.groups().group(g_repr.getId());
List<UserRepresentation> members = g.members();
group_dict.put("members",
KCUserController.formatList(members, format_users));
}
}
}
}
try {
responseBean.setSuccess(true);
ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (
JsonProcessingException e) {
e.printStackTrace();
throw new InternalServerErrorException(e);
}
}
// cannot pass the client name in the path. %2f blocked by tomcat security
// @GET
// @Path("/{name}")
// @Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
// public Response clientByName(
// @PathParam("name") String client_name) {
// ResponseBeanMap responseBean = new ResponseBeanMap();
// try {
// RealmResource realm = KkClientFactory.getSingleton().getKKRealm();
// List<ClientRepresentation> clients =
// realm.clients().findByClientId(client_name);
// if (clients.size() == 0) {
// throw new NotFoundException();
// }
// String id = clients.get(0).getId();
// ClientResource clientResource = realm.clients().get(id);
// ClientRepresentation client = clientResource.toRepresentation();
// UserRepresentation service_account_user =
// clientResource.getServiceAccountUser();
// responseBean.putResult("client", client);
// responseBean.putResult("service_account_user", service_account_user);
// Set<GroupRepresentation> members =
// clientResource.roles().get("Member").getRoleGroupMembers();
// responseBean.putResult("members", members);
// responseBean.setSuccess(true);
// ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
// String jsonData = objectMapper.writeValueAsString(responseBean);
// return Response.ok(jsonData).build();
// } catch (JsonProcessingException e) {
// e.printStackTrace();
// throw new InternalServerErrorException(e);
// }
// }
public class ClientFromTemplateParams {
String client_name;
String client_id;
String context;
}
@POST
@Path("fromTemplate/{name}")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response createClientFromTemplate(
@PathParam("name") String template_name,
ClientFromTemplateParams params) {
ResponseBeanMap responseBean = new ResponseBeanMap();
try {
RealmResource realm = KkClientFactory.getSingleton().getKKRealm();
List<ClientRepresentation> clients = realm.clients().findByClientId(template_name);
if (clients.size() == 0) {
throw new NotFoundException();
}
String id = clients.get(0).getId();
ClientResource clientResource = realm.clients().get(id);
ClientRepresentation client = clientResource.toRepresentation();
UserRepresentation template_account_user = clientResource.getServiceAccountUser();
client.setId(params.client_id);
client.setName(params.client_name);
responseBean.putResult("client", client);
responseBean.putResult("service_account_user", template_account_user);
responseBean.setSuccess(true);
ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new InternalServerErrorException(e);
}
}
@POST
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response createClientFromTemplate(ClientRepresentation client) {
logger.info("received client {}", client);
ResponseBeanMap responseBean = new ResponseBeanMap();
try {
logger.info("received client {}", client);
ClientRepresentation newClient = AdminKeycloakController.createClient(client);
responseBean.putResult("input", client);
responseBean.putResult("created", newClient);
responseBean.setSuccess(true);
ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new InternalServerErrorException(e);
}
}
@POST
@Path("/")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response createClient(ClientRepresentation client) {
ResponseBeanMap responseBean = new ResponseBeanMap();
try {
logger.info("received client {}", client);
ClientRepresentation newClient = AdminKeycloakController.createClient(client);
responseBean.putResult("input", client);
responseBean.putResult("created", newClient);
responseBean.setSuccess(true);
ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new InternalServerErrorException(e);
}
}
}

View File

@ -0,0 +1,96 @@
package org.gcube.service.idm.rest;
import java.util.HashMap;
import java.util.Map;
import org.gcube.service.idm.controller.AuthController;
import org.gcube.service.idm.controller.JWTController;
import org.gcube.service.idm.serializers.IdmObjectSerializator;
import org.gcube.service.utils.ErrorMessages;
import org.gcube.service.utils.beans.ResponseBean;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.webcohesion.enunciate.metadata.rs.RequestHeader;
import com.webcohesion.enunciate.metadata.rs.RequestHeaders;
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.InternalServerErrorException;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;
@RequestHeaders({
@RequestHeader(name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources"),
@RequestHeader(name = "Content-Type", description = "application/json")
})
@Path("jwt")
public class JwtAPI {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(JwtAPI.class);
@GET
@Path("/decode")
@Produces(MediaType.APPLICATION_JSON)
@StatusCodes({
@ResponseCode(code = 200, condition = "decode the token"),
@ResponseCode(code = 500, condition = ErrorMessages.ERROR_IN_API_RESULT)
})
public Response getDecodedJwtToken(
@QueryParam("token") String token) {
// Status status = Status.OK;
ResponseBean responseBean = new ResponseBean();
try {
ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
Map<String, Object> decoded = JWTController.decodeJwtToken(token);
responseBean.setResult(decoded);
responseBean.setSuccess(true);
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new InternalServerErrorException(e);
}
}
@GET
@Path("/auth")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
@StatusCodes({
@ResponseCode(code = 200, condition = "The user's email is reported in the 'result' field of the returned object"),
@ResponseCode(code = 500, condition = ErrorMessages.ERROR_IN_API_RESULT)
})
public Response getDecodedJwtAuth() {
Status status = Status.OK;
ResponseBean responseBean = new ResponseBean();
String token = AuthController.getAccessToken();
try {
ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
Map<String, Object> response = new HashMap<String, Object>();
response.put("auth_token", JWTController.decodeJwtToken(token));
// response.put("authorizations", authorizations);
responseBean.setResult(response);
responseBean.setSuccess(true);
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new InternalServerErrorException(e);
}
}
}

View File

@ -0,0 +1,213 @@
package org.gcube.service.idm.rest;
import java.util.List;
import java.util.Set;
import org.gcube.service.idm.IdMManager;
import org.gcube.service.idm.controller.KCRolesController;
import org.gcube.service.idm.controller.KCUserController;
import org.gcube.service.idm.keycloack.KkClientFactory;
import org.gcube.service.idm.serializers.IdmObjectSerializator;
import org.gcube.service.utils.beans.ResponseBean;
import org.gcube.service.utils.beans.ResponseBeanMap;
import org.gcube.service.utils.beans.ResponseBeanPaginated;
import org.gcube.smartgears.annotations.ManagedBy;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.RoleResource;
import org.keycloak.admin.client.resource.RolesResource;
import org.keycloak.representations.idm.GroupRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.webcohesion.enunciate.metadata.rs.RequestHeader;
import com.webcohesion.enunciate.metadata.rs.RequestHeaders;
import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.InternalServerErrorException;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
@ManagedBy(IdMManager.class)
@RequestHeaders({
@RequestHeader(name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources"),
@RequestHeader(name = "Content-Type", description = "application/json")
})
@Path("roles")
public class RolesAPI {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(RolesAPI.class);
/**
* Returns roles in context
*
* @param format roles response format
* @param firstResult pagination offset
* @param maxResults maximum results size
* @param search filter by name
*/
@GET
@Path("/")
@Produces(MediaType.APPLICATION_JSON)
public Response search(@QueryParam("search") @DefaultValue("") String search,
@QueryParam("first") @DefaultValue("0") int firstResult,
@QueryParam("max") @DefaultValue("100") int maxResults,
@QueryParam("format") @DefaultValue("name") KCRolesController.REPR format) {
ResponseBean responseBean = new ResponseBeanPaginated(firstResult, maxResults);
Boolean briefRepresentation = !KCRolesController.REPR.full.equals(format);
try {
ClientResource client = KkClientFactory.getSingleton().getKKClient();
List<RoleRepresentation> roles = client.roles().list(search, firstResult, maxResults, briefRepresentation);
responseBean.setResult(KCRolesController.formatList(roles, format));
responseBean.setSuccess(true);
ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new InternalServerErrorException(e);
}
}
/**
* Returns role by name
*
* @path role_name the role
* 'email' and 'username' must match exactly. default true
*/
@GET
@Path("/{role_name}")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response role(
@PathParam("role_name") String role_name) {
ResponseBean responseBean = new ResponseBean();
try {
RoleRepresentation role = KCRolesController.getRoleByNameCurrent(role_name);
responseBean.setResult(role);
responseBean.setSuccess(true);
ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new InternalServerErrorException(e);
}
}
/**
* Returns the list of members (users + services_accounts) with the given role
* in the context
*
* @param format users response format
* @param role_name the role
* @param firstResult pagination offset
* @param maxResults maximum results size
*/
@GET
@Path("/{role_name}/members")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response membersForRole(
@PathParam("role_name") String role_name,
@QueryParam("first") @DefaultValue("0") int firstResult,
@QueryParam("max") @DefaultValue("100") int maxResults,
@QueryParam("format") @DefaultValue("username") KCUserController.REPR format) {
ResponseBean responseBean = new ResponseBeanPaginated(firstResult, maxResults);
try {
ClientResource client = KkClientFactory.getSingleton().getKKClient();
RolesResource roles_resource = client.roles();
RoleResource r = roles_resource.get(role_name);
List<UserRepresentation> users = r.getUserMembers(firstResult, maxResults);
responseBean.setResult(KCUserController.formatList(users, format));
responseBean.setSuccess(true);
ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new InternalServerErrorException(e);
}
}
/**
* Returns the list of users with role in the context
*
* @param format users response format
* @param role_name the role
* @param firstResult pagination offset
* @param maxResults maximum results size
*/
@GET
@Path("/{role_name}/users")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response usersForRole(
@PathParam("role_name") String role_name,
@QueryParam("first") @DefaultValue("0") int firstResult,
@QueryParam("max") @DefaultValue("100") int maxResults,
@QueryParam("format") @DefaultValue("username") KCUserController.REPR format) {
ResponseBeanMap responseBean = new ResponseBeanMap();
try {
ClientResource client = KkClientFactory.getSingleton().getKKClient();
RolesResource roles_resource = client.roles();
RoleResource r = roles_resource.get(role_name);
// ruoli che danno quel
Set<GroupRepresentation> groups = r.getRoleGroupMembers(firstResult, maxResults);
responseBean.putResult("roleGroupMembers", groups);
List<UserRepresentation> users = r.getUserMembers();
responseBean.putResult("users", users);
ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new InternalServerErrorException(e);
}
// throw new InternalServerErrorException("not implemented");
}
/**
* Returns the list of users with role in the context
*
* @param format users response format
* @param role_name the role
* @param firstResult pagination offset
* @param maxResults maximum results size
*/
@GET
@Path("/{role_name}/service_accounts")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response servicesAccountsForRole(
@PathParam("role_name") String role_name,
@QueryParam("first") @DefaultValue("0") int firstResult,
@QueryParam("max") @DefaultValue("100") int maxResults,
@QueryParam("format") @DefaultValue("username") KCUserController.REPR format) {
throw new InternalServerErrorException("not implemented");
}
}

View File

@ -0,0 +1,126 @@
package org.gcube.service.idm.rest;
import org.gcube.common.security.Owner;
import org.gcube.common.security.providers.SecretManagerProvider;
import org.gcube.common.security.secrets.Secret;
import org.gcube.service.idm.IdMManager;
import org.gcube.service.idm.controller.LiferayProfileClient;
import org.gcube.service.utils.ErrorMessages;
import org.gcube.service.utils.beans.ResponseBeanMap;
import org.gcube.smartgears.annotations.ManagedBy;
import org.gcube.vomanagement.usermanagement.model.GCubeUser;
import org.slf4j.LoggerFactory;
import com.webcohesion.enunciate.metadata.rs.RequestHeader;
import com.webcohesion.enunciate.metadata.rs.RequestHeaders;
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
import jakarta.ws.rs.ForbiddenException;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;
/**
* @responseExample application/json {
* "success": true,
* "message": null,
* "result": {
* "roles": [
* "Catalogue-Editor",
* "Member"
* ],
* "context": "[CONTEXT]",
* "avatar":
* "https://www.d4science.org:443/image/user_male_portrait?img_id=0&img_id_token=AAABBBCCC",
* "fullname": "FirstName LastName",
* "username": "user.name"
* }
* }
*/
@ManagedBy(IdMManager.class)
@RequestHeaders({
@RequestHeader(name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources"),
@RequestHeader(name = "Content-Type", description = "application/json")
})
@Path("2/people")
// @ResourceGroup("Users APIs")
// @ResourceLabel("Greetings APIs")
// @RequestHeaders({
// @RequestHeader(name = "Authorization", description = "Bearer token, see <a
// href=\"https://dev.d4science.org/how-to-access-resources\">https://dev.d4science.org/how-to-access-resources</a>")
// })
public class SocialPeopleAPI {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(SocialPeopleAPI.class);
/**
* @responseExample application/json { "success" : true, "message" : null,
* "result" : { "roles" : [ ], "context" : "***", "avatar" :
* "https://*****3D", "fullname" : "John Smith", "username" :
* "john.smith" } }
* @return the user's profile. The user in this case is the one bound to the
* token
*/
@Produces(MediaType.APPLICATION_JSON)
@GET
@Path("profile")
@StatusCodes({
@ResponseCode(code = 200, condition = "Successful retrieval of user's profile, reported in the 'result' field of the returned object"),
@ResponseCode(code = 500, condition = ErrorMessages.ERROR_IN_API_RESULT)
})
public Response getProfile() {
ResponseBeanMap responseBean = new ResponseBeanMap();
Status status = Status.OK;
Secret secret = SecretManagerProvider.get();
Owner owner = secret.getOwner();
String username = owner.getId();
String context = secret.getContext();
if (owner.isApplication()) {
logger.warn("Trying to access users method via a token different than USER is not allowed");
// only users can use "me"
throw new ForbiddenException(ErrorMessages.NOT_USER_TOKEN_CONTEXT_USED);
}
try {
GCubeUser profile = LiferayProfileClient.getUserProfileByUsername(username);
responseBean.putResult("username", username);
responseBean.putResult("avatar", profile.getUserAvatarURL());
responseBean.putResult("fullname", owner.getFirstName() + " " + owner.getLastName());
responseBean.putResult("context", context);
responseBean.putResult("roles", owner.getRoles());
// UserManager userManager =
// UserManagerWSBuilder.getInstance().getUserManager();
// RoleManager roleManager =
// RoleManagerWSBuilder.getInstance().getRoleManager();
// GroupManager groupManager =
// GroupManagerWSBuilder.getInstance().getGroupManager();
// user = userManager.getUserByUsername(username);
// List<GCubeRole> roles = roleManager.listRolesByUserAndGroup(user.getUserId(),
// groupManager.getGroupIdFromInfrastructureScope(scope));
// List<String> rolesNames = new ArrayList<String>();
// for (GCubeRole gCubeRole : roles) {
// rolesNames.add(gCubeRole.getRoleName());
// }
// toReturn.put("roles", rolesNames);
responseBean.setSuccess(true);
} catch (Exception e) {
logger.error("Unable to retrieve user's profile", e);
responseBean.setMessage(e.getMessage());
status = Status.INTERNAL_SERVER_ERROR;
}
return Response.status(status).entity(responseBean).build();
}
}

View File

@ -0,0 +1,503 @@
package org.gcube.service.idm.rest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.gcube.common.security.Owner;
import org.gcube.common.security.providers.SecretManagerProvider;
import org.gcube.common.security.secrets.Secret;
import org.gcube.service.idm.IdMManager;
import org.gcube.service.idm.controller.AuthController;
import org.gcube.service.idm.controller.KCUserController;
import org.gcube.service.idm.controller.LiferayProfileClient;
import org.gcube.service.idm.keycloack.KkClientFactory;
import org.gcube.service.idm.serializers.IdmObjectSerializator;
import org.gcube.service.utils.ErrorMessages;
import org.gcube.service.utils.beans.ResponseBean;
import org.gcube.smartgears.annotations.ManagedBy;
import org.gcube.vomanagement.usermanagement.model.GCubeUser;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.RoleResource;
import org.keycloak.admin.client.resource.RolesResource;
import org.keycloak.admin.client.resource.UsersResource;
import org.keycloak.representations.idm.UserRepresentation;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
import jakarta.validation.ValidationException;
import jakarta.validation.constraints.NotNull;
import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.ForbiddenException;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;
@ManagedBy(IdMManager.class)
@Path("2/users")
// @ResourceGroup("Users APIs")
// @ResourceLabel("Greetings APIs")
// @RequestHeaders({
// @RequestHeader(name = "Authorization", description = "Bearer token, see <a
// href=\"https://dev.d4science.org/how-to-access-resources\">https://dev.d4science.org/how-to-access-resources</a>")
// })
public class SocialUsersAPI {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(SocialUsersAPI.class);
/**
* Get the profile associated to the token
*
* @responseExample application/json { "success" : true, "message" : null,
* "result" : { "user_id" : 23769487, "username" :
* "john.smith", "email" : "********", "first_name" : "John",
* "middle_name" : "", "last_name" : "Smith", "fullname" :
* "John Smith", "registration_date" : 1475151491415,
* "user_avatar_url" : "https://******D", "male" : true,
* "job_title" : "", "location_industry" : "no",
* "custom_attrs_map" : null, "email_addresses" : [ ],
* "screen_name" : "john.smith", "user_avatar_id" :
* "https://****sY%3D" } }
* @return the user's profile. The user is the one owning the token
*/
@GET
@Path("get-profile")
@Produces(MediaType.APPLICATION_JSON)
@StatusCodes({
@ResponseCode(code = 200, condition = "The user's profile is reported in the 'result' field of the returned object"),
@ResponseCode(code = 500, condition = ErrorMessages.ERROR_IN_API_RESULT)
})
public Response getUserProfile() {
ResponseBean responseBean = new ResponseBean();
Status status = Status.OK;
try {
Secret secret = SecretManagerProvider.get();
Owner owner = secret.getOwner();
String username = owner.getId();
GCubeUser profile = LiferayProfileClient.getUserProfileByUsername(username);
responseBean.setResult(profile);
responseBean.setResult(profile);
responseBean.setSuccess(true);
ObjectMapper objectMapper = new ObjectMapper()
.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (Exception e) {
logger.error("Unable to retrieve user's profile", e);
responseBean.setMessage(e.getMessage());
status = Status.INTERNAL_SERVER_ERROR;
return Response.status(status).entity(responseBean).build();
}
}
/**
* Read the user's email address. The user is the one owning the token
*
* @return rhe user's email address
*
* @responseExample application/json {
* "success": true,
* "message": null,
* "result": "email@isti.cnr.it"
* }
*/
@GET
@Path("/get-email")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response getCurrentEmail() {
ResponseBean responseBean = new ResponseBean();
Status status = Status.OK;
try {
Secret secret = SecretManagerProvider.get();
Owner owner = secret.getOwner();
String email = owner.getEmail();
responseBean.setResult(email);
responseBean.setSuccess(true);
} catch (Exception e) {
logger.error("Unable to retrieve user's email", e);
responseBean.setMessage(e.getMessage());
status = Status.INTERNAL_SERVER_ERROR;
}
return Response.status(status).entity(responseBean).build();
}
/**
* Read the user's fullname. The user is the one owning the token
*
* @return the user's fullname
*
* @responseExample application/json {
* "success": true,
* "message": null,
* "result": "FirstName LastName"
* }
*/
@GET
@Path("get-fullname")
@Produces(MediaType.APPLICATION_JSON)
@StatusCodes({
@ResponseCode(code = 200, condition = "The user's fullname is reported in the 'result' field of the returned object"),
@ResponseCode(code = 500, condition = ErrorMessages.ERROR_IN_API_RESULT)
})
public Response getUserFullname() {
ResponseBean responseBean = new ResponseBean();
Status status = Status.OK;
Secret secret = SecretManagerProvider.get();
Owner owner = secret.getOwner();
String username = owner.getId();
String fullName = null;
if (owner.isApplication()) {
logger.warn("Trying to access users method via a token different than USER is not allowed");
// only users can use "me"
throw new ForbiddenException(ErrorMessages.NOT_USER_TOKEN_CONTEXT_USED);
}
try {
GCubeUser profile = LiferayProfileClient.getUserProfileByUsername(username);
fullName = profile.getFullname();
logger.info("Found fullname " + fullName + " for user " + username);
responseBean.setResult(fullName);
responseBean.setSuccess(true);
} catch (Exception e) {
logger.error("Unable to retrieve attribute for user.", e);
status = Status.INTERNAL_SERVER_ERROR;
}
return Response.status(status).entity(responseBean).build();
}
@GET
@Path("/get-all-usernames")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response getUsernamesByRole(
@QueryParam("first") @DefaultValue("0") int first,
@QueryParam("max") @DefaultValue("100") int max,
@QueryParam("firstResult") @DefaultValue("0") int firstResult,
@QueryParam("maxResults") @DefaultValue("100") int maxResults) {
if (firstResult > 0) {
first = firstResult;
}
if (maxResults != 100) {
max = maxResults;
}
Status status = Status.OK;
ResponseBean responseBean = new ResponseBean();
try {
List<UserRepresentation> users = KCUserController.contextUsers(first, max);
Object result = KCUserController.formatList(users, KCUserController.REPR.compact);
responseBean.setResult(result);
responseBean.setSuccess(true);
ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (JsonProcessingException e) {
e.printStackTrace();
return Response.serverError().build();
} catch (Exception e) {
logger.error("Unable to retrieve users with the requested role", e);
// responseBean.setMessage(e.getMessage());
status = Status.INTERNAL_SERVER_ERROR;
}
return Response.status(status).entity(responseBean).build();
}
@GET
@Path("/get-all-fullnames-and-usernames")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response getAllUsernamesFullnames(
@QueryParam("emailVerified") Boolean emailVerified,
@QueryParam("enabled") Boolean enabled,
@QueryParam("first") @DefaultValue("0") int first,
@QueryParam("max") @DefaultValue("100") int max,
@QueryParam("firstResult") @DefaultValue("0") int firstResult,
@QueryParam("maxResults") @DefaultValue("100") int maxResults) {
if (firstResult > 0) {
first = firstResult;
}
if (maxResults != 100) {
max = maxResults;
}
Status status = Status.OK;
ResponseBean responseBean = new ResponseBean();
try {
UsersResource users_resource = KCUserController.realmUsersResource();
List<UserRepresentation> users = users_resource.search(emailVerified, first, max, enabled,
true);
Map<String, String> usernamesAndFullnames = new HashMap<String, String>();
users.forEach(user -> usernamesAndFullnames.put(user.getUsername(), user.getEmail()));
responseBean.setResult(usernamesAndFullnames);
responseBean.setSuccess(true);
} catch (Exception e) {
logger.error("Unable to retrieve users", e);
// responseBean.setMessage(e.getMessage());
status = Status.INTERNAL_SERVER_ERROR;
}
return Response.status(status).entity(responseBean).build();
}
@GET
@Path("/get-usernames-by-role")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response getUsernamesByRole(
@QueryParam("role-name") String roleName,
@QueryParam("first") @DefaultValue("0") int first,
@QueryParam("max") @DefaultValue("100") int max,
@QueryParam("firstResult") @DefaultValue("0") int firstResult,
@QueryParam("maxResults") @DefaultValue("100") int maxResults) {
if (firstResult > 0) {
first = firstResult;
}
if (maxResults != 100) {
max = maxResults;
}
Status status = Status.OK;
ResponseBean responseBean = new ResponseBean();
List<String> usernames = new ArrayList<String>();
try {
List<UserRepresentation> users = KCUserController.searchUsersByRole(roleName, first, max);
if (users != null) {
for (UserRepresentation user : users) {
usernames.add(user.getUsername());
}
}
responseBean.setResult(usernames);
responseBean.setSuccess(true);
ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (JsonProcessingException e) {
e.printStackTrace();
return Response.serverError().build();
} catch (Exception e) {
logger.error("Unable to retrieve users with the requested role", e);
responseBean.setMessage(e.getMessage());
status = Status.INTERNAL_SERVER_ERROR;
return Response.status(status).entity(responseBean).build();
}
}
@GET
@Path("/user-exists")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response checkUserExists(@QueryParam("username") String username) {
Status status = Status.OK;
ResponseBean responseBean = new ResponseBean();
try {
UserRepresentation user = KCUserController.getUserByUsername(username);
boolean user_exists = user != null;
responseBean.setResult(user_exists);
responseBean.setSuccess(true);
ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (JsonProcessingException e) {
e.printStackTrace();
return Response.serverError().build();
} catch (Exception e) {
logger.error("Unable to check if user exists with username " + username, e);
// responseBean.setMessage(e.getMessage());
status = Status.INTERNAL_SERVER_ERROR;
}
return Response.status(status).entity(responseBean).build();
}
/**
* Read a user's custom attribute. The user is the one owning the token
*
* @param attributeKey The key of the attribute to be read
* @return the user's custom attribute
* @throws ValidationException
*/
@GET
@Path("get-custom-attribute")
@Produces(MediaType.APPLICATION_JSON)
@StatusCodes({
@ResponseCode(code = 200, condition = "Successful read of the attribute, reported in the 'result' field of the returned object"),
@ResponseCode(code = 404, condition = ErrorMessages.INVALID_ATTRIBUTE),
@ResponseCode(code = 500, condition = ErrorMessages.ERROR_IN_API_RESULT)
})
public Response readCustomAttr(
@QueryParam("username") String username,
@QueryParam("attribute") @NotNull(message = "attribute name is missing") String attributeKey)
throws ValidationException {
ResponseBean responseBean = new ResponseBean();
Status status = Status.OK;
Secret secret = SecretManagerProvider.get();
Owner owner = secret.getOwner();
if (username == null || username.equals("me")) {
if (owner.isApplication()) {
logger.warn("Trying to access users method via a token different than USER is not allowed");
// only users can use "me"
throw new ForbiddenException(ErrorMessages.NOT_USER_TOKEN_CONTEXT_USED);
}
username = owner.getId();
}
if (!AuthController.checkAnyRole(AuthController.ACCESS_READ_ROLES) && !username.equals(owner.getId())) {
// the user can see only his profile
throw new ForbiddenException(ErrorMessages.USER_NOT_AUTHORIZED_PRIVATE);
}
UserRepresentation user = KCUserController.getUserByUsername(username);
Map<String, List<String>> attributes = user.getAttributes();
if (attributes.containsKey(attributeKey)) {
responseBean.setResult(attributes.get(attributeKey));
responseBean.setSuccess(true);
} else {
responseBean.setSuccess(false);
String msg = String.format("Unable to retrieve attribute %s for user %s", attributeKey, user.getUsername());
responseBean.setMessage(msg);
logger.error(msg);
status = Status.NOT_FOUND;
}
return Response.status(status).entity(responseBean).build();
}
@GET
@Path("/get-oauth-profile")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response getCurrentOAuthProfile() {
Status status = Status.OK;
ResponseBean responseBean = new ResponseBean();
try {
Secret secret = SecretManagerProvider.get();
Owner owner = secret.getOwner();
UserRepresentation user = KCUserController.getUserByUsername(owner.getId());
responseBean.setResult(user);
responseBean.setSuccess(true);
ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (JsonProcessingException e) {
e.printStackTrace();
return Response.serverError().build();
} catch (Exception e) {
logger.error(ErrorMessages.CANNOT_RETRIEVE_PROFILE);
// responseBean.setMessage(e.getMessage());
status = Status.INTERNAL_SERVER_ERROR;
}
return Response.status(status).entity(responseBean).build();
}
/**
* Get the list of users having a given global-role, e.g. 'Administrator'.
* (Legacy)
*
* @param roleName the name of the role to be checked (e.g. Administrator)
* @return the list of users having a given global-role
*/
@GET
@Path("get-usernames-by-global-role")
@Produces(MediaType.APPLICATION_JSON)
@StatusCodes({
@ResponseCode(code = 200, condition = "The list is put into the 'result' field of the returned object"),
@ResponseCode(code = 500, condition = ErrorMessages.ERROR_IN_API_RESULT)
})
public Response getUsernamesByGlobalRole(
@QueryParam("role-name") String role_name,
@QueryParam("first") @DefaultValue("0") int first,
@QueryParam("max") @DefaultValue("100") int max,
@QueryParam("firstResult") @DefaultValue("0") int firstResult,
@QueryParam("maxResults") @DefaultValue("100") int maxResults) {
if (firstResult > 0) {
first = firstResult;
}
if (maxResults != 100) {
max = maxResults;
}
ResponseBean responseBean = new ResponseBean();
Status status = Status.OK;
Secret secret = SecretManagerProvider.get();
Owner owner = secret.getOwner();
if (!owner.isApplication()) {
logger.warn(ErrorMessages.NOT_USER_TOKEN_CONTEXT_USED);
throw new ForbiddenException(ErrorMessages.NOT_SERVICE_TOKEN_CONTEXT_USED);
}
RealmResource realmResource = KkClientFactory.getSingleton().getKKRealm();
RolesResource roles_resource = realmResource.roles();
RoleResource r = roles_resource.get(role_name);
List<UserRepresentation> users = r.getUserMembers(first, max);
responseBean.setResult(KCUserController.formatList(users, KCUserController.REPR.username));
responseBean.setSuccess(true);
return Response.status(status).entity(responseBean).build();
}
}

View File

@ -0,0 +1,508 @@
package org.gcube.service.idm.rest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.gcube.common.security.Owner;
import org.gcube.common.security.providers.SecretManagerProvider;
import org.gcube.common.security.secrets.Secret;
import org.gcube.service.idm.IdMManager;
import org.gcube.service.idm.controller.AuthController;
import org.gcube.service.idm.controller.JWTController;
import org.gcube.service.idm.controller.KCUserController;
import org.gcube.service.idm.controller.LiferayProfileClient;
import org.gcube.service.idm.keycloack.KkClientFactory;
import org.gcube.service.idm.serializers.IdmObjectSerializator;
import org.gcube.service.utils.ErrorMessages;
import org.gcube.service.utils.beans.ResponseBean;
import org.gcube.service.utils.beans.ResponseBeanMap;
import org.gcube.service.utils.beans.ResponseBeanPaginated;
import org.gcube.smartgears.annotations.ManagedBy;
import org.gcube.vomanagement.usermanagement.model.GCubeUser;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.UserResource;
import org.keycloak.representations.idm.GroupRepresentation;
import org.keycloak.representations.idm.MappingsRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.webcohesion.enunciate.metadata.rs.RequestHeader;
import com.webcohesion.enunciate.metadata.rs.RequestHeaders;
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.ForbiddenException;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.InternalServerErrorException;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
/**
* <p>
* The REST API to interact with the keycloak users
* </p>
*
* <p>
* "Member" users can only invoke metods on his own user (/me)
* </p>
*
* <p>
* Users with "idm-service-read" or "idm-service-admin" role can invoke metods
* on all the users
* </p>
*
* @author Alfredo Oliviero (ISTI - CNR)
*/
@ManagedBy(IdMManager.class)
@RequestHeaders({
@RequestHeader(name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources"),
@RequestHeader(name = "Content-Type", description = "application/json")
})
@Path("users")
public class UserAPI {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(UserAPI.class);
/**
* Returns infos about the authenticated user
*
* <ul>
* <li>owner: the authenticated user</li>
* <li>profile: the profile of the user in the Liferay CMS (only if the user is
* not a service)</li>
* <li>user: the user representation from the authentication service</li>
* </uL>
*
* if the optional parameter inspect is passed as true, returns additional
* values:
* <ul>
* <li>verify: the result of introspection of the auth token on the
* authentication service</li>
* <li>roles: the authenticated user</li>
* <li>groups: the authenticated user</li>
* <li>groupRolesRealm: ...</li>
* <li>groupRolesClients: ...</li>
* </ul>
*
* @param inspect adds additional inspection values to the result
* @returns infos about the authenticated user
*
*/
@GET
@Path("/me")
@Produces(MediaType.APPLICATION_JSON)
@StatusCodes({
@ResponseCode(code = 200, condition = "current user informations"),
@ResponseCode(code = 403, condition = ErrorMessages.NOT_USER_TOKEN_CONTEXT_USED),
@ResponseCode(code = 404, condition = ErrorMessages.INVALID_ATTRIBUTE),
@ResponseCode(code = 500, condition = ErrorMessages.ERROR_IN_API_RESULT)
})
public Response getMe(
@QueryParam("inspect") @DefaultValue("false") Boolean inspect) {
logger.info("/users/me");
ResponseBean responseBean = new ResponseBean();
ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
Secret secret = SecretManagerProvider.get();
Owner owner = secret.getOwner();
String username = owner.getId();
// if (owner.isApplication()) {
// // only users can use "me"
// throw new ForbiddenException(ErrorMessages.NOT_USER_TOKEN_CONTEXT_USED);
// }
Map<String, Object> result = getUserData(username, !owner.isApplication(), inspect);
responseBean.setResult(result);
result.put("owner", owner);
try {
if (inspect) {
String token = AuthController.getAccessToken();
result.put("verify", JWTController.decodeJwtToken(token));
}
responseBean.setSuccess(true);
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new InternalServerErrorException(e);
}
}
/**
* Returns informations about the user received as parameter
*
* Only users with "idm-service-read" or "idm-service-admin" role can invoke
* this method
*
* <ul>
* <li>profile: the profile of the user in the Liferay CMS (only if the user is
* not a service)</li>
* <li>user: the user representation from the authentication service</li>
* </uL>
*
* if the optional parameter inspect is passed as true, returns additional
* values:
* <ul>
* <li>roles: the authenticated user</li>
* <li>groups: the authenticated user</li>
* <li>groupRolesRealm: ...</li>
* <li>groupRolesClients: ...</li>
* </ul>
*
* @param username the username of the user
* @param inspect adds additional inspection values to the result
* @returns infos about the user
*/
@GET
@Path("/{username}")
@Produces(MediaType.APPLICATION_JSON)
@StatusCodes({
@ResponseCode(code = 200, condition = "user informations"),
@ResponseCode(code = 403, condition = ErrorMessages.NOT_USER_TOKEN_CONTEXT_USED),
@ResponseCode(code = 404, condition = ErrorMessages.INVALID_ATTRIBUTE),
@ResponseCode(code = 500, condition = ErrorMessages.ERROR_IN_API_RESULT)
})
public Response getUser(
@PathParam("username") String username,
@QueryParam("inspect") @DefaultValue("false") Boolean inspect) {
ResponseBeanMap responseBean = new ResponseBeanMap();
ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
if (!AuthController.checkAnyRole(AuthController.ACCESS_READ_ROLES)) {
// the user can see only his profile
throw new ForbiddenException(ErrorMessages.USER_NOT_AUTHORIZED_PRIVATE);
}
Secret secret = SecretManagerProvider.get();
Owner owner = secret.getOwner();
try {
Map<String, Object> result = getUserData(username, !owner.isApplication(), inspect);
responseBean.setResult(result);
responseBean.setSuccess(true);
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new InternalServerErrorException(e);
}
}
protected Map<String, Object> getUserData(String username, Boolean getProfile, Boolean isInspect) {
Map<String, Object> result = new HashMap<>();
UserRepresentation user = KCUserController.getUserByUsername(username);
result.put("user", user);
try {
if (getProfile) {
GCubeUser profile = LiferayProfileClient.getUserProfileByUsername(username);
result.put("profile", profile);
// throw new ForbiddenException(ErrorMessages.NOT_USER_TOKEN_CONTEXT_USED);
}
} catch (Exception e) {
e.printStackTrace();
// throw new ServiceUnavailableException("Liferay portal: " + e.getMessage());
result.put("profile", null);
}
if (isInspect) {
UserResource userResource = KCUserController.getUserResourceByUsername(username);
MappingsRepresentation roles = userResource.roles().getAll();
result.put("roles", roles);
List<GroupRepresentation> groups = userResource.groups();
result.put("groups", groups);
HashMap<String, Object> groupRolesRealm = new HashMap<String, Object>();
HashMap<String, Object> groupRolesClients = new HashMap<String, Object>();
result.put("groupRolesRealm", groupRolesRealm);
result.put("groupRolesClients", groupRolesClients);
for (GroupRepresentation g : groups) {
groupRolesClients.put(g.getId(), g.getClientRoles());
}
}
return result;
}
/**
* @returns the owner object of the authenticated user
*/
@GET
@Path("/me/owner")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
@StatusCodes({
@ResponseCode(code = 200, condition = "infos about the owner of the auth token"),
@ResponseCode(code = 403, condition = ErrorMessages.NOT_USER_TOKEN_CONTEXT_USED),
@ResponseCode(code = 404, condition = ErrorMessages.INVALID_ATTRIBUTE),
@ResponseCode(code = 500, condition = ErrorMessages.ERROR_IN_API_RESULT)
})
public Response getCurrentUser() {
ResponseBean responseBean = new ResponseBean();
Secret secret = SecretManagerProvider.get();
Owner owner = secret.getOwner();
try {
// UserResource user = KCUserController.getUserById();
responseBean.setResult(owner);
responseBean.setSuccess(true);
ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new InternalServerErrorException(e);
}
}
/**
* @returns the result of introspection of the auth token on the
* authentication service
*/
@GET
@Path("/me/verify")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
@StatusCodes({
@ResponseCode(code = 200, condition = "decode the token"),
@ResponseCode(code = 403, condition = ErrorMessages.NOT_USER_TOKEN_CONTEXT_USED),
@ResponseCode(code = 404, condition = ErrorMessages.INVALID_ATTRIBUTE),
@ResponseCode(code = 500, condition = ErrorMessages.ERROR_IN_API_RESULT)
})
public Response getInrospectioCurrenttUser() {
ResponseBean responseBean = new ResponseBean();
try {
String token = AuthController.getAccessToken();
responseBean.setResult(JWTController.decodeJwtToken(token));
// UserResource user = KCUserController.getUserById();
responseBean.setSuccess(true);
ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new InternalServerErrorException(e);
}
}
/**
* Returns informations about the user received as parameter (can be also me)
*
* Normal member users can use only "me" or his username as parameter
*
* Users with "idm-service-read" or "idm-service-admin" role can invoke
* this method with any username
*
* accepted parameters are:
*
* <ul>
* <li>profile: returns the profile of the user in the Liferay CMS (only if the
* user is
* not a service)</li>
* <li>email: returns the email of the user from the authentication service</li>
*
* <li>user: the user representation from the authentication service</li>
* </uL>
*
* if the optional parameter inspect is passed as true, returns additional
* values:
* <ul>
* <li>roles_realm: roles in realm for the user from the authentication
* service</li>
* <li>roles_clients: roles in clients for the user from the authentication
* service</li>
* <li>groups: id of the user from the authentication service</li>
* <li>username: username of the user from the authentication service</li>
* <li>name: Fullname of the user from the authentication service</li>
* <li>attributes: attributes of the user from the authentication service</li>
* <li>user: full user from the authentication service</li>
* <li>profile: profile of the user from the Liferay CMS service</li>
* </ul>
*
* @param username the username of the user
* @param parameter the parameter to obtain. accepts profile, email,
* roles_realm, roles_clients, groups, id, username , name,
* attributes, user
* @param inspect adds additional inspection values to the result
* @returns infos about the user
*/
public enum USER_DETAILS {
profile, email, roles_realm, roles_clients,
groups, id, username, name, attributes, user
}
@GET
@Path("/{username}/{parameter}")
@Produces(MediaType.APPLICATION_JSON)
@StatusCodes({
@ResponseCode(code = 200, condition = "decode the token"),
@ResponseCode(code = 403, condition = ErrorMessages.NOT_USER_TOKEN_CONTEXT_USED),
@ResponseCode(code = 404, condition = ErrorMessages.INVALID_ATTRIBUTE),
@ResponseCode(code = 500, condition = ErrorMessages.ERROR_IN_API_RESULT)
})
public Response getUserParameter(
@PathParam("username") String username,
@PathParam("parameter") USER_DETAILS parameter) {
ResponseBean responseBean = new ResponseBean();
ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
Secret secret = SecretManagerProvider.get();
Owner owner = secret.getOwner();
if (username.equals("me")) {
username = owner.getId();
}
if (!AuthController.checkAnyRole(AuthController.ACCESS_READ_ROLES)
&& !AuthController.userIsMe(username, owner)) {
// the user can see only his profile
throw new ForbiddenException(ErrorMessages.USER_NOT_AUTHORIZED_PRIVATE);
}
try {
if (parameter.equals(USER_DETAILS.profile)) {
GCubeUser profile = LiferayProfileClient.getUserProfileByUsername(username);
responseBean.setResult(profile);
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
}
UserRepresentation user = KCUserController.getUserByUsername(username);
if (parameter.equals(USER_DETAILS.email))
responseBean.setResult(user.getEmail());
else if (parameter.equals(USER_DETAILS.roles_realm))
responseBean.setResult(user.getRealmRoles());
else if (parameter.equals(USER_DETAILS.roles_clients))
responseBean.setResult(user.getClientRoles());
else if (parameter.equals(USER_DETAILS.groups))
responseBean.setResult(user.getGroups());
else if (parameter.equals(USER_DETAILS.id))
responseBean.setResult(user.getId());
else if (parameter.equals(USER_DETAILS.username))
responseBean.setResult(user.getUsername());
else if (parameter.equals(USER_DETAILS.name))
responseBean.setResult(user.getFirstName() + " " + user.getLastName());
else if (parameter.equals(USER_DETAILS.attributes))
responseBean.setResult(user.getAttributes());
else if (parameter.equals(USER_DETAILS.user) || parameter == null)
responseBean.setResult(user);
else
throw new BadRequestException("unknow parameter " + parameter);
responseBean.setSuccess(true);
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new InternalServerErrorException(e);
}
}
/**
* Search users in all realm, filtered according to query parameters.
*
* @param format response format
* @param exact Boolean which defines whether the params 'last', 'first',
* 'email' and 'username' must match exactly. default true
* @param username A String contained in username, or the complete username,
* if
* param 'exact' is true
* @param firstName A String contained in firstName, or the complete
* firstName,
* if param 'exact' is true
* @param lastName A String contained in firstName, or the complete
* firstName,
* if param 'exact' is true
* @param firstResult pagination offset
* @param maxResults maximum results size
* @param enabled Boolean representing if user is enabled or not
* @param email A String contained in email, or the complete email, if
* param 'exact' is true
*/
@GET
@Path("/search")
@Produces(MediaType.APPLICATION_JSON)
public Response search(
@QueryParam("format") @DefaultValue("username") KCUserController.REPR format,
@QueryParam("exact") @DefaultValue("true") Boolean exact,
@QueryParam("username") String username,
@QueryParam("firsnName") String firstName,
@QueryParam("lastName") String lastName,
@QueryParam("email") String email,
@QueryParam("first") @DefaultValue("0") int firstResult,
@QueryParam("max") @DefaultValue("100") int maxResults,
@QueryParam("enabled") @DefaultValue("true") Boolean enabled) {
ResponseBean responseBean;
responseBean = new ResponseBeanPaginated(firstResult, maxResults);
try {
if (!format.equals(KCUserController.REPR.username)
&& !AuthController.checkAnyRole(AuthController.ACCESS_READ_ROLES)) {
// the user can see only his profile
throw new ForbiddenException(ErrorMessages.USER_NOT_AUTHORIZED_PRIVATE);
}
RealmResource realm = KkClientFactory.getSingleton().getKKRealm();
Boolean briefRepresentation = !KCUserController.REPR.full.equals(format);
List<UserRepresentation> users = realm.users().search(
username, firstName, lastName, email,
firstResult, maxResults,
enabled, briefRepresentation, exact);
responseBean.setResult(KCUserController.formatList(users, format));
responseBean.setSuccess(true);
ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new InternalServerErrorException(e);
}
}
}

View File

@ -0,0 +1,67 @@
package org.gcube.service.idm.rest.examples;
import org.gcube.common.authorization.control.annotations.AuthorizationControl;
import org.gcube.common.security.providers.SecretManagerProvider;
import org.gcube.common.security.secrets.Secret;
import org.gcube.smartgears.utils.InnerMethodName;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
/**
* @author Lucio Lelii (ISTI - CNR)
* @author Alfredo Oliviero (ISTI - CNR)
*/
@Path("auth")
public class AuthenticatedService {
private static final String ALLOWED_ROLE = "myRole";
private static final String ALLOWED_ROLE_ORG = "OrganizationMember";
private static final String ALLOWED_ROLE_MEMBER = "Member";
@AuthorizationControl(allowedRoles = { ALLOWED_ROLE_ORG })
@GET
@Path("org_member")
@Produces(MediaType.TEXT_PLAIN)
public String authorizedOrg() {
InnerMethodName.set("auth");
Secret secret = SecretManagerProvider.get();
String userId = secret.getOwner().getId();
String context = secret.getContext();
return String.format(
"User %s in context %s is authorized to execute this method because he has the role %s", userId,
context, ALLOWED_ROLE_ORG);
}
@AuthorizationControl(allowedRoles = { ALLOWED_ROLE_MEMBER })
@GET
@Path("member")
@Produces(MediaType.TEXT_PLAIN)
public String authorizedMember() {
InnerMethodName.set("auth");
Secret secret = SecretManagerProvider.get();
String userId = secret.getOwner().getId();
String context = secret.getContext();
return String.format(
"User %s in context %s is authorized to execute this method because he has the role %s", userId,
context, ALLOWED_ROLE_MEMBER);
}
@AuthorizationControl(allowedRoles = { ALLOWED_ROLE })
@GET
@Path("")
@Produces(MediaType.TEXT_PLAIN)
public String authorized() {
InnerMethodName.set("auth");
Secret secret = SecretManagerProvider.get();
String userId = secret.getOwner().getId();
String context = secret.getContext();
return String.format(
"User %s in context %s is authorized to execute this method because he has the role %s", userId,
context, ALLOWED_ROLE);
}
}

View File

@ -0,0 +1,31 @@
package org.gcube.service.idm.rest.examples;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Path("guest")
public class ExcludeAuthorizationService {
private final Logger logger = LoggerFactory.getLogger(ExcludeAuthorizationService.class);
/**
* this method doesn't need authorization and the SecretManagerProvider is null
* see to implement this behavior add to excludes section in your
* application.yaml
*
* - path: /{path-to-your-method-path}
*
* example for this method
*
* - path: /excluded
*
*/
@GET
public String exludedMethod() {
logger.info("executed whithout any authorization");
return "executed whithout any authorization";
}
}

View File

@ -0,0 +1,85 @@
package org.gcube.service.idm.rest.examples;
import java.util.HashMap;
import java.util.Map;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.gcube.common.security.Owner;
import org.gcube.common.security.providers.SecretManagerProvider;
import org.gcube.common.security.secrets.Secret;
import org.gcube.service.idm.IdMManager;
import org.gcube.service.idm.serializers.IdmObjectSerializator;
import org.gcube.smartgears.ContextProvider;
import org.gcube.smartgears.annotations.ManagedBy;
import org.gcube.smartgears.context.container.ContainerContext;
import org.gcube.smartgears.utils.InnerMethodName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
*
* @author Lucio Lelii (ISTI-CNR)
* @author Alfredo Oliviero (ISTI-CNR)
*/
@ManagedBy(IdMManager.class)
@Path("")
public class HelloService {
private final Logger logger = LoggerFactory.getLogger(HelloService.class);
@GET
@Path("hello")
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
InnerMethodName.set("hello");
Secret secret = SecretManagerProvider.get();
String userId = secret.getOwner().getId();
String context = secret.getContext();
String infrastructureName = ContextProvider.get().container().configuration().infrastructure();
logger.info("caller id is {}", userId);
return String.format("Hello %s in context %s in infastructure %s -roles %s", userId, context,
infrastructureName, secret.getOwner().getRoles());
}
@GET
@Path("details")
@Produces({ MediaType.APPLICATION_JSON })
public Response details() {
InnerMethodName.set("details");
Map<String, Object> data = new HashMap<>();
Secret secret = SecretManagerProvider.get();
String context = secret.getContext();
data.put("context", context);
Owner owner = secret.getOwner();
data.put("owner", owner);
ContainerContext container = ContextProvider.get().container();
data.put("container", container);
ObjectMapper objectMapper = IdmObjectSerializator.getSerializer();
try {
String jsonData = objectMapper.writeValueAsString(data);
return Response.ok(jsonData).build();
} catch (JsonProcessingException e) {
e.printStackTrace();
return Response.serverError().build();
}
}
}

View File

@ -0,0 +1,139 @@
package org.gcube.service.idm.rest.examples;
import java.util.ArrayList;
import java.util.List;
import org.gcube.service.idm.IdMManager;
import org.gcube.service.idm.serializers.IdmObjectSerializator;
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.DefaultValue;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Response;
@ManagedBy(IdMManager.class)
@Path("")
public class KeycloakTestService {
private final String CLIENT_SECRET = "NOT_COMMITTED";
private final static Logger logger = LoggerFactory.getLogger(KeycloakTestService.class);
public static String encodeClientIdContext(String context) {
return context.replace("/", "%2F");
}
@GET
@Path("keycloak")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response testKeycloak(
@QueryParam("serverUrl") @DefaultValue("https://accounts.dev.d4science.org/auth") String serverUrl,
@QueryParam("realm") @DefaultValue("d4science") String realm,
@QueryParam("role-name") @DefaultValue("Member") String roleName,
@QueryParam("client_id") @DefaultValue("id.d4science.org") String clientId,
@QueryParam("client_secret") @DefaultValue(CLIENT_SECRET) String client_secret) {
String client_contenxt = "/gcube";
List<String> usernames = new ArrayList<String>();
try {
Keycloak kclient = KeycloakBuilder.builder()
.serverUrl(serverUrl)
.realm(realm)
.grantType(OAuth2Constants.CLIENT_CREDENTIALS)
.clientId(clientId) //
.clientSecret(client_secret).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 = IdmObjectSerializator.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 = encodeClientIdContext(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,15 @@
/**
* <h1>Identity Manager (IDM) Service</h1>
*
* <p>
* Welcome to Identity Manager Service (aka IDM) API documentation.
* </p>
*
* <p>
* To get a complete overview of gCat service take a look at
* <a href="../docs/index.html">wiki page</a>.
* </p>
*
*
*/
package org.gcube.service.idm.rest;

View File

@ -0,0 +1,45 @@
package org.gcube.service.idm.serializers;
import java.io.IOException;
import org.gcube.smartgears.configuration.container.ContainerConfiguration;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
/**
* Jackson serializer for ContainerConfiguration
*
* @author Alfredo Oliviero (ISTI-CNR)
*
*/
public class ContainerConfigurationSerializer extends StdSerializer<ContainerConfiguration> {
protected ContainerConfigurationSerializer(Class<ContainerConfiguration> t) {
super(t);
}
public ContainerConfigurationSerializer() {
super(ContainerConfiguration.class, true);
}
@Override
public void serialize(ContainerConfiguration configuration, JsonGenerator jgen, SerializerProvider provider)
throws IOException {
jgen.writeStartObject();
jgen.writeObjectField("mode", configuration.mode());
jgen.writeObjectField("app", configuration.apps());
jgen.writeObjectField("site", configuration.site());
jgen.writeObjectField("infrastructure", configuration.infrastructure());
jgen.writeObjectField("hostname", configuration.hostname());
jgen.writeObjectField("port", configuration.port());
jgen.writeObjectField("protocol", configuration.protocol());
jgen.writeObjectField("authorizeChildrenContext", configuration.authorizeChildrenContext());
jgen.writeObjectField("proxy", configuration.proxy());
jgen.writeObjectField("desc", configuration.toString());
jgen.writeEndObject();
}
}

View File

@ -0,0 +1,42 @@
package org.gcube.service.idm.serializers;
import java.io.IOException;
import org.gcube.smartgears.context.container.ContainerContext;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
/**
* Jackson serializer for ContainerContext
*
* @author Alfredo Oliviero (ISTI-CNR)
*
*/
public class ContainerContextSerializer extends StdSerializer<ContainerContext> {
protected ContainerContextSerializer(Class<ContainerContext> t) {
super(t);
}
public ContainerContextSerializer() {
super(ContainerContext.class, true);
}
@Override
public void serialize(ContainerContext ccontext, JsonGenerator jgen, SerializerProvider provider)
throws IOException {
jgen.writeStartObject();
jgen.writeStringField("id", ccontext.id());
// jgen.writeObjectField("configuration.site", ccontext.configuration().site());
// jgen.writeObjectField("configuration", ccontext.configuration());
jgen.writeObjectField("properties", ccontext.properties());
jgen.writeObjectField("authorizationProvider", ccontext.authorizationProvider());
jgen.writeObjectField("configuration", ccontext.configuration());
jgen.writeObjectField("desc", ccontext.toString());
jgen.writeEndObject();
}
}

View File

@ -0,0 +1,57 @@
package org.gcube.service.idm.serializers;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import org.gcube.smartgears.configuration.container.ContainerConfiguration;
import org.gcube.smartgears.context.container.ContainerContext;
import org.gcube.smartgears.security.SimpleCredentials;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
/**
* Jackson Serialization utils for Smartgear Context classes
*
* @author Alfredo Oliviero (ISTI-CNR)
*
*/
public class IdmObjectSerializator {
private static ObjectMapper serializer = null;
protected static TypeReference<HashMap<String, Object>> typeRefHashmap = new TypeReference<HashMap<String, Object>>() {
};
public static String decodeBase64String(String encodedString) {
return new String(Base64.getUrlDecoder().decode(encodedString), StandardCharsets.UTF_8);
}
public static HashMap<String, Object> jsonStringToHasmap(String jsonString)
throws JsonMappingException, JsonProcessingException {
return getSerializer().readValue(jsonString, typeRefHashmap);
}
public static ObjectMapper getSerializer() {
if (serializer == null) {
ObjectMapper om = new ObjectMapper();
SimpleModule module = new SimpleModule();
// module.addSerializer(Owner.class, new OwnerSerializer());
module.addSerializer(ContainerConfiguration.class, new ContainerConfigurationSerializer());
module.addSerializer(ContainerContext.class, new ContainerContextSerializer());
module.addSerializer(SimpleCredentials.class, new SimpleCredentialsSerializer());
// DecodedJWT serialization
om.registerModule(new JavaTimeModule());
serializer = om;
}
return serializer;
}
}

View File

@ -0,0 +1,47 @@
package org.gcube.service.idm.serializers;
import java.io.IOException;
import org.gcube.common.security.Owner;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
/**
* Jackson serializer for Owner
*
* @author Alfredo Oliviero (ISTI-CNR)
*
*/
public class OwnerSerializer extends StdSerializer<Owner> {
protected OwnerSerializer(Class<Owner> t) {
super(t);
}
public OwnerSerializer() {
super(Owner.class, true);
}
@Override
public void serialize(Owner owner, JsonGenerator jgen, SerializerProvider provider) throws IOException {
jgen.writeStartObject();
jgen.writeStringField("ownerId", owner.getId());
jgen.writeStringField("clientName", owner.getClientName());
jgen.writeArrayFieldStart("roles");
for (String role : owner.getRoles()) {
jgen.writeString(role);
}
jgen.writeEndArray();
jgen.writeStringField("email", owner.getEmail());
jgen.writeStringField("firstName", owner.getFirstName());
jgen.writeStringField("lastName", owner.getLastName());
jgen.writeBooleanField("externalClient", owner.isExternalClient());
jgen.writeStringField("contactPerson", owner.getClientName());
jgen.writeStringField("contactOrganisation", owner.getContactOrganisation());
jgen.writeEndObject();
}
}

View File

@ -0,0 +1,64 @@
package org.gcube.service.idm.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

@ -0,0 +1,36 @@
package org.gcube.service.idm.serializers;
import java.io.IOException;
import org.gcube.smartgears.security.SimpleCredentials;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
/**
* Jackson serializer for SimpleCredentials
*
* @author Alfredo Oliviero (ISTI-CNR)
*
*/
public class SimpleCredentialsSerializer extends StdSerializer<SimpleCredentials> {
protected SimpleCredentialsSerializer(Class<SimpleCredentials> t) {
super(t);
}
public SimpleCredentialsSerializer() {
super(SimpleCredentials.class, true);
}
@Override
public void serialize(SimpleCredentials credentials, JsonGenerator jgen, SerializerProvider provider)
throws IOException {
jgen.writeStartObject();
jgen.writeStringField("clientId", credentials.getClientID());
jgen.writeStringField("secret", "[*****]");
jgen.writeEndObject();
}
}

View File

@ -0,0 +1,88 @@
package org.gcube.service.idm.utils;
import org.gcube.common.authorization.library.ClientType;
import org.gcube.common.authorization.library.utils.Caller;
import org.slf4j.LoggerFactory;
/**
* Tokens utils methods
*/
public class TokensUtils {
// a user context token (not qualified) has as qualifier the word "TOKEN"
private static final String DEFAULT_QUALIFIER_USER_TOKEN = "TOKEN";
// Logger
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(TokensUtils.class);
/**
* Check if it is a service token
*
* @return a boolean value
*/
public static boolean isServiceToken(Caller caller) {
return caller.getClient().getType().equals(ClientType.SERVICE);
}
/**
* Check if it is an application token
*
* @return a boolean value
*/
public static boolean isApplicationToken(Caller caller) {
String username = caller.getClient().getId();
if (username.startsWith("service-account-")) {
return true;
}
return caller.getClient().getType().equals(ClientType.EXTERNALSERVICE);
}
/**
* Check if it is a container token
*
* @return a boolean value
*/
public static boolean isContainerToken(Caller caller) {
return caller.getClient().getType().equals(ClientType.CONTAINER);
}
/**
* Check if it is a user token
*
* @return a boolean value
*/
public static boolean isUserToken(Caller caller) {
logger.debug("\n ****** \n isUserToken: caller.getClient().getType().equals(ClientType.USER) => "
+ caller.getClient().getType().equals(ClientType.USER));
String username = caller.getClient().getId();
if (username.startsWith("service-account-")) {
return false;
}
return caller.getClient().getType().equals(ClientType.USER);
}
/**
* Check if it is a user token (not qualified)
*
* @return a boolean value
*/
public static boolean isUserTokenDefault(Caller caller) {
return isUserToken(caller);
}
/**
* Check if it is a user token (qualified)
*
* @return a boolean value
*/
public static boolean isUserTokenQualified(Caller caller) {
return caller.getClient().getType().equals(ClientType.USER)
&& !caller.getTokenQualifier().equals(DEFAULT_QUALIFIER_USER_TOKEN);
}
}

View File

@ -0,0 +1,40 @@
package org.gcube.service.utils;
public class ErrorMessages {
public static final String ERROR_IN_API_RESULT = "The error is reported into the 'message' field of the returned object";
public static final String INVALID_ATTRIBUTE = "Such an attribute doesn't exist";
public static final String NOT_USER_TOKEN_CONTEXT_USED = "User's information can only be retrieved through a user token (not qualified)";
public static final String NOT_SERVICE_TOKEN_CONTEXT_USED = "This method can only be called with an infrastructure token";
public static final String USER_NOT_AUTHORIZED_PRIVATE = "User is not authorized to access private data";
public static final String CANNOT_RETRIEVE_PROFILE = "Unable to retrieve user profile";
public static final String RESERVED_PARAMETER = "The parameter can be used only by realm administrators: ";
//
// protected static final String CANNOT_RETRIEVE_SERVICE_ENDPOINT_INFORMATION =
// "Unable to retrieve such service endpoint information";
// private static final String NO_RUNTIME_RESOURCE_TEMPLATE_NAME_CATEGORY =
// "There is no Runtime Resource having name %s and Category %s in this scope";
// public static final String MISSING_TOKEN = "Missing token.";
// public static final String MISSING_PARAMETERS = "Missing request
// parameters.";
// public static final String INVALID_TOKEN = "Invalid token.";
// public static final String TOKEN_GENERATION_APP_FAILED = "Token generation
// failed.";
// public static final String NOT_APP_TOKEN = "Invalid token: not belonging to
// an application.";
// public static final String NOT_APP_ID = "Invalid application id: it doesn't
// belong to an application.";
// public static final String NO_APP_PROFILE_FOUND = "There is no application
// profile for this app id/scope.";
// public static final String BAD_REQUEST = "Please check the parameter you
// passed, it seems a bad request";
// public static final String POST_OUTSIDE_VRE = "A post cannot be written into
// a context that is not a VRE";
// public static final String DEPRECATED_METHOD = "This method is deprecated,
// must use version 2";
}

View File

@ -1,4 +1,5 @@
package org.gcube.rest;
package org.gcube.service.utils.beans;
import java.io.Serializable;
/**
@ -11,16 +12,16 @@ public class ResponseBean implements Serializable {
/**
* The result of the request: true if it succeeded, false otherwise
*/
private boolean success;
protected boolean success;
/**
* An error message if something wrong happened, null/empty otherwise
*/
private String message;
protected String message;
/**
* The result object of the request
*/
private Object result;
protected Object result;
public ResponseBean() {
super();
@ -68,4 +69,3 @@ public class ResponseBean implements Serializable {
+ ", message=" + message + ", result=" + result + "]";
}
}

View File

@ -0,0 +1,76 @@
package org.gcube.service.utils.beans;
import java.util.HashMap;
import java.util.Map;
/**
* Response bean
*
*/
public class ResponseBeanMap extends ResponseBean {
private static final long serialVersionUID = -2725238162673879658L;
/**
* The result of the request: true if it succeeded, false otherwise
*/
protected boolean success;
/**
* An error message if something wrong happened, null/empty otherwise
*/
protected String message;
/**
* The result object of the request
*/
protected Map<String, Object> result = new HashMap<String, Object>();
public ResponseBeanMap() {
super();
}
/**
* @param success
* @param message
* @param result
*/
public ResponseBeanMap(boolean success, String message, Map<String, Object> mapResults) {
super();
this.success = success;
this.message = message;
this.result = mapResults;
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Map<String, Object> getResult() {
return result;
}
public void setResult(Map<String, Object> mapResults) {
this.result = mapResults;
}
public void putResult(String key, Object res) {
this.result.put(key, res);
}
@Override
public String toString() {
return "ResponseBean [success=" + success
+ ", message=" + message + ", result=" + result + "]";
}
}

View File

@ -0,0 +1,74 @@
package org.gcube.service.utils.beans;
/**
* Response bean
*
*/
public class ResponseBeanPaginated extends ResponseBean {
private static final long serialVersionUID = -2725238162673879658L;
protected Integer firstResult;
protected Integer maxResults;
public Integer getFirstResult() {
return firstResult;
}
public void setFirstResult(Integer firstResult) {
this.firstResult = firstResult;
}
public Integer getMaxResults() {
return maxResults;
}
public void setMaxResults(Integer maxResults) {
this.maxResults = maxResults;
}
public ResponseBeanPaginated() {
super();
}
// /**
// * @param success
// * @param message
// * @param result
// */
// public ResponseBeanPaginated(boolean success, String message, Object result)
// {
// super(success, message, result);
// }
/**
* @param firstResult
* @param maxResults
*/
public ResponseBeanPaginated(Integer firstResult, Integer maxResults) {
this.firstResult = firstResult;
this.maxResults = maxResults;
}
/**
* @param success
* @param message
* @param result
* @param firstResult
* @param maxResults
*/
public ResponseBeanPaginated(boolean success, String message, Object result, Integer firstResult,
Integer maxResults) {
super(success, message, result);
this.firstResult = firstResult;
this.maxResults = maxResults;
}
@Override
public String toString() {
return "ResponseBean [success=" + success
+ ", message=" + message + ", result=" + result + ", firstResult=" + firstResult + ", maxResults="
+ maxResults + "]";
}
}

View File

@ -1,12 +0,0 @@
package org.gcube.utils.IdmConstantsaa;
public class IdmConstants {
public static final String SERVICE_CLASS = "org.gcube.data-catalogue";
public static final String SERVICE_NAME = "gcat";
public final static String CONFIGURATION_CATEGORY = IdmConstants.SERVICE_CLASS;
public final static String CONFIGURATION_NAME = IdmConstants.SERVICE_NAME + "-configuration";
public static final String SERVICE_ENTRY_NAME = "org.gcube.gcat.ResourceInitializer";
}

File diff suppressed because it is too large Load Diff

30
todo.md Normal file
View File

@ -0,0 +1,30 @@
# OPENSTACK
* [ ] deploy su openstack
* [ ] aggiungere accounting
# CONFIGURAZIONE
* [ ] leggere parametri da file di container.ini
* [ ] rivedere gestione errori
# MASSI
* API REST social
* [ ] stesso path (con 2/ davanti)
* [ ] verificare di accettare esattamente stessi parametri
* [ ]usare stesso bean (senza limit e max )
* nel token abbiamo i ruoli realm globali. al momento smartgear li ignora, bisogna implementare logica di accesso basata su quei ruoli. verificare se smartgear li legge, in caso contrario fare una chiamata di appoggio che li ottiene dal token, versione futura si appoggerà su smartgear
* "realm_access":
{
"roles": [
"offline_access",
"uma_authorization",
"service-endpoint-key"
]
},
* [ ] implementare client (rif. gcat-client)
* [ ] verificare controllo diritti