Compare commits

...

7 Commits

36 changed files with 2188 additions and 755 deletions

1
.gitignore vendored
View File

@ -1,4 +1,5 @@
#
ignore_me
tomcat
target

View File

@ -1,12 +1,11 @@
[node]
mode = offline
hostname = myhostname.isti.cnr.it
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

View File

@ -1,12 +1,11 @@
[node]
mode = offline
hostname = myhostname.isti.cnr.it
mode = online
hostname = idm.isti.cnr.it
protocol= http
port = 8080
infrastructure = d4science.research-infrastructures.eu
infrastructure = gcube
authorizeChildrenContext = true
publicationFrequencyInSeconds = 60
[properties]
SmartGearsDistribution = 4.0.0-SNAPSHOT
SmartGearsDistributionBundle = UnBundled

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,7 +1,7 @@
<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>Ï <pattern>%-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
@ -21,7 +21,7 @@
<logger name="org.gcube.data.access.storagehub.handlers" level="DEBUG" />
-->
<root level="WARN">
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>

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>

View File

@ -2,13 +2,13 @@
<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" />

View File

@ -1,5 +1,5 @@
name: idm
group: org.gcube.service
name: identity-manager
group: org.gcube.auth
version: 1.0.0
description: Identity Manager Service
excludes:

View File

@ -14,7 +14,12 @@
<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>idm</servlet-name>

35
pom.xml
View File

@ -6,8 +6,8 @@
<groupId>org.gcube.idm</groupId>
<artifactId>idm</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Identity Manager Smargears</name>
<description>Identity Manager Smargears Service</description>
<name>Identity Manager (IDM) Service</name>
<description>Identity Manager (IDM) Service - Smartgears</description>
<packaging>war</packaging>
<parent>
@ -28,7 +28,8 @@
<!-- bind jackson version to avoid inconsistences with keycloak dependencies -->
<jackson.version>2.15.3</jackson.version>
<enunciate.version>2.14.0</enunciate.version>
<enunciate.version>2.16.1</enunciate.version>
<keycloak.client.version>21.1.2</keycloak.client.version>
</properties>
@ -124,10 +125,12 @@ solution: bind version, or exclude them in usermanagement-core
<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>21.1.2</version>
<version>${keycloak.client.version}</version>
<exclusions>
<exclusion>
@ -197,6 +200,7 @@ solution: bind version, or exclude them in usermanagement-core
</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>
@ -209,6 +213,14 @@ solution: bind version, or exclude them in usermanagement-core
<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 -->
@ -238,6 +250,13 @@ solution: bind version, or exclude them in usermanagement-core
<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
@ -344,7 +363,7 @@ java.lang.NoClassDefFoundError: org/apache/http/ssl/TrustStrategy
</plugin> -->
<!-- Enunciate Maven plugin -->
<!-- <plugin>
<plugin>
<groupId>com.webcohesion.enunciate</groupId>
<artifactId>enunciate-maven-plugin</artifactId>
<version>${enunciate.version}</version>
@ -356,11 +375,11 @@ java.lang.NoClassDefFoundError: org/apache/http/ssl/TrustStrategy
</goals>
</execution>
</executions>
</plugin> -->
</plugin>
<!-- Copy Enunciate Documentation from your-application/api-docs
into your war -->
<!-- <plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<executions>
@ -384,7 +403,7 @@ java.lang.NoClassDefFoundError: org/apache/http/ssl/TrustStrategy
</configuration>
</execution>
</executions>
</plugin> -->
</plugin>
</plugins>
</build>

View File

@ -0,0 +1,210 @@
package org.gcube.service.idm.controller;
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.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 {
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,82 @@
package org.gcube.service.idm.controller;
import java.util.List;
import java.util.Map;
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";
public final static String IDM_SERVICE_ADMIN = "idm-service-admin";
public final static List<String> ACCESS_READ_ROLES = List.of(IDM_SERVICE_READ, IDM_SERVICE_ADMIN);
public final static List<String> ACCESS_ADMIN_ROLES = List.of(IDM_SERVICE_READ);
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);
}
}

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.ContextSerializator;
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 = ContextSerializator.decodeBase64String(decodedJWT.getHeader());
String payloadJson = ContextSerializator.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", ContextSerializator.jsonStringToHasmap(headerJson));
decoded.put("payload", ContextSerializator.jsonStringToHasmap(payloadJson));
// decoded.put("signature",
// ContextSerializator.jsonStringToHasmap(signatureJson));
decoded.put("decodedJWT", decodedJWT);
return decoded;
}
}

View File

