From 4b87f742fc66d37cebc1dfe0e6f2a34800f30c2a Mon Sep 17 00:00:00 2001 From: Alfredo Oliviero Date: Wed, 15 May 2024 17:57:25 +0200 Subject: [PATCH] fix and devel for the client --- pom.xml | 57 +- .../controller/AdminKeycloakController.java | 1 - .../idm/controller/AuthController.java | 4 +- .../service/idm/controller/JWTController.java | 36 +- .../idm/controller/KCClientsController.java | 1 - .../idm/controller/KCRolesController.java | 1 - .../idm/controller/KCUserController.java | 9 +- .../idm/keycloack/KkClientFactory.java | 18 +- .../idm/liferay/LiferayClientFactory.java | 48 +- .../org/gcube/service/idm/models/IdmUser.java | 300 ----- .../org/gcube/service/idm/rest/JwtAPI.java | 3 +- .../org/gcube/service/idm/rest/UserAPI.java | 24 +- .../META-INF/enunciate/d4science_docs.fmt | 1185 +++++++++++++++++ .../css/d4science_enunciate_custom.css | 26 + .../org/gcube/service/idm/test/EmptyTest.java | 21 + .../org/gcube/service/idm/test/IsTest.java | 71 + .../service/idm/test/ServiceContextTest.java | 194 +++ src/test/resources/config.ini | 5 + src/test/resources/token_user.properties | 14 + 19 files changed, 1650 insertions(+), 368 deletions(-) delete mode 100644 src/main/java/org/gcube/service/idm/models/IdmUser.java create mode 100644 src/main/resources/META-INF/enunciate/d4science_docs.fmt create mode 100644 src/main/webapp/api-docs/css/d4science_enunciate_custom.css create mode 100644 src/test/java/org/gcube/service/idm/test/EmptyTest.java create mode 100644 src/test/java/org/gcube/service/idm/test/IsTest.java create mode 100644 src/test/java/org/gcube/service/idm/test/ServiceContextTest.java create mode 100644 src/test/resources/config.ini create mode 100644 src/test/resources/token_user.properties diff --git a/pom.xml b/pom.xml index ff5e9f2..929637a 100644 --- a/pom.xml +++ b/pom.xml @@ -59,9 +59,9 @@ org.gcube.idm idm-common-library - 1.0.0-SNAPSHOT + 0.0.1-SNAPSHOT - + com.fasterxml.jackson.core jackson-core @@ -86,10 +86,27 @@ ${jackson.version} + jakarta.activation jakarta.activation-api - 2.1.2 + 1.2.1 + + + + jakarta.xml.bind + jakarta.xml.bind-api + + + + + jakarta.xml.ws + jakarta.xml.ws-api + runtime + @@ -140,16 +157,23 @@ solution: bind version, or exclude them in usermanagement-core - + org.gcube.core common-smartgears + + org.gcube.common common-security @@ -160,6 +184,12 @@ solution: bind version, or exclude them in usermanagement-core common-authorization + + org.gcube.common authorization-control-library @@ -168,7 +198,7 @@ solution: bind version, or exclude them in usermanagement-core org.gcube.core common-smartgears-app - + org.glassfish.jersey.containers @@ -193,10 +223,10 @@ solution: bind version, or exclude them in usermanagement-core - + org.slf4j slf4j-api @@ -235,8 +265,8 @@ solution: bind version, or exclude them in usermanagement-core org.aspectj aspectjrt - 1.9.7 - runtime + + @@ -287,14 +317,17 @@ java.lang.NoClassDefFoundError: org/apache/http/ssl/TrustStrategy org.gcube.resources.discovery ic-client provided - + --> org.gcube.core common-scope provided - --> + + + + ${project.artifactId} diff --git a/src/main/java/org/gcube/service/idm/controller/AdminKeycloakController.java b/src/main/java/org/gcube/service/idm/controller/AdminKeycloakController.java index 08c453f..c55a056 100644 --- a/src/main/java/org/gcube/service/idm/controller/AdminKeycloakController.java +++ b/src/main/java/org/gcube/service/idm/controller/AdminKeycloakController.java @@ -20,7 +20,6 @@ import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.slf4j.LoggerFactory; -import jakarta.ws.rs.NotFoundException; import jakarta.ws.rs.WebApplicationException; public class AdminKeycloakController { diff --git a/src/main/java/org/gcube/service/idm/controller/AuthController.java b/src/main/java/org/gcube/service/idm/controller/AuthController.java index a5d0464..4ad2d25 100644 --- a/src/main/java/org/gcube/service/idm/controller/AuthController.java +++ b/src/main/java/org/gcube/service/idm/controller/AuthController.java @@ -3,13 +3,13 @@ package org.gcube.service.idm.controller; import java.util.List; import java.util.Map; -import javax.ws.rs.ForbiddenException; - import org.gcube.common.keycloak.model.ModelUtils; import org.gcube.common.security.Owner; import org.gcube.common.security.providers.SecretManagerProvider; import org.gcube.common.security.secrets.Secret; +import jakarta.ws.rs.ForbiddenException; + public class AuthController { public final static String IDM_SERVICE_READ = "idm-service-read"; diff --git a/src/main/java/org/gcube/service/idm/controller/JWTController.java b/src/main/java/org/gcube/service/idm/controller/JWTController.java index 6b11bce..6fddc22 100644 --- a/src/main/java/org/gcube/service/idm/controller/JWTController.java +++ b/src/main/java/org/gcube/service/idm/controller/JWTController.java @@ -1,8 +1,6 @@ package org.gcube.service.idm.controller; -import java.util.HashMap; -import java.util.Map; - +import org.gcube.idm.common.models.IdmVerifyObject; import org.gcube.service.idm.serializers.IdmObjectSerializator; import com.auth0.jwt.JWT; @@ -12,7 +10,9 @@ import com.fasterxml.jackson.databind.JsonMappingException; public class JWTController { - public static Map decodeJwtToken(String token) + // public static Map decodeJwtToken(String token) + public static IdmVerifyObject decodeJwtToken(String token) + throws JsonMappingException, JsonProcessingException { DecodedJWT decodedJWT = JWT.decode(token); @@ -21,15 +21,25 @@ public class JWTController { // String signatureJson = // ContextSerializator.decodeBase64String(decodedJWT.getSignature()); - Map decoded = new HashMap(); - decoded.put("jwt_token", token); - decoded.put("token", decodedJWT.getToken()); - decoded.put("header", IdmObjectSerializator.jsonStringToHasmap(headerJson)); - decoded.put("payload", IdmObjectSerializator.jsonStringToHasmap(payloadJson)); - // decoded.put("signature", - // ContextSerializator.jsonStringToHasmap(signatureJson)); - decoded.put("decodedJWT", decodedJWT); - return decoded; + IdmVerifyObject verify = new IdmVerifyObject(); + verify.setJwt_token(token); + verify.setToken(decodedJWT.getToken()); + verify.setHeader(IdmObjectSerializator.jsonStringToHasmap(headerJson)); + verify.setJwt_token(token); + verify.setPayload(IdmObjectSerializator.jsonStringToHasmap(payloadJson)); + verify.setJwt_decoded(decodedJWT); + return verify; + + // Map decoded = new HashMap(); + // decoded.put("jwt_token", token); + // decoded.put("token", decodedJWT.getToken()); + // decoded.put("header", IdmObjectSerializator.jsonStringToHasmap(headerJson)); + // decoded.put("payload", + // IdmObjectSerializator.jsonStringToHasmap(payloadJson)); + // // decoded.put("signature", + // // ContextSerializator.jsonStringToHasmap(signatureJson)); + // decoded.put("jwt_decoded", decodedJWT); + // return decoded; } } diff --git a/src/main/java/org/gcube/service/idm/controller/KCClientsController.java b/src/main/java/org/gcube/service/idm/controller/KCClientsController.java index 8f4ee7d..e554b4f 100644 --- a/src/main/java/org/gcube/service/idm/controller/KCClientsController.java +++ b/src/main/java/org/gcube/service/idm/controller/KCClientsController.java @@ -1,7 +1,6 @@ package org.gcube.service.idm.controller; import java.rmi.ServerException; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Objects; diff --git a/src/main/java/org/gcube/service/idm/controller/KCRolesController.java b/src/main/java/org/gcube/service/idm/controller/KCRolesController.java index 68960b4..bd10bf3 100644 --- a/src/main/java/org/gcube/service/idm/controller/KCRolesController.java +++ b/src/main/java/org/gcube/service/idm/controller/KCRolesController.java @@ -1,6 +1,5 @@ package org.gcube.service.idm.controller; -import java.rmi.ServerException; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; diff --git a/src/main/java/org/gcube/service/idm/controller/KCUserController.java b/src/main/java/org/gcube/service/idm/controller/KCUserController.java index 3080596..37488df 100644 --- a/src/main/java/org/gcube/service/idm/controller/KCUserController.java +++ b/src/main/java/org/gcube/service/idm/controller/KCUserController.java @@ -1,10 +1,7 @@ package org.gcube.service.idm.controller; -import java.rmi.ServerException; -import java.util.Collection; import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; @@ -166,8 +163,10 @@ public class KCUserController { logger.info("Searching user by username: {}", username); RealmResource realm = KkClientFactory.getSingleton().getKKRealm(); - UserRepresentation user = realm.users() - .search(username, true).stream().findFirst().orElse(null); + UsersResource users = realm.users(); + List search_user = users.search(username, true); + + UserRepresentation user = search_user.stream().findFirst().orElse(null); if (user == null) { throw new NotFoundException("cannot retrieve user " + username); diff --git a/src/main/java/org/gcube/service/idm/keycloack/KkClientFactory.java b/src/main/java/org/gcube/service/idm/keycloack/KkClientFactory.java index d5c1a30..9e1537c 100644 --- a/src/main/java/org/gcube/service/idm/keycloack/KkClientFactory.java +++ b/src/main/java/org/gcube/service/idm/keycloack/KkClientFactory.java @@ -58,18 +58,23 @@ public class KkClientFactory { config = configuration; } - public Secret getSecretForInfrastructure(){ + public Secret getSecretForInfrastructure() { return InfrastrctureServiceClient.getSecretForInfrastructure(); } - public IsServerConfig fetchIsConfig(Secret secret) throws InternalServerErrorException { + public IsServerConfig fetchIsConfig() throws InternalServerErrorException { + if (this.secret == null) { + this.secret = getSecretForInfrastructure(); + } + return fetchIsConfig(this.secret); + } + public IsServerConfig fetchIsConfig(Secret secret) throws InternalServerErrorException { + if (secret == null) { + throw new InternalServerErrorException("null secret for fetchIsConfig"); + } try { - if (secret == null){ - throw new InternalServerErrorException("null secret for fetchIsConfig"); - } - IsServerConfig cfg = InfrastrctureServiceClient.serviceConfigFromIS(RUNTIME_RESOURCE_NAME, CATEGORY, END_POINT_NAME, IS_ROOT_SERVICE, secret); logger.info("KeycloakAPICredentials object built {} - {}", cfg.getServerUrl(), cfg.getName()); @@ -93,7 +98,6 @@ public class KkClientFactory { } public static KeycloackApiClient createtKeycloakInstance(IsServerConfig config, String context) { - Keycloak kclient = KeycloakBuilder.builder() .serverUrl(config.getServerUrl()) .realm(config.getName()) diff --git a/src/main/java/org/gcube/service/idm/liferay/LiferayClientFactory.java b/src/main/java/org/gcube/service/idm/liferay/LiferayClientFactory.java index f39388f..653cb0d 100644 --- a/src/main/java/org/gcube/service/idm/liferay/LiferayClientFactory.java +++ b/src/main/java/org/gcube/service/idm/liferay/LiferayClientFactory.java @@ -20,6 +20,11 @@ public class 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; private Secret secret; @@ -31,20 +36,39 @@ public class LiferayClientFactory { return singleton; } + // set a custom secret, instead of fetch it from IS public Secret getSecret() { return secret; } public void setSecret(Secret secret) { this.secret = secret; - this.config = fetchIsConfig(this.secret); + // this.setConfig(null) = null; + // this.client = null; + } + + // set a custom config for the factory, skipping or overriding the fetch from IS + public void setConfig(IsServerConfig config) { + this.config = config; + this.client = null; + } + + public Secret getSecretForInfrastructure() { + return InfrastrctureServiceClient.getSecretForInfrastructure(); + } + + public IsServerConfig fetchIsConfig() throws InternalServerErrorException { + if (this.secret == null) { + this.secret = getSecretForInfrastructure(); + } + return fetchIsConfig(this.secret); } public IsServerConfig fetchIsConfig(Secret secret) throws InternalServerErrorException { + if (secret == null) { + throw new InternalServerErrorException("null secret for fetchIsConfig"); + } try { - if (this.secret == null) - this.secret = InfrastrctureServiceClient.getSecretForInfrastructure(); - IsServerConfig cfg = InfrastrctureServiceClient.serviceConfigFromIS(RUNTIME_RESOURCE_NAME, CATEGORY, END_POINT_NAME, IS_ROOT_SERVICE, secret); logger.info("KeycloakAPICredentials object built {} - {}", cfg.getServerUrl(), cfg.getName()); @@ -58,7 +82,7 @@ public class LiferayClientFactory { public LiferayWSUserManager createtLiferayClientInstance() { if (this.config == null) { - this.config = fetchIsConfig(this.secret); + this.config = fetchIsConfig(); } return createtLiferayClientInstance(this.config); } @@ -79,22 +103,14 @@ public class LiferayClientFactory { throw new InternalServerErrorException("cannot create Liferay client"); } - if (client == null) { - throw new InternalServerErrorException("cannot create Liferay client"); - } + // if (client == null) { + // throw new InternalServerErrorException("cannot create Liferay client"); + // } logger.info("Liferay object built {} - {}", config.getServerUrl(), config.getName()); - return client; } - // public IsServerConfig getConfig() { - // if (this.config == null) { - // this.config = fetchIsConfig(); - // } - // return this.config; - // } - public LiferayWSUserManager getClient() { if (this.client == null) { this.client = createtLiferayClientInstance(); diff --git a/src/main/java/org/gcube/service/idm/models/IdmUser.java b/src/main/java/org/gcube/service/idm/models/IdmUser.java deleted file mode 100644 index 3395766..0000000 --- a/src/main/java/org/gcube/service/idm/models/IdmUser.java +++ /dev/null @@ -1,300 +0,0 @@ -package org.gcube.service.idm.models; - -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.gcube.idm.common.models.IdmProfileInterface; - -public class IdmUser implements IdmProfileInterface { - - protected String self; // link - protected String id; - // protected String origin; - protected Long createdTimestamp; - protected String username; - protected Boolean enabled; - // protected Boolean totp; - protected Boolean emailVerified; - protected String firstName; - protected String lastName; - protected String email; - protected String federationLink; - protected String serviceAccountClientId; // For rep, it points to clientId (not DB ID) - - // @JsonDeserialize(using = StringListMapDeserializer.class) - protected Map> attributes; - // protected List credentials; - // protected Set disableableCredentialTypes; - protected List requiredActions; - // protected List federatedIdentities; - protected List realmRoles; - protected Map> clientRoles; - // protected List clientConsents; - protected Integer notBefore; - - // @Deprecated - // protected Map> applicationRoles; - // @Deprecated - // protected List socialLinks; - - protected List groups; - private Map access; - - public String getSelf() { - return self; - } - - public void setSelf(String self) { - this.self = self; - } - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public Long getCreatedTimestamp() { - return createdTimestamp; - } - - public void setCreatedTimestamp(Long createdTimestamp) { - this.createdTimestamp = createdTimestamp; - } - - public String getFirstName() { - return firstName; - } - - public void setFirstName(String firstName) { - this.firstName = firstName; - } - - public String getLastName() { - return lastName; - } - - public void setLastName(String lastName) { - this.lastName = lastName; - } - - public String getEmail() { - return email; - } - - public void setEmail(String email) { - this.email = email; - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public Boolean isEnabled() { - return enabled; - } - - public void setEnabled(Boolean enabled) { - this.enabled = enabled; - } - - // @Deprecated - // public Boolean isTotp() { - // return totp; - // } - - // @Deprecated - // public void setTotp(Boolean totp) { - // this.totp = totp; - // } - - public Boolean isEmailVerified() { - return emailVerified; - } - - public void setEmailVerified(Boolean emailVerified) { - this.emailVerified = emailVerified; - } - - public Map> getAttributes() { - return attributes; - } - - public void setAttributes(Map> attributes) { - this.attributes = attributes; - } - - // public UserRepresentation singleAttribute(String name, String value) { - // if (this.attributes == null) - // this.attributes = new HashMap<>(); - // attributes.put(name, (value == null ? new ArrayList() : - // Arrays.asList(value))); - // return this; - // } - - // public String firstAttribute(String key) { - // return this.attributes == null ? null - // : this.attributes.get(key) == null ? null - // : this.attributes.get(key).isEmpty() ? null : - // this.attributes.get(key).get(0); - // } - - // public List getCredentials() { - // return credentials; - // } - - // public void setCredentials(List credentials) { - // this.credentials = credentials; - // } - - public List getRequiredActions() { - return requiredActions; - } - - public void setRequiredActions(List requiredActions) { - this.requiredActions = requiredActions; - } - - // public List getFederatedIdentities() { - // return federatedIdentities; - // } - - // public void setFederatedIdentities(List - // federatedIdentities) { - // this.federatedIdentities = federatedIdentities; - // } - - // public List getSocialLinks() { - // return socialLinks; - // } - - // public void setSocialLinks(List socialLinks) { - // this.socialLinks = socialLinks; - // } - - public List getRealmRoles() { - return realmRoles; - } - - public void setRealmRoles(List realmRoles) { - this.realmRoles = realmRoles; - } - - public Map> getClientRoles() { - return clientRoles; - } - - public void setClientRoles(Map> clientRoles) { - this.clientRoles = clientRoles; - } - - // public List getClientConsents() { - // return clientConsents; - // } - - // public void setClientConsents(List clientConsents) - // { - // this.clientConsents = clientConsents; - // } - - public Integer getNotBefore() { - return notBefore; - } - - public void setNotBefore(Integer notBefore) { - this.notBefore = notBefore; - } - - // @Deprecated - // public Map> getApplicationRoles() { - // return applicationRoles; - // } - - public String getFederationLink() { - return federationLink; - } - - public void setFederationLink(String federationLink) { - this.federationLink = federationLink; - } - - public String getServiceAccountClientId() { - return serviceAccountClientId; - } - - public void setServiceAccountClientId(String serviceAccountClientId) { - this.serviceAccountClientId = serviceAccountClientId; - } - - public List getGroups() { - return groups; - } - - public void setGroups(List groups) { - this.groups = groups; - } - - // /** - // * Returns id of UserStorageProvider that loaded this user - // * - // * @return NULL if user stored locally - // */ - // public String getOrigin() { - // return origin; - // } - - // public void setOrigin(String origin) { - // this.origin = origin; - // } - - // public Set getDisableableCredentialTypes() { - // return disableableCredentialTypes; - // } - - // public void setDisableableCredentialTypes(Set - // disableableCredentialTypes) { - // this.disableableCredentialTypes = disableableCredentialTypes; - // } - - public Map getAccess() { - return access; - } - - public void setAccess(Map access) { - this.access = access; - } - - public Map> toAttributes() { - Map> attrs = new HashMap<>(); - - if (getAttributes() != null) - attrs.putAll(getAttributes()); - - if (getUsername() != null) - attrs.put("username", Collections.singletonList(getUsername())); - else - attrs.remove("username"); - - if (getEmail() != null) - attrs.put("email", Collections.singletonList(getEmail())); - else - attrs.remove("email"); - - if (getLastName() != null) - attrs.put("lastName", Collections.singletonList(getLastName())); - - if (getFirstName() != null) - attrs.put("firstName", Collections.singletonList(getFirstName())); - - return attrs; - } - -} diff --git a/src/main/java/org/gcube/service/idm/rest/JwtAPI.java b/src/main/java/org/gcube/service/idm/rest/JwtAPI.java index 4b67bf3..b301b78 100644 --- a/src/main/java/org/gcube/service/idm/rest/JwtAPI.java +++ b/src/main/java/org/gcube/service/idm/rest/JwtAPI.java @@ -3,6 +3,7 @@ package org.gcube.service.idm.rest; import java.util.HashMap; import java.util.Map; +import org.gcube.idm.common.models.IdmVerifyObject; import org.gcube.service.idm.controller.AuthController; import org.gcube.service.idm.controller.JWTController; import org.gcube.service.idm.serializers.IdmObjectSerializator; @@ -50,7 +51,7 @@ public class JwtAPI { try { ObjectMapper objectMapper = IdmObjectSerializator.getSerializer(); - Map decoded = JWTController.decodeJwtToken(token); + IdmVerifyObject decoded = JWTController.decodeJwtToken(token); responseBean.setResult(decoded); responseBean.setSuccess(true); diff --git a/src/main/java/org/gcube/service/idm/rest/UserAPI.java b/src/main/java/org/gcube/service/idm/rest/UserAPI.java index b0958e1..c170118 100644 --- a/src/main/java/org/gcube/service/idm/rest/UserAPI.java +++ b/src/main/java/org/gcube/service/idm/rest/UserAPI.java @@ -23,6 +23,7 @@ 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.GroupRepresentation; import org.keycloak.representations.idm.MappingsRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.slf4j.LoggerFactory; @@ -225,18 +226,23 @@ public class UserAPI { MappingsRepresentation roles = userResource.roles().getAll(); result.put("roles", roles); - List groups = userResource.groups(); - result.put("groups", groups); + // TODO: update keycloak library to 24. GroupRepresentation + // https://www.keycloak.org/docs-api/24.0.1/rest-api/index.html#GroupRepresentation + // https://www.keycloak.org/docs-api/21.1.2/rest-api/index.html#_grouprepresentation + + // cannot deserialize using lib 21 + // List groups = userResource.groups(); + // result.put("groups", groups); - HashMap groupRolesRealm = new HashMap(); - HashMap groupRolesClients = new HashMap(); + // HashMap groupRolesRealm = new HashMap(); + // HashMap groupRolesClients = new HashMap(); - result.put("groupRolesRealm", groupRolesRealm); - result.put("groupRolesClients", groupRolesClients); + // result.put("groupRolesRealm", groupRolesRealm); + // result.put("groupRolesClients", groupRolesClients); - for (GroupRepresentation g : groups) { - groupRolesClients.put(g.getId(), g.getClientRoles()); - } + // for (GroupRepresentation g : groups) { + // groupRolesClients.put(g.getId(), g.getClientRoles()); + // } } return result; } diff --git a/src/main/resources/META-INF/enunciate/d4science_docs.fmt b/src/main/resources/META-INF/enunciate/d4science_docs.fmt new file mode 100644 index 0000000..7e7c7e3 --- /dev/null +++ b/src/main/resources/META-INF/enunciate/d4science_docs.fmt @@ -0,0 +1,1185 @@ + +[#ftl] +[#-- + + Copyright © 2006-2016 Web Cohesion (info@webcohesion.com) + + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +--] +[#-- @ftlvariable name="resourceApis" type="java.util.List" --] +[#-- @ftlvariable name="serviceApis" type="java.util.List" --] +[#-- @ftlvariable name="data" type="java.util.List" --] +[#-- @ftlvariable name="downloads" type="java.util.List" --] +[#-- @ftlvariable name="title" type="java.lang.String" --] +[#-- @ftlvariable name="indexPageName" type="java.lang.String" --] +[#-- @ftlvariable name="disableMountpoint" type="java.lang.Boolean" --] +[#-- @ftlvariable name="disableResourceLinks" type="java.lang.Boolean" --] +[#-- @ftlvariable name="apiRelativePath" type="java.lang.String" --] +[#-- @ftlvariable name="cssFile" type="java.lang.String" --] +[#-- @ftlvariable name="additionalCssFiles" type="java.util.List" --] +[#-- @ftlvariable name="copyright" type="java.lang.String" --] +[#-- @ftlvariable name="apiDoc" type="java.lang.String" --] +[#-- @ftlvariable name="swaggerUI" type="com.webcohesion.enunciate.api.InterfaceDescriptionFile" --] +[#-- @ftlvariable name="favicon" type="java.lang.String" --] +[#-- @ftlvariable name="includeApplicationPath" type="java.lang.Boolean" --] +[#-- @ftlvariable name="includeDataTypesHomeIndex" type="java.lang.Boolean" --] +[#--set up the subnavigation menus--] +[#assign nav_sections = { } /] +[#if resourceApis?size > 0] + [#assign nav_sections = nav_sections + { "Resources" : "resources.html" }/] +[/#if] +[#if serviceApis?size > 0] + [#assign nav_sections = nav_sections + { "Services" : "services.html" }/] +[/#if] +[#if data?size > 0] + [#list data as syntax] + [#assign nav_sections = { syntax.label : syntax.slug + ".html" } /] + [/#list] +[/#if] +[#if downloads?size > 0] + [#assign nav_sections = nav_sections + { "Files and Libraries" : "downloads.html"} /] +[/#if] +[#--Basic boilerplate macro.--] +[#macro boilerplate title=title breadcrumbs=[{"title" : "Home", "href" : indexPageName}] pagenav=[] codeblocks=true] + + + + + + + + + ${title} + + + + + + + + [#if cssFile??] + + + [/#if] + [#list additionalCssFiles as additionalCssFile] + + [/#list] + [#if favicon??] + + + [/#if] + + + + + + +
+ D4Science + D4Science + + Don't have a D4Science account? + + Create one + + Could not find what you are looking for? + + Contact us. +
+ + + +
+
+ + +
+ + + [#nested/] + +
+
+

[#if copyright??]Copyright © ${copyright}. [/#if]Generated by Enunciate.

+
+
+ +
+
+
+ + + + + + + + + + + + + + +[/#macro] +[#--Macro that wraps its text in a deprecated tag if the element is deprecated.--] +[#macro deprecation element] + [#assign deprecated=(element?? && element.deprecated??)/] + [#if deprecated][/#if][#nested/][#if deprecated][/#if] +[/#macro] +[@file name=indexPageName] + [#assign pagenav=[]/] + [#if resourceApis?size > 0] + [#assign pagenav=pagenav + [{ "href" : "#resources", "title" : "Resources" }]/] + [/#if] + [#if serviceApis?size > 0] + [#assign pagenav=pagenav + [{ "href" : "#services", "title" : "Services" }]/] + [/#if] + [#list data as syntax] + [#assign pagenav=pagenav + [{ "href" : "#" + syntax.slug, "title" : syntax.label }]/] + [/#list] + [@boilerplate pagenav=pagenav] + [#if apiDoc??] + + [/#if] + [#if resourceApis?size > 0] + +

Resources

+ [#list resourceApis as resourceApi] + [#if downloads?size > 0] + +

+ The resources use a data model that is supported by a set of client-side libraries that are made available on the + files and libraries page. +

+ [/#if] + [#if resourceApi.wadlFile??] + +

+ There is a WADL document available that describes the resources API. +

+ [/#if] + [#if swaggerUI??] + +

+ You may also enjoy the interactive interface provided for this API by Swagger. +

+

+ Try it out! +

+ [/#if] + + + + + [#if resourceApi.includeResourceGroupName!false] + + [/#if] + + + + + + + [#list resourceApi.resourceGroups as resourceGroup] + [@processResourceGroup resourceGroup=resourceGroup/] + + [#if resourceApi.includeResourceGroupName!false] + + [/#if] + + + + + [/#list] + +
namepathmethodsdescription
[@deprecation element=resourceGroup]${resourceGroup.label}[/@deprecation]
    [#list resourceGroup.paths as path]
  • [@deprecation element=resourceGroup][#if ((includeApplicationPath!false) && (resourceGroup.relativeContextPath?has_content))]/${resourceGroup.relativeContextPath}[/#if]${path.path}[/@deprecation]
  • [/#list]
    [#list resourceGroup.paths as path]
  • [@deprecation element=resourceGroup][#list path.methods as method]${method} [/#list][/@deprecation]
  • [/#list]
[@deprecation element=resourceGroup]${resourceGroup.description!" "}[/@deprecation]
+ [/#list] + [/#if] + [#if serviceApis?size > 0] + +

Services

+ [#list serviceApis as serviceApi] + [#list serviceApi.serviceGroups as serviceGroup] + + + + + + + + + + + [#list serviceGroup.services as service] + [@processService service=service/] + + + + + [/#list] + +
Namespace ${serviceGroup.namespace!"(Default)"}[#if serviceGroup.wsdlFile??] (wsdl)[/#if]
namedescription
[@deprecation element=service]${service.label}[/@deprecation][@deprecation element=service]${service.description!" "}[/@deprecation]
+ [/#list] + [/#list] + [#if downloads?size > 0] + +

The services API is also accessible by a set of client-side libraries that can be downloaded from the files and libraries page.

+ [/#if] + [/#if] + [#if data?size > 0 && includeDataTypesHomeIndex] + +

Data Types

+ [#list data as syntax] + [@processDataSyntax syntax=syntax/] + +

${syntax.label}

+ [#list syntax.namespaces as ns] + [#if ns.types?size > 0] + + + [#if ns.uri??] + [#if ns.uri?length > 0] + + [#else] + + [/#if] + [/#if] + + + + + + + + [#list ns.types as type] + + + + + [/#list] + +
Namespace ${ns.uri}[#if ns.schemaFile??] (schema)[/#if]Default Namespace [#if ns.schemaFile??] (schema)[/#if]
typedescription
[@deprecation element=type]${type.label}[/@deprecation][@deprecation element=type]${type.description}[/@deprecation]
+ [/#if] + [/#list] + [/#list] + [#elseif data?size > 0] + [#list data as syntax] + [@processDataSyntax syntax=syntax/] + [/#list] + [/#if] + [/@boilerplate] +[/@file] +[@file name="data.html"] + [#assign pagenav=[]/] + [#list data as syntax] + [#assign pagenav=pagenav + [{ "href" : "#" + syntax.slug, "title" : syntax.label }]/] + [/#list] + [@boilerplate title=title + ": Data Types" breadcrumbs=[{"title" : "Home", "href" : indexPageName}, {"title" : "Data Types" , "href" : "data.html"}] pagenav=pagenav] +

Data Types

+ [#list data as syntax] + +

${syntax.label}

+ [#list syntax.namespaces as ns] + [#if ns.types?size > 0] + + + [#if ns.uri??] + [#if ns.uri?length > 0] + + [#else] + + [/#if] + [/#if] + + + + + + + + [#list ns.types as type] + + + + + [/#list] + +
Namespace ${ns.uri}[#if ns.schemaFile??] (schema)[/#if]Default Namespace [#if ns.schemaFile??] (schema)[/#if]
typedescription
[@deprecation element=type]${type.label}[/@deprecation][@deprecation element=type]${type.description}[/@deprecation]
+ [/#if] + [/#list] + [/#list] + [/@boilerplate] +[/@file] +[#if downloads?size > 0] + [@file name="downloads.html"] + [#assign pagenav=[]/] + [#list downloads as download] + [#assign pagenav=pagenav + [{ "href" : "#" + download.slug, "title" : download.name }]/] + [/#list] + [@boilerplate title=title + ": Files and Libraries" breadcrumbs=[{"title" : "Home", "href" : indexPageName}, { "title" : "Files and Libraries" , "href" : "downloads.html"}] codeblocks=true pagenav=pagenav] +

Files and Libraries

+ + [#list downloads as download] +

${download.name}

+ [#if download.created??] +

Created ${download.created?date?string.long}

+ [/#if] + [#if download.artifactId??] +
+ [#if download.groupId??] +
groupId
+
${download.groupId}
+ [/#if] + [#if download.artifactId??] +
artifactId
+
${download.artifactId}
+ [/#if] + [#if download.version??] +
version
+
${download.version}
+
+ [/#if] + [/#if] + [#if download.description??] +

${download.description}

+ [/#if] + + + + + + + + + + + [#list download.files as file] + + + + + + [/#list] + +
Files
namesizedescription
${file.name}${file.size}${file.description!download.description!" "}
+ [/#list] + [/@boilerplate] + [/@file] +[/#if] +[#if resourceApis?size > 0] + [@file name="resources.html"] + [@boilerplate title=title + ": Resources" breadcrumbs=[{"title" : "Home", "href" : indexPageName}, {"title" : "Resources" , "href" : "resources.html"}]] +

Resources

+ + [#list resourceApis as resourceApi] + [#if downloads?size > 0] +

+ The resources use a data model that is supported by a set of client-side libraries that are made available on the + files and libraries page. +

+ [/#if] + [#if resourceApi.wadlFile??] +

+ There is a WADL document available that describes the resources API. +

+ [/#if] + [#if swaggerUI??] +

+ You may also enjoy the interactive interface provided for this API by Swagger. +

+

+ Try it out! +

+ [/#if] + + + + [#if resourceApi.includeResourceGroupName!false] + + [/#if] + + + + + + + [#list resourceApi.resourceGroups as resourceGroup] + + [#if resourceApi.includeResourceGroupName!false] + + [/#if] + + + + + [/#list] + +
namepathmethodsdescription
[@deprecation element=resourceGroup]${resourceGroup.label}[/@deprecation]
    [#list resourceGroup.paths as path]
  • [@deprecation element=resourceGroup][#if ((includeApplicationPath!false) && (resourceGroup.relativeContextPath?has_content))]/${resourceGroup.relativeContextPath}[/#if]${path.path}[/@deprecation]
  • [/#list]
    [#list resourceGroup.paths as path]
  • [@deprecation element=resourceGroup][#list path.methods as method]${method} [/#list][/@deprecation]
  • [/#list]
[@deprecation element=resourceGroup]${resourceGroup.description!" "}[/@deprecation]
+ [/#list] + [/@boilerplate] + [/@file] +[/#if] +[#if serviceApis?size > 0] + [@file name="services.html"] + [@boilerplate title=title + ": Services" breadcrumbs=[{"title" : "Home", "href" : indexPageName}, {"title" : "Services" , "href" : "services.html"}]] +

Services

+ [#list serviceApis as serviceApi] + [#list serviceApi.serviceGroups as serviceGroup] + + + + + + + + + + + [#list serviceGroup.services as service] + + + + + [/#list] + +
Namespace ${serviceGroup.namespace}[#if serviceGroup.wsdlFile??] (wsdl)[/#if]
namedescription
[@deprecation element=service]${service.label}[/@deprecation][@deprecation element=service]${service.description!" "}[/@deprecation]
+ [/#list] + [/#list] + [#if downloads?size > 0] + +

The services API is also accessible by a set of client-side libraries that can be downloaded from the files and libraries page.

+ [/#if] + [/@boilerplate] + [/@file] +[/#if] +[#macro processResourceGroup resourceGroup] + [#assign pagenav=[]/] + [#list resourceGroup.resources as resource] + [#list resource.methods as method] + [#assign path=resource.path/] + [#if ((includeApplicationPath!false) && (resourceGroup.relativeContextPath?has_content))] + [#assign path="/" + resourceGroup.relativeContextPath + resource.path/] + [/#if] + [#assign pagenav=pagenav + [{ "href" : "#" + method.slug, "title" : method.label + " " + path }]/] + [/#list] + [/#list] + [#-- @ftlvariable name="resourceGroup" type="com.webcohesion.enunciate.api.resources.ResourceGroup" --] + [@file name=resourceGroup.slug + ".html"] + [@boilerplate title=title + ": " + resourceGroup.label breadcrumbs=[{"title" : "Home", "href" : indexPageName}, {"title" : "Resources" , "href" : "resources.html"}, {"title" : resourceGroup.label , "href" : resourceGroup.slug + ".html"}] pagenav=pagenav] +

${resourceGroup.label} Resource

+ [#if resourceGroup.description??] + +

${resourceGroup.description}

+ [/#if] + [#list resourceGroup.resources as resource] + [#if resource.since?? || resource.version?? || resource.seeAlso??] + +
+ [#if resource.since??] +
Available Since
+
${resource.since}
+ [/#if] + [#if resource.version??] +
Version
+
${resource.version}
+ [/#if] + [#if resource.seeAlso??] + [#list resource.seeAlso as seeAlso] +
See Also
+
${seeAlso}
+ [/#list] + [/#if] +
+ [/#if] + [#list resource.methods as method] + +
+

${method.label} [#if ((includeApplicationPath!false) && (resourceGroup.relativeContextPath?has_content))]/${resourceGroup.relativeContextPath}[/#if]${resource.path}[#if !disableResourceLinks!false] [/#if]

+ [#if resourceGroup.deprecated?? || method.deprecated??] + +
This method has been deprecated. [#if method.deprecated??] ${method.deprecated!""}[#else] ${resource.deprecated!""}[/#if]
+ [/#if] + [#if method.description??] + +

${method.description}

+ [/#if] + [#-- would be nice to enable a "Try it out" link to Swagger. See https://github.com/swagger-api/swagger-spec/issues/239 + [#if swaggerUI??] + +

Try it out!

+ [/#if] + --] + [#assign securityRoles=method.securityRoles![]/] + [#if (method.since?? || method.version?? || method.seeAlso?? || securityRoles?size > 0)] + +
+ [#if method.since??] +
Available Since
+
${method.since}
+ [/#if] + [#if method.version??] +
Version
+
${method.version}
+ [/#if] + [#if securityRoles?size > 0] +
Security Roles Allowed
+
[#list securityRoles as role]${role}[#if role_has_next], [/#if][/#list]
+ [/#if] + [#if method.seeAlso??] + [#list method.seeAlso as seeAlso] +
See Also
+
${seeAlso}
+ [/#list] + [/#if] +
+ [/#if] + [#if method.parameters?size > 0] + + + + + + + + + [#assign includeDefault=method.includeDefaultParameterValues/] + [#if includeDefault] + + [/#if] + [#assign includeConstraints=method.hasParameterConstraints/] + [#if includeConstraints] + + [/#if] + [#assign includeMultiplicity=method.hasParameterMultiplicity/] + [#if includeMultiplicity] + + [/#if] + + + + [#list method.parameters as parameter] + + + + + [#if includeDefault] + + [/#if] + [#if includeConstraints] + + [/#if] + [#if includeMultiplicity] + + [/#if] + + [/#list] + +
Request Parameters
nametypedescriptiondefaultconstraintsmultivalued
${parameter.name}${parameter.typeLabel}${parameter.description!" "}${parameter.defaultValue!" "}${parameter.constraints!" "}${parameter.multivalued?string("yes", "no")}
+ [/#if] + [#if method.requestEntity??] + + + + + + + + [#if method.requestEntity.description??] + + [/#if] + + + + [#list method.requestEntity.mediaTypes as io] + + + + [#if io_index = 0 && method.requestEntity.description??] + 1] rowspan="${method.requestEntity.mediaTypes?size}" class="multi-row-description"[/#if]>${method.requestEntity.description} + [/#if] + + [/#list] + +
Request Body
media typedata typedescription
${io.mediaType}[@referenceDataType referenceType=io.dataType!{"label" : "(custom)"}/][#if io.syntax??] (${io.syntax})[/#if]
+ [/#if] + [#if method.responseCodes?size > 0] + + + + + + + + [#assign hasExpectedTypes=false/] + [#list method.responseCodes as responseCode] + [#if responseCode.mediaTypes?size > 0] + [#assign hasExpectedTypes=true/] + [/#if] + [/#list] + [#if hasExpectedTypes] + + [/#if] + + + + [#list method.responseCodes as responseCode] + + + + [#if hasExpectedTypes] + + [/#if] + + [/#list] + +
Response Codes
codeconditiontype
${responseCode.code}${responseCode.condition!""}
    [#list responseCode.mediaTypes as io]
  • [@referenceDataType referenceType=io.dataType!{"label" : "(custom)"}/][#if io.syntax??] (${io.syntax})[/#if]
  • [/#list]
+ [/#if] + [#if method.responseEntity??] + + + + + + + + [#if method.responseEntity.description??] + + [/#if] + + + + [#list method.responseEntity.mediaTypes as io] + + + + [#if io_index = 0 && method.responseEntity.description??] + 1] rowspan="${method.responseEntity.mediaTypes?size}" class="multi-row-description"[/#if]>${method.responseEntity.description} + [/#if] + + [/#list] + +
Response Body
media typedata typedescription
${io.mediaType}[@referenceDataType referenceType=io.dataType!{"label" : "(custom)"}/][#if io.syntax??] (${io.syntax})[/#if]
+ [/#if] + [#if method.warnings?size > 0] + + + + + + + + + + + [#list method.warnings as responseCode] + + + + + [/#list] + +
Response Warnings
codecondition
${responseCode.code}${responseCode.condition!""}
+ [/#if] + [#if method.responseHeaders?size > 0] + + + + + + + + + + + [#list method.responseHeaders as header] + + + + + [/#list] + +
Response Headers
namedescription
${header.name}${header.description!" "}
+ + [/#if] + [#if method.example??] +

Example

+ +
+
+
+
Request
+
+${method.example.requestHeaders?xhtml}
+            [#if method.example.requestLang??]
+                
+${method.example.requestBody?xhtml}
+                
+            [/#if]
+              
+
+
+
Response
+
+${method.example.responseHeaders?xhtml}
+            [#if method.example.responseLang??]
+                
+${method.example.responseBody?xhtml}
+                
+            [/#if]
+              
+
+
+
+ [/#if] +
+ [/#list] + [/#list] + [/@boilerplate] + [/@file] +[/#macro] +[#macro processService service] + [#assign pagenav=[]/] + [#list service.operations as operation] + [#assign pagenav=pagenav + [{ "href" : "#" + operation.slug, "title" : operation.name }]/] + [/#list] + [#-- @ftlvariable name="service" type="com.webcohesion.enunciate.api.services.Service" --] + [@file name=service.slug + ".html"] + [@boilerplate title=title + ": " + service.label breadcrumbs=[{"title" : "Home", "href" : indexPageName}, {"title" : service.label , "href" : service.slug + ".html"}] pagenav=pagenav] +

${service.label} Service

+ [#if service.deprecated??] + +
This service has been deprecated. ${service.deprecated}
+ [/#if] + [#if service.description??] + +

${service.description}

+ [/#if] + +
+ [#if service.namespace?? && service.namespace?length > 0] +
Namespace
+
${service.namespace}
+ [/#if] + [#if service.group.wsdlFile??] +
WSDL
+
${service.group.wsdlFile.href}
+ [/#if] + [#if service.path??] +
Path
+
${service.path}
+ [/#if] + [#if service.since??] +
Available Since
+
${service.since}
+ [/#if] + [#if service.version??] +
Version
+
${service.version}
+ [/#if] + [#if service.seeAlso??] + [#list service.seeAlso as seeAlso] +
See Also
+
${seeAlso}
+ [/#list] + [/#if] +
+ [#list service.operations as operation] + +

${operation.name} Operation

+ [#if operation.deprecated??] + +
This method has been deprecated. ${operation.deprecated}
+ [/#if] + [#if operation.description??] + +

${operation.description}

+ [/#if] + [#if (operation.since?? || operation.version?? || operation.seeAlso??)] + +
+ [#if operation.since??] +
Available Since
+
${operation.since}
+ [/#if] + [#if operation.version??] +
Version
+
${operation.version}
+ [/#if] + [#if operation.seeAlso??] + [#list operation.seeAlso as seeAlso] +
See Also
+
${seeAlso}
+ [/#list] + [/#if] +
+ [/#if] + [#if operation.inputParameters?size > 0] + + + + + + + + + + + + [#list operation.inputParameters as parameter] + + + + + + [/#list] + +
Input Parameters
nametypedescription
${parameter.name}[@referenceDataType referenceType=parameter.dataType/]${parameter.description!" "}
+ [/#if] + [#if operation.httpRequestHeaders?size > 0] + + + + + + + + + + + + [#list operation.httpRequestHeaders as parameter] + + + + + + [/#list] + +
HTTP Request Headers
nametypedescription
${parameter.name}[@referenceDataType referenceType=parameter.dataType/]${parameter.description!" "}
+ [/#if] + [#if operation.outputParameters?size > 0] + + + + + + + + + + + + [#list operation.outputParameters as parameter] + + + + + + [/#list] + +
Output Parameters
nametypedescription
${parameter.name}[@referenceDataType referenceType=parameter.dataType/]${parameter.description!" "}
+ [/#if] + [#if operation.returnType??] + + + + + + + + + + + + + + + +
Return Value
typedescription
[@referenceDataType referenceType=operation.returnType/]${operation.returnDescription!" "}
+ [/#if] + [#if operation.faults?size > 0] + + + + + + + + + + + + [#list operation.faults as fault] + + + + + + [/#list] + +
Faults
nametypeconditions
${fault.name}[@referenceDataType referenceType=fault.dataType/]${fault.conditions!" "}
+ [/#if] + [/#list] + [/@boilerplate] + [/@file] +[/#macro] +[#macro processDataSyntax syntax] + [#-- @ftlvariable name="syntax" type="com.webcohesion.enunciate.api.datatype.Syntax" --] + [@file name=syntax.slug + ".html"] + [@boilerplate title=title + ": " + syntax.label breadcrumbs=[{"title" : "Home", "href" : indexPageName}, {"title" : syntax.label , "href" : syntax.slug + ".html"} ]] +

${syntax.label}

+ [#list syntax.namespaces as ns] + [#if ns.types?size > 0] + + + [#if ns.uri??] + [#if ns.uri?length > 0] + + [#else] + + [/#if] + [/#if] + + + + + + + + [#list ns.types as type] + [@processDataType type=type/] + + + + + [/#list] + +
Namespace ${ns.uri}[#if ns.schemaFile??] (schema)[/#if]Default Namespace [#if ns.schemaFile??] (schema)[/#if]
typedescription
[@deprecation element=type]${type.label}[/@deprecation][@deprecation element=type]${type.description}[/@deprecation]
+ [/#if] + [/#list] + [/@boilerplate] + [/@file] +[/#macro] +[#macro processDataType type] + [#-- @ftlvariable name="type" type="com.webcohesion.enunciate.api.datatype.DataType" --] + [#assign pagenav=[]/] + [#if type.values??] + [#list type.values as value] + [#assign pagenav=pagenav + [{ "href" : "#" + value.value, "title" : value.value }]/] + [/#list] + [/#if] + [@file name=type.slug + ".html"] + [@boilerplate title=title + ": " + type.label breadcrumbs=[{"title" : "Home", "href" : indexPageName}, {"title" : type.syntax.label , "href" : type.syntax.slug + ".html"}, {"title" : type.label , "href" : type.slug + ".html"} ] pagenav=pagenav codeblocks=true] +

${type.label} Data Type

+ [#if type.deprecated??] + +
This data type has been deprecated. ${type.deprecated}
+ [/#if] + [#if type.description??] + +

${type.description}

+ [/#if] + +
+ [#if type.namespace.uri??] +
Namespace
+ [#if type.namespace.uri?length > 0] +
${type.namespace.uri}
+ [#else] +
(Default)
+ [/#if] + [/#if] + [#if type.namespace.schemaFile??] +
Schema
+
${type.namespace.schemaFile.href}
+ [/#if] + [#if type.since??] +
Available Since
+
${type.since}
+ [/#if] + [#if type.version??] +
Version
+
${type.version}
+ [/#if] + [#if type.abstract] +
Abstract Type
+
+ [/#if] + [#if type.subtypes??] +
Subtypes
+
[#list type.subtypes as subtype][#if subtype.slug??]${subtype.label}[#else]${subtype.label}[/#if][#if subtype_has_next], [/#if][/#list]
+ [/#if] + [#if type.interfaces??] +
Implemented Interfaces
+
[#list type.interfaces as iface]${iface.label}[#if iface_has_next], [/#if][/#list]
+ [/#if] + [#if type.seeAlso??] + [#list type.seeAlso as seeAlso] +
See Also
+
${seeAlso}
+ [/#list] + [/#if] +
+ [#if type.values??] + + + + + + + + + + + [#list type.values as value] + + + + + [/#list] + +
Values
valuedescription
${value.value}[#if value.since??]since ${value.since} [/#if]${value.description!" "}
+ [/#if] + [#if type.properties??] + + + + + + + + [#list type.propertyMetadata?keys as meta] + + [/#list] + + + + + [#list type.properties as property] + + + + [#list type.propertyMetadata?keys as meta] + + [/#list] + + + [/#list] + + [#if type.supertypes??] + [#list type.supertypes as supertype] + [#if supertype.value?? && supertype.value.properties?? && supertype.value.properties?size > 0] + + + + + [#list supertype.value.properties as superProperty] + + + + [#list type.propertyMetadata?keys as meta] + + [/#list] + + + [/#list] + + [/#if] + [/#list] + [/#if] +
Properties
namedata type${type.propertyMetadata[meta]}description
[@deprecation element=property]${property.name}[/@deprecation][@deprecation element=property][@referenceDataType referenceType=property.dataType/][/@deprecation][@deprecation element=property][@printPropertyMetadata property=property meta=meta/][/@deprecation][@deprecation element=property][#if property.since??]since ${property.since} [/#if]${property.description!" "}[/@deprecation]
Properties inherited from ${supertype.label}
${superProperty.name}[@referenceDataType referenceType=superProperty.dataType/][@printPropertyMetadata property=superProperty meta=meta/][#if superProperty.since??]since ${superProperty.since} [/#if]${superProperty.description!" "}
+ [/#if] + [#if type.example??] + +

Example

+ [#if type.abstract && type.subtypes?? ] + +
This data type is abstract. The example below may be incomplete. More accurate examples can be found in subtypes pages.
+ [/#if] + +
${type.example.body?xhtml}
+ [/#if] + [/@boilerplate] + [/@file] +[/#macro] +[#macro referenceDataType referenceType] +[#-- @ftlvariable name="type" type="com.webcohesion.enunciate.api.datatype.DataTypeReference" --] +[#if referenceType.containers??][#list referenceType.containers as container]${container?string} of [/#list][/#if][#if referenceType.slug??][/#if]${referenceType.label!"(custom)"}[#if referenceType.slug??][/#if] +[/#macro] +[#macro printPropertyMetadata property meta] + [#assign metaValue=property[meta]!({ "structure" : true })/] + [#if metaValue?is_hash && metaValue.structure!false] +[#if metaValue.href??][/#if][#if metaValue.title??][/#if]${metaValue.value!" "}[#if metaValue.title??][/#if][#if metaValue.href??][/#if] + [#else] +${metaValue} + [/#if] +[/#macro] \ No newline at end of file diff --git a/src/main/webapp/api-docs/css/d4science_enunciate_custom.css b/src/main/webapp/api-docs/css/d4science_enunciate_custom.css new file mode 100644 index 0000000..48a9abd --- /dev/null +++ b/src/main/webapp/api-docs/css/d4science_enunciate_custom.css @@ -0,0 +1,26 @@ +.d4science_intro { + top: 0; + z-index: 2000; + position: fixed; + display: block ruby; + padding: 10px; + background: white; + width: 100%; + height: 100px; +} + +.navbar-fixed-top { + top: 100px !important; +} + +.sidebar { + top: 160px !important; +} + +.navbar { + margin-bottom: 40px !important; +} + +.main { + top: 90px; +} \ No newline at end of file diff --git a/src/test/java/org/gcube/service/idm/test/EmptyTest.java b/src/test/java/org/gcube/service/idm/test/EmptyTest.java new file mode 100644 index 0000000..82ce61a --- /dev/null +++ b/src/test/java/org/gcube/service/idm/test/EmptyTest.java @@ -0,0 +1,21 @@ +package org.gcube.service.idm.test; + +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Luca Frosini (ISTI - CNR) + */ +public class EmptyTest { + + private static final Logger logger = LoggerFactory.getLogger(EmptyTest.class); + + @Test + public void testVersions() { + logger.debug("Testing works"); + + return; + } + +} diff --git a/src/test/java/org/gcube/service/idm/test/IsTest.java b/src/test/java/org/gcube/service/idm/test/IsTest.java new file mode 100644 index 0000000..ac68323 --- /dev/null +++ b/src/test/java/org/gcube/service/idm/test/IsTest.java @@ -0,0 +1,71 @@ +package org.gcube.service.idm.test; + +import org.gcube.idm.common.is.IsServerConfig; +import org.gcube.service.idm.keycloack.KkClientFactory; +import org.gcube.service.idm.liferay.LiferayClientFactory; +import org.gcube.smartgears.ContextProvider; +import org.gcube.smartgears.configuration.container.ContainerConfiguration; +import org.gcube.smartgears.context.application.ApplicationContext; +import org.gcube.smartgears.context.container.ContainerContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import jakarta.ws.rs.NotFoundException; + +public class IsTest extends ServiceContextTest { + private static final Logger logger = LoggerFactory.getLogger(IsTest.class); + + // @Test + // @Ignore + public void checkContext() throws Exception { + ApplicationContext ctx = ContextProvider.get(); + ContainerContext container = ctx.container(); + ContainerConfiguration configuration = container.configuration(); + + String infra_context = "/" + configuration.infrastructure(); + logger.debug("Testing Keycloak service IS config %s", infra_context ); + } + + // @Test + public void testIsKeycloak() throws Exception { + + logger.debug("Testing Keycloak service IS config"); + IsServerConfig config = null; + + KkClientFactory client = KkClientFactory.getSingleton(); + org.junit.Assert.assertNotNull(client); + client.setSecret(ServiceContextTest.current_secret); + + try { + config = client.fetchIsConfig(ServiceContextTest.current_secret); + } catch (NotFoundException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + org.junit.Assert.assertNotNull(config); + + logger.debug("fetched Keycloak service IS config "); + } + + //@Test + public void testIsLiferay() { + logger.debug("Testing Liferay service IS config"); + IsServerConfig config = null; + + LiferayClientFactory client = LiferayClientFactory.getSingleton(); + org.junit.Assert.assertNotNull(client); + + try { + client.setSecret(ServiceContextTest.current_secret); + config = client.fetchIsConfig(); + } catch (NotFoundException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + org.junit.Assert.assertNotNull(config); + + logger.debug("fetched Liferay service IS config "); + } +} diff --git a/src/test/java/org/gcube/service/idm/test/ServiceContextTest.java b/src/test/java/org/gcube/service/idm/test/ServiceContextTest.java new file mode 100644 index 0000000..4209cae --- /dev/null +++ b/src/test/java/org/gcube/service/idm/test/ServiceContextTest.java @@ -0,0 +1,194 @@ +package org.gcube.service.idm.test; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +import org.gcube.common.keycloak.KeycloakClientFactory; +import org.gcube.common.keycloak.KeycloakClientHelper; +import org.gcube.common.keycloak.model.TokenResponse; +import org.gcube.common.security.providers.SecretManagerProvider; +import org.gcube.common.security.secrets.AccessTokenSecret; +import org.gcube.common.security.secrets.Secret; +import org.gcube.smartgears.ContextProvider; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Luca Frosini (ISTI - CNR) + * + */ +public abstract class ServiceContextTest { + + private static final Logger logger = LoggerFactory.getLogger(ServiceContextTest.class); + + protected static final String CONFIG_INI_FILENAME = "config.ini"; + + public static final String DEFAULT_TEST_SCOPE; + public static Secret current_secret; + + // public static final String GCUBE; + // public static final String DEVNEXT; + // public static final String NEXTNEXT; + // public static final String DEVSEC; + // public static final String DEVVRE; + + // public static final String ROOT; + // public static final String VO; + public static final String VRE; + + // private static final String ROOT_PRE; + // private static final String VO_PREPROD; + // protected static final String VRE_GRSF_PRE; + + // private static final String ROOT_PROD; + + protected static final Properties properties; + + public static final String TYPE_PROPERTY_KEY = "type"; + public static final String USERNAME_PROPERTY_KEY = "username"; + public static final String PASSWORD_PROPERTY_KEY = "password"; + public static final String CLIENT_ID_PROPERTY_KEY = "clientId"; + + static { + + try { + + // GCUBE = "/gcube"; + // DEVNEXT = GCUBE + "/devNext"; + // NEXTNEXT = DEVNEXT + "/NextNext"; + // DEVSEC = GCUBE + "/devsec"; + // DEVVRE = DEVSEC + "/devVRE"; + + // ROOT = GCUBE; + // VO = DEVSEC; + // VRE = DEVVRE; + + // ROOT_PRE = "/pred4s"; + // VO_PREPROD = ROOT_PRE + "/preprod"; + // VRE_GRSF_PRE = VO_PREPROD + "/GRSF_Pre"; + + // ROOT_PROD = "/d4science.research-infrastructures.eu"; + + properties = readProperties(CONFIG_INI_FILENAME); + VRE = properties.getProperty("context"); + DEFAULT_TEST_SCOPE = VRE; + + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static Properties readProperties(String filename) throws IOException { + + Properties p = new Properties(); + InputStream input = ServiceContextTest.class.getClassLoader().getResourceAsStream(CONFIG_INI_FILENAME); + // load the properties file + p.load(input); + return p; + + } + + private enum Type { + USER, CLIENT_ID + }; + + public static void set(Secret secret) throws Exception { + SecretManagerProvider.reset(); + SecretManagerProvider.set(secret); + current_secret = secret; + ContextProvider.set(null); + } + + public static void setContextByName(String fullContextName) throws Exception { + logger.debug("Going to set credentials for context {}", fullContextName); + Secret secret = getSecretByContextName(fullContextName); + set(secret); + } + + private static TokenResponse getJWTAccessToken(String context) throws Exception { + Type type = Type.valueOf(properties.get(TYPE_PROPERTY_KEY).toString()); + + TokenResponse tr = null; + + int index = context.indexOf('/', 1); + String root = context.substring(0, index == -1 ? context.length() : index); + + switch (type) { + case CLIENT_ID: + String clientId = properties.getProperty(CLIENT_ID_PROPERTY_KEY); + String clientSecret = properties.getProperty(root); + + tr = KeycloakClientFactory.newInstance().queryUMAToken(context, clientId, clientSecret, context, null); + break; + + case USER: + default: + String username = properties.getProperty(USERNAME_PROPERTY_KEY); + String password = properties.getProperty(PASSWORD_PROPERTY_KEY); + + switch (root) { + case "/gcube": + default: + clientId = "next.d4science.org"; + break; + + case "/pred4s": + clientId = "pre.d4science.org"; + break; + + case "/d4science.research-infrastructures.eu": + clientId = "services.d4science.org"; + break; + } + clientSecret = null; + + tr = KeycloakClientHelper.getTokenForUser(context, username, password); + break; + + } + + return tr; + + } + + public static Secret getSecretByContextName(String context) throws Exception { + TokenResponse tr = getJWTAccessToken(context); + Secret secret = new AccessTokenSecret(tr.getAccessToken(), context); + return secret; + } + + public static void setContext(String token) throws Exception { + Secret secret = getSecret(token); + set(secret); + } + + private static Secret getSecret(String token) throws Exception { + // TODO: verificare classe (AccessTokenSecret anziche JWTToken) e context(VRE) + Secret secret = new AccessTokenSecret(token, VRE); + return secret; + } + + public static String getUser() { + String user = "UNKNOWN"; + try { + user = SecretManagerProvider.get().getOwner().getId(); + } catch (Exception e) { + logger.error("Unable to retrieve user. {} will be used", user); + } + return user; + } + + @BeforeClass + public static void beforeClass() throws Exception { + setContextByName(DEFAULT_TEST_SCOPE); + } + + @AfterClass + public static void afterClass() throws Exception { + SecretManagerProvider.reset(); + } + +} diff --git a/src/test/resources/config.ini b/src/test/resources/config.ini new file mode 100644 index 0000000..d902749 --- /dev/null +++ b/src/test/resources/config.ini @@ -0,0 +1,5 @@ +context=/gcube +type=CLIENT_ID +clientId=id.d4science.org +/gcube=09c26f24-3c65-4039-9fa0-e5cc4f4032cd +root \ No newline at end of file diff --git a/src/test/resources/token_user.properties b/src/test/resources/token_user.properties new file mode 100644 index 0000000..5d33796 --- /dev/null +++ b/src/test/resources/token_user.properties @@ -0,0 +1,14 @@ +type=USER +#type=CLIENT_ID +username=alfredo.oliviero +context=/gcube/devsec/devVRE +/gcube/devsec/devVRE=eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJSSklZNEpoNF9qdDdvNmREY0NlUDFfS1l0akcxVExXVW9oMkQ2Tzk1bFNBIn0.eyJleHAiOjE3MTQ5ODkwMzMsImlhdCI6MTcxNDk4NzIzMywianRpIjoiMDA2OTBhZjAtYTQzZC00NjcwLThkNTQtOTc3OTMyMjAxYzQxIiwiaXNzIjoiaHR0cHM6Ly9hY2NvdW50cy5kZXYuZDRzY2llbmNlLm9yZy9hdXRoL3JlYWxtcy9kNHNjaWVuY2UiLCJhdWQiOiIlMkZnY3ViZSUyRmRldnNlYyUyRmRldlZSRSIsInN1YiI6ImI5OTY5YjUxLTU3OGYtNGI2OS1hNTNmLTJjOGFkZjllZmNjNyIsInR5cCI6IkJlYXJlciIsImF6cCI6ImlkLmQ0c2NpZW5jZS5vcmciLCJzZXNzaW9uX3N0YXRlIjoiZmZjZjQ5ZDUtNTFkZS00NGM2LTk4MWUtYzk4ZmMxMGJmMDRkIiwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbImRlZmF1bHQtcm9sZXMtZDRzY2llbmNlIiwib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7IiUyRmdjdWJlJTJGZGV2c2VjJTJGZGV2VlJFIjp7InJvbGVzIjpbIkNhdGFsb2d1ZS1FZGl0b3IiLCJNZW1iZXIiXX19LCJzY29wZSI6ImVtYWlsIHByb2ZpbGUiLCJzaWQiOiJmZmNmNDlkNS01MWRlLTQ0YzYtOTgxZS1jOThmYzEwYmYwNGQiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiZ2VuZGVyIjoiTWFsZSIsIm5hbWUiOiJBbGZyZWRvIE9saXZpZXJvIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiYWxmcmVkby5vbGl2aWVybyIsImdpdmVuX25hbWUiOiJBbGZyZWRvIiwiZmFtaWx5X25hbWUiOiJPbGl2aWVybyIsImVtYWlsIjoiYWxmcmVkby5vbGl2aWVyb0Bpc3RpLmNuci5pdCJ9.J-fM7Wkx-qBG_eBVW_j7EHILJOtcKlqD39R-uTU3660KEuueu85PX_29kArTgoZpgcLoxs2nUrEtEMnHjMS2kNRDIvcbCC3ZNMSggCHp8cyNIigYNwhIJdB1OGZjZdi3qxeRwV7xCRAG1qrgmUirMZN-4My-1dUfPjgGxpXw7AroQQ-sMRXGYzO5nVYml4q_61yWPoGHzyEnElG6FOsIl0owhEALOAPwdqRBg03hXUXm6z4kfhb6KzFNzQyTFcE8M3w_WC1Oy033GDAEfZtPrs1XAbAsTschqOc2wyrluaXeG3NLKv5gBByoY1k9QwYmb7wEWTvYBpIMWaxlSpMV0A +#username=grsf.publisher +#password= +#clientId=grsf-publisher +#/gcube= +#/pred4s= +#/d4science.research-infrastructures.eu= + +password= +