@ -0,0 +1,65 @@
package org.gcube.service.idm.controller;
import java.util.ArrayList;
import java.util.List;
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.slf4j.LoggerFactory;
import jakarta.ws.rs.NotFoundException;
public class KCRolesController {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(KCRolesController.class);
public enum REPRESENTATION {
full, compact, name, id
}
public static Object formatListRoles(List<RoleRepresentation> roles, KCRolesController.REPRESENTATION format) {
if (format.equals(KCRolesController.REPRESENTATION.id)) {
List<String> ids = new ArrayList<String>();
if (roles != null) {
for (RoleRepresentation role : roles) {
ids.add(role.getId());
}
}
return ids;
} else if (format.equals(KCRolesController.REPRESENTATION.name)) {
List<String> names = new ArrayList<String>();
if (roles != null) {
for (RoleRepresentation role : roles) {
names.add(role.getName());
}
}
return names;
} else
return roles;
}
public static List<RoleRepresentation> getRoles() {
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 getRoleByName(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();
}
}

View File

@ -1,6 +1,9 @@
package org.gcube.service.idm.controller;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.gcube.common.security.providers.SecretManagerProvider;
import org.gcube.service.idm.keycloack.KeycloackApiClient;
@ -13,18 +16,68 @@ 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 KKUserClient {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(KKUserClient.class);
public class KCUserController {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(KCUserController.class);
public static UsersResource users(){
public enum REPRESENTATION {
full, compact, username, email, id, email_username, fullname
}
public static UsersResource users() {
RealmResource realm = KkClientFactory.getSingleton().getKKRealm();
UsersResource users = realm.users();
return users;
}
/**
public static Object formatListUsers(List<UserRepresentation> users, KCUserController.REPRESENTATION format){
if (format.equals(KCUserController.REPRESENTATION.username)) {
List<String> usernames = new ArrayList<String>();
if (users != null) {
for (UserRepresentation user : users) {
usernames.add(user.getUsername());
}
}
return usernames;
} else if (format.equals(KCUserController.REPRESENTATION.email)) {
List<String> emails = new ArrayList<String>();
if (users != null) {
for (UserRepresentation user : users) {
emails.add(user.getEmail());
}
}
return emails;
} else if (format.equals(KCUserController.REPRESENTATION.id)) {
List<String> ids = new ArrayList<String>();
if (users != null) {
for (UserRepresentation user : users) {
ids.add(user.getId());
}
}
return ids;
}else if (format.equals(KCUserController.REPRESENTATION.email_username)) {
Map<String, String> usernamesAndFullnames = new HashMap<String, String>();
users.forEach(user -> usernamesAndFullnames.put(user.getUsername(), user.getEmail()));
return usernamesAndFullnames;
}else if (format.equals(KCUserController.REPRESENTATION.fullname)) {
List<String> fullnames = new ArrayList<String>();
if (users != null) {
for (UserRepresentation user : users) {
fullnames.add(user.getFirstName() + " " + user.getLastName());
}
}
return fullnames;
} else
return users;
}
/**
* Search for users based on the given filters.
*
* @param username a value contained in username
@ -71,6 +124,9 @@ public class KKUserClient {
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;
}
@ -79,6 +135,9 @@ public class KKUserClient {
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;
}
@ -88,9 +147,32 @@ public class KKUserClient {
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();
@ -101,7 +183,7 @@ public class KKUserClient {
}
public static List<UserRepresentation> users(
Integer firstResult,
Integer firstResult,
Integer maxResults) {
RealmResource realm = KkClientFactory.getSingleton().getKKRealm();
@ -120,4 +202,43 @@ public class KKUserClient {
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

@ -1,32 +0,0 @@
package org.gcube.service.idm.controller;
import java.util.List;
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.slf4j.LoggerFactory;
public class KKRolesClient {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(KKRolesClient.class);
public static List<RoleRepresentation> getRoles() {
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 getRoleByName(String name) {
logger.info("Searching users for context");
ClientResource client = KkClientFactory.getSingleton().getKKClient();
RolesResource roles_resource = client.roles();
RoleResource r = roles_resource.get(name);
return r.toRepresentation();
}
}

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 (NotFoundException | ServerException | 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

@ -3,11 +3,11 @@ package org.gcube.service.idm.is;
import static org.gcube.resources.discovery.icclient.ICFactory.clientFor;
import static org.gcube.resources.discovery.icclient.ICFactory.queryFor;
import java.rmi.ServerException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
import org.gcube.common.encryption.encrypter.StringEncrypter;
import org.gcube.common.resources.gcore.ServiceEndpoint;
import org.gcube.common.security.AuthorizedTasks;
import org.gcube.common.security.secrets.Secret;
@ -21,7 +21,8 @@ import org.slf4j.LoggerFactory;
import jakarta.ws.rs.NotFoundException;
/**
* Utility class to query EndPoints and search for AccessPoints from IS
* Utility class to query EndPoints and search for AccessPoints from IS
*
* @author Alfredo Oliviero (ISTI - CNR)
*/
@ -35,6 +36,7 @@ public class InfrastrctureServiceClient {
* @param category
* @param accessPointName
* @param is_root_service
*
* @return the list of EndPoints matching the parameters
* @throws Exception
*/
@ -46,7 +48,7 @@ public class InfrastrctureServiceClient {
if (resource_name != null) {
query.addCondition("$resource/Profile/Name/text() eq '" + resource_name + "'");
}
if (category != null){
if (category != null) {
query.addCondition("$resource/Profile/Category/text() eq '" + category + "'");
}
DiscoveryClient<ServiceEndpoint> client = clientFor(ServiceEndpoint.class);
@ -77,19 +79,18 @@ public class InfrastrctureServiceClient {
}
/**
* obatains the list of AccessPoints matching the parameters
* obatains the list of AccessPoints matching the parameters
*
* @param resource_name
* @param category
* @param accessPointName
* @param endPointName
* @param is_root_service
* @return the list of AccessPoints
* @throws Exception
*/
public static List<ServiceEndpoint.AccessPoint> getAccessPointsFromIS(String resource_name, String category,
String accessPointName, boolean is_root_service) throws Exception {
String endPointName, boolean is_root_service) {
List<ServiceEndpoint> resources = getEndopintsFromIS(resource_name, category, is_root_service);
@ -100,11 +101,11 @@ public class InfrastrctureServiceClient {
}
List<ServiceEndpoint.AccessPoint> response = new ArrayList<ServiceEndpoint.AccessPoint>();
resources.forEach(res -> {
resources.forEach(res -> {
Stream<ServiceEndpoint.AccessPoint> access_points_res = res.profile().accessPoints().stream();
if (accessPointName == null) {
access_points_res = access_points_res.filter(ap -> ap.name().equals(accessPointName));
if (endPointName == null) {
access_points_res = access_points_res.filter(ap -> ap.name().equals(endPointName));
}
access_points_res.forEach(a -> response.add(a));
@ -113,21 +114,23 @@ public class InfrastrctureServiceClient {
}
/**
* obatains the list of AccessPoints matching the parameters, and returns the first one
* obatains the list of AccessPoints matching the parameters, and returns the
* first one
*
* @param resource_name
* @param category
* @param accessPointName
* @param entryPointName
* @return an AccessPoints matching the parameters
* @throws Exception
*/
public static ServiceEndpoint.AccessPoint getAccessPointFromIS(String resource_name, String category,
String accessPointName, boolean root_service) throws Exception {
public static ServiceEndpoint.AccessPoint getFirstAccessPointFromIS(String resource_name, String category,
String entryPointName, boolean root_service) {
List<ServiceEndpoint.AccessPoint> access_points = getAccessPointsFromIS(resource_name, category, accessPointName, root_service);
List<ServiceEndpoint.AccessPoint> access_points = getAccessPointsFromIS(resource_name, category, entryPointName,
root_service);
if (access_points.size() == 0) {
logger.error("Unable to retrieve service endpoint " + accessPointName);
logger.error("Unable to retrieve service endpoint " + entryPointName);
return null;
}
@ -135,38 +138,40 @@ public class InfrastrctureServiceClient {
}
/**
/**
* Reads the service configuration from the IS
* @param resourceName
* @param category
* @param accessPointName
* @param is_root_service
* @return
* @throws Exception
*/
public static IsServerConfig serviceConfigFromIS(String resourceName, String category, String accessPointName, boolean is_root_service)
throws Exception {
*
* @param resourceName
* @param category
* @param accessPointName
* @param is_root_service
* @return
* @throws Exception
*/
public static IsServerConfig serviceConfigFromIS(String resourceName, String category, String endPointName,
boolean is_root_service)
throws NotFoundException, ServerException {
logger.info("Starting creating service credentials");
ServiceEndpoint.AccessPoint accessPoint = InfrastrctureServiceClient.getAccessPointFromIS(resourceName,
category, accessPointName, is_root_service);
logger.info("Starting creating service credentials");
ServiceEndpoint.AccessPoint accessPoint = InfrastrctureServiceClient.getFirstAccessPointFromIS(resourceName,
category, endPointName, is_root_service);
if (accessPoint == null) {
String error_log = "Unable to retrieve service endpoint " + accessPointName;
if (accessPoint == null) {
String error_log = "Unable to retrieve service endpoint " + endPointName;
logger.error(error_log);
throw new NotFoundException(error_log);
}
logger.error(error_log);
throw new NotFoundException(error_log);
}
String service_url = accessPoint.address();
String name = accessPoint.name();
String clientId = accessPoint.username();
String clientSecret = StringEncrypter.getEncrypter().decrypt(accessPoint.password());
try {
IsServerConfig config = new IsServerConfig(accessPoint);
return config;
IsServerConfig config = new IsServerConfig(service_url, name, clientId, clientSecret);
logger.info("Found AccessPoint URL = " + service_url);
return config;
}
} catch (Exception e) {
logger.error("cannot create server config from {}", accessPoint);
e.printStackTrace();
throw new ServerException(e.getMessage());
}
}
}

View File

@ -1,21 +1,35 @@
package org.gcube.service.idm.is;
import java.util.HashMap;
import java.util.Map;
import org.gcube.common.encryption.encrypter.StringEncrypter;
import org.gcube.common.resources.gcore.ServiceEndpoint;
import org.gcube.common.resources.gcore.ServiceEndpoint.Property;
import org.keycloak.OAuth2Constants;
public class IsServerConfig {
private String serverUrl;
private String realm;
private String name;
private String clientId;
private Map<String, String> properties = new HashMap<String, String>();
private String clientSecret;
private String grantType = OAuth2Constants.CLIENT_CREDENTIALS;
public Map<String, String> getProperties(){
return this.properties;
}
public String getServerUrl() {
return serverUrl;
}
public String getRealm() {
return realm;
public String getName() {
return name;
}
public String getClientId() {
@ -30,16 +44,47 @@ public class IsServerConfig {
return grantType;
}
public IsServerConfig(String serverUrl, String realm, String clientId, String clientSecret) {
public boolean hasProperty(String key) {
return this.properties.containsKey(key);
}
public String getProperty(String key) {
return this.properties.get(key);
}
public IsServerConfig(String serverUrl, String name, String clientId, String clientSecret) {
this.serverUrl = serverUrl;
this.realm = realm;
this.name = name;
this.clientId = clientId;
this.clientSecret = clientSecret;
}
public IsServerConfig(String serverUrl, String realm, String clientId, String clientSecret,
public IsServerConfig(String serverUrl, String name, String clientId, String clientSecret,
Map<String, String> properties) {
this(serverUrl, name, clientId, clientSecret);
this.properties = properties;
}
public IsServerConfig(ServiceEndpoint.AccessPoint accessPoint) throws Exception {
this.serverUrl = accessPoint.address();
this.name = accessPoint.name();
this.clientId = accessPoint.username();
this.clientSecret = StringEncrypter.getEncrypter().decrypt(accessPoint.password());
this.properties = new HashMap<String, String>();
for (Property p : accessPoint.properties()) {
String value = p.value();
if (p.isEncrypted()) {
value = StringEncrypter.getEncrypter().decrypt(value);
}
this.properties.put(p.name(), value);
}
}
public IsServerConfig(String serverUrl, String name, String clientId, String clientSecret,
Map<String, String> properties,
String grantType) {
this(serverUrl, realm, clientId, clientSecret);
this(serverUrl, name, clientId, clientSecret, properties);
this.grantType = grantType;
}
}

View File

@ -1,46 +0,0 @@
package org.gcube.service.idm.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";
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";
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,10 +1,14 @@
package org.gcube.service.idm.keycloack;
import java.rmi.ServerException;
import java.util.List;
import org.gcube.common.security.providers.SecretManagerProvider;
import org.gcube.service.idm.is.InfrastrctureServiceClient;
import org.gcube.service.idm.is.IsServerConfig;
import org.gcube.smartgears.ContextProvider;
import org.gcube.smartgears.context.application.ApplicationContext;
import org.gcube.smartgears.security.SimpleCredentials;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.KeycloakBuilder;
import org.keycloak.admin.client.resource.ClientResource;
@ -13,12 +17,14 @@ import org.keycloak.representations.idm.ClientRepresentation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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 REALM_D4S = "d4science";
private final static String END_POINT_NAME = "d4science";
private final static boolean IS_ROOT_SERVICE = true;
// the singleton obj
@ -46,24 +52,40 @@ public class KkClientFactory {
logger.info("Building KeycloakAPICredentials object");
try {
if (this.config == null) {
this.config = InfrastrctureServiceClient.serviceConfigFromIS(RUNTIME_RESOURCE_NAME, CATEGORY, REALM_D4S,
IS_ROOT_SERVICE);
this.config = fetchIsConfig();
}
// if (this.config_keycloak == null)
// this.config = IsServerConfig.getTestConfig();
} catch (
Exception e) {
logger.error("error obtaining IAM configuration from IS {} ", e);
}
logger.info("KeycloakAPICredentials object built {} - {}", config.getServerUrl(), config.getRealm());
logger.info("KeycloakAPICredentials object built {} - {}", config.getServerUrl(), config.getName());
}
public void setIsConfig() throws Exception {
this.config = InfrastrctureServiceClient.serviceConfigFromIS(RUNTIME_RESOURCE_NAME, CATEGORY, REALM_D4S,
IS_ROOT_SERVICE);
// public IsServerConfig configFromINI() throws NotFoundException, ServerException{
// ApplicationContext appContext = ContextProvider.get();
// SimpleCredentials credentials = (SimpleCredentials)appContext.authorizationProvider().getCredentials();
// IsServerConfig cfg = fetchIsConfig();
// IsServerConfig newConfig = new IsServerConfig(
// cfg.getServerUrl(),
// cfg.getName(),
// credentials.getClientID(), // cfg.getClientId(),
// credentials.getSecret(), // cfg.getClientSecrxet(),
// cfg.getProperties()
// );
// return newConfig;s
// }
public IsServerConfig fetchIsConfig() throws NotFoundException, ServerException {
IsServerConfig cfg = InfrastrctureServiceClient.serviceConfigFromIS(RUNTIME_RESOURCE_NAME, CATEGORY, END_POINT_NAME, IS_ROOT_SERVICE);
return cfg;
}
public static String encodeClientIdContext(String context) {
@ -78,12 +100,12 @@ public class KkClientFactory {
Keycloak kclient = KeycloakBuilder.builder()
.serverUrl(config.getServerUrl())
.realm(config.getRealm())
.realm(config.getName())
.grantType(config.getGrantType())
.clientId(config.getClientId()) //
.clientSecret(config.getClientSecret()).build();
return new KeycloackApiClient(kclient, config.getRealm(), context);
return new KeycloackApiClient(kclient, config.getName(), context);
}
public RealmResource getKKRealm() {
@ -119,13 +141,13 @@ public class KkClientFactory {
}
// TODO: REMOVE
static IsServerConfig getTestConfig() {
String serverUrl = "https://accounts.dev.d4science.org/auth";
String realm = "d4science";
String clientId = "id.d4science.org";
String clientSecret = "09c26f24-3c65-4039-9fa0-e5cc4f4032cd";
// 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);
}
// return new IsServerConfig(serverUrl, realm, clientId, clientSecret);
// }
}

View File

@ -1,18 +0,0 @@
KKRolesClient.list
KeycloakClientFactory.getKKClientForContext();
String ctx = SecretManagerProvider.get().getContext();
RealmResource realm = getKKRealmForContext();
KeycloackApiClient keycloackApiClient = KeycloakClientFactory.getSingleton().createtKeycloakInstance(ctx).clientIdContext;
List<ClientRepresentation> clients = realm.clients().findByClientId(keycloackApiClient.clientIdContext);
getUserByEmail
RealmResource realm = KeycloakClientFactory.getKKRealmForContext();
String ctx = SecretManagerProvider.get().getContext();
KeycloakClientFactory factory = KeycloakClientFactory.getSingleton()
new KeycloakClientFactory()
lookupPropertiesFromIs();

View File

@ -1,89 +1,85 @@
package org.gcube.service.idm.liferay;
import org.gcube.common.encryption.encrypter.StringEncrypter;
import org.gcube.common.resources.gcore.ServiceEndpoint;
import java.rmi.ServerException;
import org.gcube.service.idm.is.InfrastrctureServiceClient;
import org.gcube.service.idm.is.IsServerConfig;
import org.gcube.vomanagement.usermanagement.impl.ws.LiferayWSUserManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.ws.rs.NotFoundException;
public class LiferayClientFactory {
private static final Logger logger = LoggerFactory.getLogger(LoggerFactory.class);
private static final Logger logger = LoggerFactory.getLogger(LoggerFactory.class);
// Service endpoint properties
private final static String RUNTIME_RESOURCE_NAME = "D4Science Infrastructure Gateway";
// Service endpoint properties
private final static String RUNTIME_RESOURCE_NAME = "D4Science Infrastructure Gateway";
private final static String CATEGORY = "Portal";
private final static String REALM_D4S = "d4science";
private final static String END_POINT_NAME = "JSONWSUser";
private final static boolean IS_ROOT_SERVICE = true;
private static LiferayClientFactory singleton = new LiferayClientFactory();
LiferayWSUserManager client = null;
/**
* 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
*/
private IsServerConfig config;
public static LiferayWSUserManager createtLiferayClientInstance(IsServerConfig config)
throws NotFoundException, ServerException {
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 ServerException("cannot create Liferay client");
}
if (client == null) {
throw new ServerException("cannot create Liferay client");
}
logger.info("Liferay object built {} - {}", config.getServerUrl(), config.getName());
return client;
}
public IsServerConfig getConfig() throws NotFoundException, ServerException {
if (this.config == null) {
this.config = fetchIsConfig();
}
return this.config;
}
public LiferayWSUserManager getClient() throws NotFoundException, ServerException {
if (this.client == null) {
IsServerConfig configuration = this.getConfig();
this.client = createtLiferayClientInstance(configuration);
}
return this.client;
}
private static LiferayClientFactory singleton = new LiferayClientFactory();
public static LiferayClientFactory getSingleton() {
if (singleton == null)
singleton = new LiferayClientFactory();
return singleton;
}
/**
* Private constructor
* obtains the config from IS
*/
private LiferayClientFactory() {
logger.info("Building LiferayClientFactory object");
try {
if (this.config == null)
setIsInstance();
// if (this.config_keycloak == null)
// setTestKeycloackInstance();
} catch (Exception e) {
logger.error("error obtaining Liferay configuration from IS {} ", e);
}
logger.info("Liferay object built {} - {}", config.getServerUrl(), config.getRealm() );
public IsServerConfig fetchIsConfig() throws NotFoundException, ServerException {
IsServerConfig cfg = InfrastrctureServiceClient.serviceConfigFromIS(RUNTIME_RESOURCE_NAME, CATEGORY,
END_POINT_NAME,
IS_ROOT_SERVICE);
return cfg;
}
public void setIsInstance() throws Exception {
this.config = lookupPropertiesFromIs(RUNTIME_RESOURCE_NAME, CATEGORY, REALM_D4S, IS_ROOT_SERVICE);
}
/**
* Read the properties from the infrastructure
*
* @throws Exception
*/
private IsServerConfig lookupPropertiesFromIs(String resource_name, String category, String accessPointName, boolean root_service)
throws Exception {
logger.info("Starting creating KeycloakAPICredentials");
ServiceEndpoint.AccessPoint accessPoint = InfrastrctureServiceClient.getAccessPointFromIS(resource_name,
category, accessPointName, root_service);
if (accessPoint == null) {
String error_log = "Unable to retrieve service endpoint " + accessPointName;
logger.error(error_log);
throw new NotFoundException(error_log);
}
String service_url = accessPoint.address();
String name = accessPoint.name();
String clientId = accessPoint.username();
String clientSecret = StringEncrypter.getEncrypter().decrypt(accessPoint.password());
IsServerConfig config = new IsServerConfig(service_url, name, clientId, clientSecret);
logger.info("Found accesspoint URL = " + service_url);
return config;
}
}

View File

@ -0,0 +1,56 @@
package org.gcube.service.idm.mappers;
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;
import org.gcube.service.rest.ResponseBean;
/**
* @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 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;
import org.gcube.service.rest.ResponseBean;
/**
* @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,185 @@
package org.gcube.service.idm.rest;
import java.util.List;
import jakarta.ws.rs.NotFoundException;
import org.gcube.service.idm.IdMManager;
import org.gcube.service.idm.controller.AdminKeycloakController;
import org.gcube.service.idm.keycloack.KkClientFactory;
import org.gcube.service.idm.serializers.ContextSerializator;
import org.gcube.service.rest.ResponseBean;
import org.gcube.service.rest.ResponseBeanMap;
import org.gcube.smartgears.annotations.ManagedBy;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.InternalServerErrorException;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Response;
@ManagedBy(IdMManager.class)
@Path("clients")
public class ClientsAPI {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(ClientsAPI.class);
@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);
responseBean.setSuccess(true);
ObjectMapper objectMapper = ContextSerializator.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 = ContextSerializator.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 = ContextSerializator.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 = ContextSerializator.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,92 @@
package org.gcube.service.idm.rest;
import java.util.HashMap;
import java.util.Map;
import jakarta.ws.rs.core.MediaType;
import org.gcube.common.security.providers.SecretManagerProvider;
import org.gcube.service.idm.controller.AuthController;
import org.gcube.service.idm.controller.JWTController;
import org.gcube.service.idm.serializers.ContextSerializator;
import org.gcube.service.rest.ErrorMessages;
import org.gcube.service.rest.ResponseBean;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
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.Response;
import jakarta.ws.rs.core.Response.Status;
@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 = ContextSerializator.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 = ContextSerializator.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

@ -1,127 +0,0 @@
package org.gcube.service.idm.rest;
import java.util.HashMap;
import java.util.Map;
import org.gcube.common.security.providers.SecretManagerProvider;
import org.gcube.service.idm.serializers.ContextSerializator;
import org.gcube.service.rest.ResponseBean;
import org.slf4j.LoggerFactory;
import com.auth0.jwt.JWT;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
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;
import jakarta.ws.rs.core.Response.Status;
@Path("jwt")
public class JwtService {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(JwtService.class);
public static Map<String, Object> decodeJwtToken(String token)
throws JsonMappingException, JsonProcessingException {
DecodedJWT decodedJWT = JWT.decode(token);
String headerJson = ContextSerializator.decodeBase64String(decodedJWT.getHeader());
String payloadJson = ContextSerializator.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", ContextSerializator.jsonStringToHasmap(headerJson));
decoded.put("payload", ContextSerializator.jsonStringToHasmap(payloadJson));
// decoded.put("signature",
// ContextSerializator.jsonStringToHasmap(signatureJson));
decoded.put("decodedJWT", decodedJWT);
return decoded;
}
@GET
@Path("/decode")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response getDecodedJwtToken(
@QueryParam("token") String token) {
Status status = Status.OK;
ResponseBean responseBean = new ResponseBean();
try {
ObjectMapper objectMapper = ContextSerializator.getSerializer();
Map<String, Object> decoded = decodeJwtToken(token);
responseBean.setResult(decoded);
responseBean.setSuccess(true);
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (JWTDecodeException e) {
e.printStackTrace();
logger.error("error decoding the token", e);
responseBean.setMessage(e.getMessage());
status = Status.INTERNAL_SERVER_ERROR;
return Response.status(status).entity(responseBean).build();
}
catch (Exception e) {
logger.error("error processing the decoding request", e);
responseBean.setMessage(e.getMessage());
status = Status.INTERNAL_SERVER_ERROR;
return Response.status(status).entity(responseBean).build();
}
}
@GET
@Path("/auth")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response getDecodedJwtAuth() {
Status status = Status.OK;
ResponseBean responseBean = new ResponseBean();
Map<String, String> authorizations = SecretManagerProvider.get().getHTTPAuthorizationHeaders();
String token = authorizations.get("Authorization").replace("Bearer", "").trim();
try {
ObjectMapper objectMapper = ContextSerializator.getSerializer();
Map<String, Object> response = new HashMap<String, Object>();
response.put("auth_token", decodeJwtToken(token));
// response.put("authorizations", authorizations);
responseBean.setResult(response);
responseBean.setSuccess(true);
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
} catch (JWTDecodeException e) {
e.printStackTrace();
logger.error("error decoding the token", e);
responseBean.setMessage(e.getMessage());
status = Status.INTERNAL_SERVER_ERROR;
return Response.status(status).entity(responseBean).build();
}
catch (Exception e) {
e.printStackTrace();
logger.error("error processing the decoding request", e);
responseBean.setMessage(e.getMessage());
status = Status.INTERNAL_SERVER_ERROR;
return Response.status(status).entity(responseBean).build();
}
}
}

View File

@ -1,9 +1,11 @@
package org.gcube.service.idm.rest;
import java.util.ArrayList;
import java.util.List;
import org.gcube.service.idm.IdMManager;
import org.gcube.service.idm.controller.KKRolesClient;
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.ContextSerializator;
import org.gcube.service.rest.ResponseBean;
@ -21,39 +23,43 @@ import com.fasterxml.jackson.databind.ObjectMapper;
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;
import jakarta.ws.rs.core.Response.Status;
@ManagedBy(IdMManager.class)
@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") Integer firstResult,
@QueryParam("max") Integer maxResults,
@QueryParam("briefRepresentation") @DefaultValue("true") boolean briefRepresentation) {
@QueryParam("first") @DefaultValue("0") int firstResult,
@QueryParam("max") @DefaultValue("100") int maxResults,
@QueryParam("format") @DefaultValue("name") KCRolesController.REPRESENTATION format) {
ResponseBean responseBean = new ResponseBeanPaginated(firstResult, maxResults);
Boolean briefRepresentation = !KCRolesController.REPRESENTATION.full.equals(format);
Status status = Status.OK;
ResponseBean responseBean;
if (firstResult != null || maxResults != null) {
responseBean = new ResponseBeanPaginated(firstResult, maxResults);
} else {
responseBean = new ResponseBean();
}
try {
ClientResource client = KkClientFactory.getSingleton().getKKClient();
List<RoleRepresentation> roles = client.roles().list(search, firstResult, maxResults, briefRepresentation);
responseBean.setResult(roles);
responseBean.setResult(KCRolesController.formatListRoles(roles, format));
responseBean.setSuccess(true);
ObjectMapper objectMapper = ContextSerializator.getSerializer();
@ -63,25 +69,25 @@ public class RolesAPI {
} 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();
throw new InternalServerErrorException(e);
}
}
/**
* Returns role by name
*
* @path role_name the role
* 'email' and 'username' must match exactly. default true
*/
@GET
@Path("/{name}")
@Path("/{role_name}")
@Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
public Response role(
@PathParam("name") String role_name) {
Status status = Status.OK;
@PathParam("role_name") String role_name) {
ResponseBean responseBean = new ResponseBean();
try {
RoleRepresentation role = KKRolesClient.getRoleByName(role_name);
RoleRepresentation role = KCRolesController.getRoleByName(role_name);
responseBean.setResult(role);
responseBean.setSuccess(true);
@ -93,30 +99,28 @@ public class RolesAPI {
} 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();
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") Integer firstResult,
@QueryParam("max") Integer maxResults) {
Status status = Status.OK;
ResponseBean responseBean;
if (firstResult != null || maxResults != null) {
responseBean = new ResponseBeanPaginated(firstResult, maxResults);
} else {
responseBean = new ResponseBean();
}
@QueryParam("first") @DefaultValue("0") int firstResult,
@QueryParam("max") @DefaultValue("100") int maxResults,
@QueryParam("format") @DefaultValue("username") KCUserController.REPRESENTATION format) {
ResponseBean responseBean = new ResponseBeanPaginated(firstResult, maxResults);
try {
ClientResource client = KkClientFactory.getSingleton().getKKClient();
@ -125,7 +129,7 @@ public class RolesAPI {
List<UserRepresentation> users = r.getUserMembers(firstResult, maxResults);
responseBean.setResult(users);
responseBean.setResult(KCUserController.formatListUsers(users, format));
responseBean.setSuccess(true);
ObjectMapper objectMapper = ContextSerializator.getSerializer();
@ -135,14 +139,7 @@ public class RolesAPI {
} 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();
throw new InternalServerErrorException(e);
}
}
}

View File

@ -1,54 +1,258 @@
package org.gcube.service.idm.rest;
import java.lang.reflect.MalformedParametersException;
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.KKUserClient;
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.ContextSerializator;
import org.gcube.service.rest.ErrorMessages;
import org.gcube.service.rest.ResponseBean;
import org.gcube.service.rest.ResponseBeanMap;
import org.gcube.service.rest.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.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;
import jakarta.ws.rs.core.Response.Status;
/**
* <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)
@Path("users")
public class UserApi {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(UserApi.class);
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 = ContextSerializator.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 = ContextSerializator.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);
if (getProfile) {
GCubeUser profile = LiferayProfileClient.getUserProfileByUsername(username);
result.put("profile", profile);
// throw new ForbiddenException(ErrorMessages.NOT_USER_TOKEN_CONTEXT_USED);
}
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());
}
groups.get(0).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" })
public Response getCurrentUsers() {
Status status = Status.OK;
@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 {
Secret secret = SecretManagerProvider.get();
Owner owner = secret.getOwner();
// UserResource user = KKUserClient.getUserById();
// UserResource user = KCUserController.getUserById();
responseBean.setResult(owner);
responseBean.setSuccess(true);
@ -59,175 +263,237 @@ public class UserApi {
} 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();
throw new InternalServerErrorException(e);
}
}
// @GET
// @Path("/{username}")
// @Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
// public Response getUser(
// @PathParam("username") String username) {
// return getUserParameter(username, null);
// }
/**
* @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 = ContextSerializator.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({ "application/json;charset=UTF-8", "application/vnd.api+json" })
@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") String parameter) {
@PathParam("parameter") USER_DETAILS parameter) {
Status status = Status.OK;
ResponseBean responseBean = new ResponseBean();
ObjectMapper objectMapper = ContextSerializator.getSerializer();
Secret secret = SecretManagerProvider.get();
Owner owner = secret.getOwner();
if (username.equals("me")) {
Secret secret = SecretManagerProvider.get();
Owner owner = secret.getOwner();
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)) {
UserRepresentation user = KKUserClient.getUserByUsername(username);
GCubeUser profile = LiferayProfileClient.getUserProfileByUsername(username);
responseBean.setResult(profile);
if (parameter == null)
responseBean.setResult(user);
String jsonData = objectMapper.writeValueAsString(responseBean);
return Response.ok(jsonData).build();
}
// UserResource user = KKUserClient.getUserById();
UserRepresentation user = KCUserController.getUserByUsername(username);
if (parameter.equals("profile") || parameter == null)
responseBean.setResult(user);
else if (parameter.equals("email"))
if (parameter.equals(USER_DETAILS.email))
responseBean.setResult(user.getEmail());
else if (parameter.equals("roles_realm"))
else if (parameter.equals(USER_DETAILS.roles_realm))
responseBean.setResult(user.getRealmRoles());
else if (parameter.equals("roles_clients"))
else if (parameter.equals(USER_DETAILS.roles_clients))
responseBean.setResult(user.getClientRoles());
else if (parameter.equals("groups"))
else if (parameter.equals(USER_DETAILS.groups))
responseBean.setResult(user.getGroups());
else if (parameter.equals("id"))
else if (parameter.equals(USER_DETAILS.id))
responseBean.setResult(user.getId());
else if (parameter.equals("username"))
else if (parameter.equals(USER_DETAILS.username))
responseBean.setResult(user.getUsername());
else if (parameter.equals("name"))
else if (parameter.equals(USER_DETAILS.name))
responseBean.setResult(user.getFirstName() + " " + user.getLastName());
else if (parameter.equals("attributes"))
else if (parameter.equals(USER_DETAILS.attributes))
responseBean.setResult(user.getAttributes());
else if (parameter.equals("organization"))
responseBean.setResult(user.getAttributes().get("organizations"));
else if (parameter.equals(USER_DETAILS.user) || parameter == null)
responseBean.setResult(user);
else
throw new MalformedParametersException("unknow parameter " + parameter);
throw new BadRequestException("unknow parameter " + parameter);
responseBean.setSuccess(true);
ObjectMapper objectMapper = ContextSerializator.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();
throw new InternalServerErrorException(e);
}
}
/**
* Returns users, 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("username") String username,
@QueryParam("firstName") String firstName,
public Response search(
@QueryParam("format") @DefaultValue("username") KCUserController.REPRESENTATION format,
@QueryParam("exact") @DefaultValue("true") Boolean exact,
@QueryParam("username") String username,
@QueryParam("firsnName") String firstName,
@QueryParam("lastName") String lastName,
@QueryParam("email") String email,
@QueryParam("emailVerified") @DefaultValue("true") Boolean emailVerified,
@QueryParam("idpAlias") String idpAlias,
@QueryParam("idpUserId") String idpUserId,
@QueryParam("first") Integer firstResult,
@QueryParam("max") Integer maxResults,
@QueryParam("enabled") @DefaultValue("true") Boolean enabled,
@QueryParam("briefRepresentation") @DefaultValue("true") Boolean briefRepresentation) {
@QueryParam("first") @DefaultValue("0") int firstResult,
@QueryParam("max") @DefaultValue("100") int maxResults,
@QueryParam("enabled") @DefaultValue("true") Boolean enabled) {
Status status = Status.OK;
ResponseBean responseBean;
if (firstResult != null || maxResults != null) {
responseBean = new ResponseBeanPaginated(firstResult, maxResults);
} else {
responseBean = new ResponseBean();
}
responseBean = new ResponseBeanPaginated(firstResult, maxResults);
try {
if (!format.equals(KCUserController.REPRESENTATION.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.REPRESENTATION.full.equals(format);
List<UserRepresentation> users = realm.users().search(
username, firstName, lastName, email,
firstResult, maxResults,
enabled, briefRepresentation, exact);
List<UserRepresentation> users = realm.users().search(username, firstName, lastName, email, emailVerified,
idpAlias, idpUserId, firstResult, maxResults, enabled, briefRepresentation);
responseBean.setResult(users);
responseBean.setResult(KCUserController.formatListUsers(users, format));
responseBean.setSuccess(true);
ObjectMapper objectMapper = ContextSerializator.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("/{username}")
@Produces(MediaType.APPLICATION_JSON)
public Response getById(@PathParam("username") String id) {
Status status = Status.OK;
ResponseBean responseBean = new ResponseBean();
try {
UserRepresentation user = KKUserClient.getUserById(id);
responseBean.setResult(user);
responseBean.setSuccess(true);
ObjectMapper objectMapper = ContextSerializator.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();
throw new InternalServerErrorException(e);
}
}
}

View File

@ -9,13 +9,14 @@ 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.KKUserClient;
import org.gcube.service.idm.keycloack.ErrorMessages;
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.serializers.ContextSerializator;
import org.gcube.service.rest.ErrorMessages;
import org.gcube.service.rest.ResponseBean;
import org.gcube.service.rest.ResponseBeanPaginated;
import org.gcube.smartgears.annotations.ManagedBy;
import org.gcube.vomanagement.usermanagement.UserManager;
import org.gcube.vomanagement.usermanagement.model.GCubeUser;
import org.keycloak.admin.client.resource.UsersResource;
import org.keycloak.representations.idm.UserRepresentation;
@ -29,6 +30,7 @@ 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;
@ -38,7 +40,7 @@ import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status;
@ManagedBy(IdMManager.class)
@Path("social/users")
@Path("2/users")
// @ResourceGroup("Users APIs")
// @ResourceLabel("Greetings APIs")
// @RequestHeaders({
@ -48,27 +50,207 @@ import jakarta.ws.rs.core.Response.Status;
public class UsersSocialAPI {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(UsersSocialAPI.class);
// private static final List<String> GLOBAL_ROLES_ALLOWED_BY_LOCAL_CALL_METHOD =
// Arrays.asList("DataMiner-Manager",
// "Quota-Manager");
/**
* 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();
}
/**
* Read the user's fullname. The user is the one owning the token
*
* @return the user's fullname
*/
@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();
}
/**
* Read the user's email address. The user is the one owning the token
*
* @return rhe user's email address
*/
@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();
}
/**
* 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);
} 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();
}
@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") Integer firstResult,
@QueryParam("max") Integer maxResults) {
@QueryParam("first") Integer first,
@QueryParam("max") Integer max,
@QueryParam("firstResult") Integer firstResult,
@QueryParam("maxResults") Integer maxResults) {
if (first > 0) {
firstResult = first;
}
if (max > 0) {
maxResults = first;
}
Status status = Status.OK;
ResponseBean responseBean;
if (firstResult != null || maxResults != null) {
responseBean = new ResponseBeanPaginated(firstResult, maxResults);
} else {
responseBean = new ResponseBean();
}
responseBean = new ResponseBean();
List<String> usernames = new ArrayList<String>();
try {
List<UserRepresentation> users = KKUserClient.searchUsersByRole(roleName, firstResult, maxResults);
List<UserRepresentation> users = KCUserController.searchUsersByRole(roleName, firstResult, maxResults);
if (users != null) {
for (UserRepresentation user : users) {
usernames.add(user.getUsername());
@ -101,34 +283,36 @@ public class UsersSocialAPI {
@QueryParam("emailVerified") Boolean emailVerified,
@QueryParam("enabled") Boolean enabled,
@QueryParam("briefRepresentation") @DefaultValue("true") Boolean briefRepresentation,
@QueryParam("first") Integer firstResult,
@QueryParam("max") Integer maxResults) {
@QueryParam("first") Integer first,
@QueryParam("max") Integer max,
@QueryParam("firstResult") Integer firstResult,
@QueryParam("maxResults") Integer maxResults) {
if (first > 0) {
firstResult = first;
}
if (max > 0) {
maxResults = first;
}
Status status = Status.OK;
ResponseBean responseBean;
if (firstResult != null || maxResults != null ) {
if (firstResult != null || maxResults != null) {
responseBean = new ResponseBeanPaginated(firstResult, maxResults);
} else {
responseBean = new ResponseBean();
}
try {
UsersResource users_resource = KKUserClient.users();
UsersResource users_resource = KCUserController.users();
List<UserRepresentation> users = users_resource.search(emailVerified, firstResult, maxResults, enabled,
briefRepresentation);
true);
if (briefRepresentation) {
Map<String, String> usernamesAndFullnames = new HashMap<String, String>();
users.forEach(user -> usernamesAndFullnames.put(user.getUsername(), user.getEmail()));
responseBean.setResult(usernamesAndFullnames);
} else {
Map<String, Object> usernamesAndUsers = new HashMap<String, Object>();
users.forEach(user -> usernamesAndUsers.put(user.getUsername(), user));
responseBean.setResult(usernamesAndUsers);
}
responseBean.setSuccess(true);
} catch (Exception e) {
logger.error("Unable to retrieve users", e);
@ -139,148 +323,6 @@ public class UsersSocialAPI {
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 = "Such an attribute doesn't exist"),
@ResponseCode(code = 500, condition = ErrorMessages.ERROR_IN_API_RESULT)
})
public Response readCustomAttr(
@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();
String userId = secret.getOwner().getId();
// String context = secret.getContext();
// TODO: aggiornare implementazione
// if (!TokensUtils.isUserTokenDefault(caller)) {
// status = Status.FORBIDDEN;
// responseBean.setMessage(NOT_USER_TOKEN_CONTEXT_USED);
// logger.warn("Trying to access users method via a token different than USER is
// not allowed");
// return Response.status(status).entity(responseBean).build();
// }
UserManager lr_userManager = null; // UserManagerWSBuilder.getInstance().getUserManager();
try {
GCubeUser user = lr_userManager.getUserByUsername(userId);
String toReturn = (String) lr_userManager.readCustomAttr(user.getUserId(), attributeKey);
responseBean.setSuccess(true);
responseBean.setResult(toReturn);
} catch (Exception e) {
logger.error("Unable to retrieve attribute for user.", e);
responseBean.setMessage(e.toString());
responseBean.setSuccess(false);
status = Status.NOT_FOUND;
}
return Response.status(status).entity(responseBean).build();
}
/**
* 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;
Secret secret = SecretManagerProvider.get();
// Owner owner = secret.getOwner();
String userId = secret.getOwner().getId();
// String context = secret.getContext();
// TODO: aggiornare implementazione
// if (!TokensUtils.isUserTokenDefault(caller)) {
// status = Status.FORBIDDEN;
// responseBean.setMessage(NOT_USER_TOKEN_CONTEXT_USED);
// logger.warn("Trying to access users method via a token different than USER is
// not allowed");
// return Response.status(status).entity(responseBean).build();
// }
UserManager lr_userManager = null; // UserManagerWSBuilder.getInstance().getUserManager();
try {
GCubeUser userprofiile = lr_userManager.getUserByUsername(userId);
responseBean.setResult(userprofiile);
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();
}
@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();
}
// @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" })
@ -297,7 +339,7 @@ public class UsersSocialAPI {
List<String> usernames = new ArrayList<String>();
try {
List<UserRepresentation> users = KKUserClient.users(firstResult, maxResults);
List<UserRepresentation> users = KCUserController.users(firstResult, maxResults);
if (users != null) {
for (UserRepresentation user : users) {
usernames.add(user.getUsername());
@ -331,7 +373,7 @@ public class UsersSocialAPI {
Status status = Status.OK;
ResponseBean responseBean = new ResponseBean();
try {
UserRepresentation user = KKUserClient.getUserByUsername(username);
UserRepresentation user = KCUserController.getUserByUsername(username);
boolean user_exists = user != null;
responseBean.setResult(user_exists);
@ -354,10 +396,36 @@ public class UsersSocialAPI {
return Response.status(status).entity(responseBean).build();
}
// @GET
// @Path("/get-oauth-profile")
// @Produces({ "application/json;charset=UTF-8", "application/vnd.api+json" })
// public boolean getCurrentOAuthProfile() {
// throw new NotImplementedYetException();
// }
@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 = ContextSerializator.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();
}
}

View File

@ -0,0 +1,13 @@
/**
* <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

@ -1,15 +1,15 @@
package org.gcube.service.idm.rest.test;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
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)

View File

@ -0,0 +1,32 @@
package org.gcube.service.rest;
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 USER_NOT_AUTHORIZED_PRIVATE = "User is not authorized to access private data";
public static final String CANNOT_RETRIEVE_PROFILE = "Unable to retrieve user profile";
//
// 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

@ -0,0 +1,76 @@
package org.gcube.service.rest;
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 + "]";
}
}

45
todo.md
View File

@ -1,26 +1,27 @@
# CONFIGURAZIONE
* come ottengo la configurazione di keycloak? devo interrogare IS? file di configurazione?
* lucio dice di chiedere a IS
* luca dice che non va chiesto a IS ma ha un indirizzo fisso che ottengo dal contesto
* [x] verificare configurazione keycloak
* [ ] caching realm e client keycloak?
risoluzione liferay
endpoint "D4science Infrastructure Gateway"
service "Portal"
# API
* [ ] getEmail: non trovo in owner. da dove si prende?
* [ ] accesso al profilo utente
* [ ] come risolvere liferay?
* [ ] implementare metodi relativi a profile
* [ ] API full rest
* [ ] esecuzione in container
* [ ] implementare tutta la gestione profilo
* [ ] leggere parametri da file di container.ini
* [ ] rivedere gestione errori
* [x] formalizzare formato risposta
*
# 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
* [ ] mettere su openstack