commit bffb8f3d98fc8663fe532ea194b65b31c11ce84d Author: francesco.mangiacrapa Date: Mon Jun 1 16:00:23 2020 +0200 initial commit diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..ae19729 --- /dev/null +++ b/.classpath @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.project b/.project new file mode 100644 index 0000000..93a6d99 --- /dev/null +++ b/.project @@ -0,0 +1,23 @@ + + + catalogue-util-library + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..b9a1035 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,15 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=1.7 diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 0000000..f897a7f --- /dev/null +++ b/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..668791f --- /dev/null +++ b/pom.xml @@ -0,0 +1,228 @@ + + 4.0.0 + + + maven-parent + org.gcube.tools + 1.1.0 + + + + org.gcube.datacatalogue + catalogue-util-library + 0.1.0-SNAPSHOT + + CKan utility library + + Utility library to retrieve users information, organizations information and so on from the ckan d4science datacatalogue + + + + scm:git:https://code-repo.d4science.org/gCubeSystem/${project.artifactId}.git + scm:git:https://code-repo.d4science.org/gCubeSystem/${project.artifactId}.git + https://code-repo.d4science.org/gCubeSystem/${project.artifactId} + + + + data-catalogue + 0.4.2 + 9.4.1208.jre7 + UTF-8 + UTF-8 + 2.7.0 + + + + + + org.gcube.distribution + maven-portal-bom + LATEST + pom + import + + + + + + + com.google.gwt + gwt-user + provided + + + + + eu.trentorise.opendata + jackan + ${jackanVersion} + compile + + + + org.gcube.data-publishing + gcat-client + [1.0.0, 2.0.0-SNAPSHOT) + compile + + + + + + + + + + + junit + junit + 4.8 + test + + + org.gcube.common.portal + portal-manager + provided + + + org.gcube.resources.discovery + ic-client + provided + + + org.gcube.core + common-scope-maps + provided + + + + org.gcube.core + common-encryption + provided + + + org.gcube.dvos + usermanagement-core + provided + + + net.htmlparser.jericho + jericho-html + 3.3 + compile + + + com.google.guava + guava + + + com.googlecode.json-simple + json-simple + compile + + + org.apache.solr + solr-solrj + [4.10.2,5.0.0) + + + org.slf4j + slf4j-log4j12 + provided + + + org.slf4j + slf4j-api + provided + + + + + + + src/main/java + + **/*.* + + + + + + maven-compiler-plugin + + + + org.apache.maven.plugins + maven-jar-plugin + 2.2 + + + + test-jar + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.12 + + true + + + + org.apache.maven.plugins + maven-resources-plugin + 2.5 + + + copy-profile + install + + copy-resources + + + target + + + ${distroDirectory} + true + + profile.xml + + + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + ${distroDirectory}/descriptor.xml + + + + fully.qualified.MainClass + + + + + + servicearchive + install + + single + + + + + + + diff --git a/src/main/java/org/gcube/datacatalogue/ckanutillibrary/CkanUtilLibrary.gwt.xml b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/CkanUtilLibrary.gwt.xml new file mode 100644 index 0000000..a3990ca --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/CkanUtilLibrary.gwt.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/main/java/org/gcube/datacatalogue/ckanutillibrary/ckan/ExtendCkanClient.java b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/ckan/ExtendCkanClient.java new file mode 100644 index 0000000..43a2fce --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/ckan/ExtendCkanClient.java @@ -0,0 +1,240 @@ +/** + * + */ +package org.gcube.datacatalogue.ckanutillibrary.ckan; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URLEncoder; +import java.util.Arrays; + +import javax.annotation.Nullable; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Charsets; +import com.google.common.io.CharStreams; + +import eu.trentorise.opendata.jackan.CheckedCkanClient; +import eu.trentorise.opendata.jackan.exceptions.CkanException; +import eu.trentorise.opendata.jackan.exceptions.JackanException; +import eu.trentorise.opendata.jackan.internal.org.apache.http.client.fluent.Request; +import eu.trentorise.opendata.jackan.internal.org.apache.http.client.fluent.Response; +import eu.trentorise.opendata.jackan.model.CkanGroup; +import eu.trentorise.opendata.jackan.model.CkanOrganization; +import eu.trentorise.opendata.jackan.model.CkanResponse; + + +/** + * The Class ExtendCkanClient. + * + * @author Francesco Mangiacrapa at ISTI-CNR (francesco.mangiacrapa@isti.cnr.it) + * + * Jun 14, 2019 + */ +public class ExtendCkanClient extends CheckedCkanClient implements PatchedCkan{ + + private static final Logger logger = LoggerFactory.getLogger(ExtendCkanClient.class); + + @Nullable + private static ObjectMapper objectMapper; + + private String catalogueURL; + private String ckanToken; + private int extendTimeout = 120000; //in milliseconds + + + /** + * Instantiates a new extend ckan client. + * + * @param catalogueURL the catalogue url + */ + public ExtendCkanClient(String catalogueURL) { + super(catalogueURL); + this.catalogueURL = catalogueURL; + + } + + /** + * Instantiates a new extend ckan client. + * + * @param catalogueURL the catalogue url + * @param ckanToken the ckan token + */ + public ExtendCkanClient(String catalogueURL, @Nullable String ckanToken) { + super(catalogueURL, ckanToken); + this.catalogueURL = catalogueURL; + this.ckanToken = ckanToken; + } + + /** + * Instantiates a new extend ckan client. + * + * @param catalogueURL the catalogue url + * @param ckanToken the ckan token + * @param timeout the timeout + */ + public ExtendCkanClient(String catalogueURL, @Nullable String ckanToken, int timeout) { + super(catalogueURL, ckanToken); + this.catalogueURL = catalogueURL; + this.ckanToken = ckanToken; + this.extendTimeout = timeout; + } + + + /** + * Configures the request. Should work both for GETs and POSTs. + * + * @param request the request + * @return the request + */ + @Override + protected Request configureRequest(Request request) { + request = super.configureRequest(request); + + logger.debug("Setting timeout to {}", extendTimeout); + + request.socketTimeout(this.extendTimeout) + .connectTimeout(this.extendTimeout); + + return request; + } + + /** + * Gets the catalogue url. + * + * @return the catalogueURL + */ + public String getCatalogueURL() { + + return catalogueURL; + } + + + /** + * Gets the ckan token. + * + * @return the ckanToken + */ + @Override + public String getCkanToken() { + + return ckanToken; + } + + /** + * Gets the timeout. + * + * @return the timeout + */ + public int getTimeout() { + return extendTimeout; + } + + /* (non-Javadoc) + * @see eu.trentorise.opendata.jackan.CkanClient#getOrganization(java.lang.String) + */ + public synchronized CkanOrganization getOrganization(String idOrName) { + checkNotNull(idOrName, "Need a valid id or name!"); + logger.info("Patched read organization for id/name: {}", idOrName); + return getHttp(OrganizationResponse.class, "/api/3/action/organization_show", "id", idOrName, + "include_datasets", "false", "include_users", "true").result; + + } + + + /* (non-Javadoc) + * @see eu.trentorise.opendata.jackan.CkanClient#getGroup(java.lang.String) + */ + public synchronized CkanGroup getGroup(String idOrName) { + checkNotNull(idOrName, "Need a valid id or name!"); + logger.info("Patched read group for id/name: {}", idOrName); + return getHttp(GroupResponse.class, "/api/3/action/group_show", "id", idOrName, "include_datasets", + "false", "include_users", "true").result; + } + + + /** + * Retrieves the Jackson object mapper for reading operations. Internally, + * Object mapper is initialized at first call. + * + * @return the object mapper + */ + static ObjectMapper getObjectMapper() { + if (objectMapper == null) { + objectMapper = new ObjectMapper(); + configureObjectMapper(objectMapper); + } + return objectMapper; + } + + /* (non-Javadoc) + * @see org.gcube.datacatalogue.ckanutillibrary.server.patch.PatchedCkan#getHttp(java.lang.Class, java.lang.String, java.lang.Object[]) + */ + public T getHttp(Class responseType, String path, Object... params) { + + checkNotNull(responseType); + checkNotNull(path); + String fullUrl = calcFullUrl(path, params); + T ckanResponse; + String returnedText; + + try { + + logger.debug("getting {}", fullUrl); + Request request = Request.Get(fullUrl); + configureRequest(request); + Response response = request.execute(); + InputStream stream = response.returnResponse() + .getEntity() + .getContent(); + + try (InputStreamReader reader = new InputStreamReader(stream, Charsets.UTF_8)) { + returnedText = CharStreams.toString(reader); + } + + logger.trace("returnedText {}", returnedText); + } catch (Exception ex) { + throw new CkanException("Error while performing GET. Request url was: " + fullUrl, this, ex); + } + try { + ckanResponse = getObjectMapper().readValue(returnedText, responseType); + } catch (Exception ex) { + throw new CkanException( + "Couldn't interpret json returned by the server! Returned text was: " + returnedText, this, ex); + } + + if (!ckanResponse.isSuccess()) { + throwCkanException("Error while performing GET. Request url was: " + fullUrl, ckanResponse); + } + return ckanResponse; + } + + + /* (non-Javadoc) + * @see org.gcube.datacatalogue.ckanutillibrary.server.patch.PatchedCkan#calcFullUrl(java.lang.String, java.lang.Object[]) + */ + public String calcFullUrl(String path, Object[] params) { + checkNotNull(path); + + try { + StringBuilder sb = new StringBuilder().append(catalogueURL) + .append(path); + for (int i = 0; i < params.length; i += 2) { + sb.append(i == 0 ? "?" : "&") + .append(URLEncoder.encode(params[i].toString(), "UTF-8")) + .append("=") + .append(URLEncoder.encode(params[i + 1].toString(), "UTF-8")); + } + return sb.toString(); + } catch (Exception ex) { + throw new JackanException("Error while building url to perform GET! \n path: " + path + " \n params: " + + Arrays.toString(params), ex); + } + } + +} diff --git a/src/main/java/org/gcube/datacatalogue/ckanutillibrary/ckan/PatchedCkan.java b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/ckan/PatchedCkan.java new file mode 100644 index 0000000..b7501fd --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/ckan/PatchedCkan.java @@ -0,0 +1,113 @@ +package org.gcube.datacatalogue.ckanutillibrary.ckan; + +import eu.trentorise.opendata.jackan.CkanClient; +import eu.trentorise.opendata.jackan.exceptions.CkanException; +import eu.trentorise.opendata.jackan.exceptions.JackanException; +import eu.trentorise.opendata.jackan.model.CkanGroup; +import eu.trentorise.opendata.jackan.model.CkanOrganization; +import eu.trentorise.opendata.jackan.model.CkanResponse; + +/** + * The Interface PatchedCkan. + * + * @author Francesco Mangiacrapa at ISTI-CNR (francesco.mangiacrapa@isti.cnr.it) + * + * Jun 14, 2019 + */ +public interface PatchedCkan { + + /** + * + * This Class is private in {@link CkanClient} so I need to rewrite it + * + * The Class OrganizationResponse. + * + * @author Francesco Mangiacrapa at ISTI-CNR (francesco.mangiacrapa@isti.cnr.it) + * + * Jun 7, 2019 + */ + static class OrganizationResponse extends CkanResponse { + + public CkanOrganization result; + + } + + /** + * + * + * This Class is private in {@link CkanClient} so I need to rewrite it + * + * + * The Class GroupResponse. + * + * @author Francesco Mangiacrapa at ISTI-CNR (francesco.mangiacrapa@isti.cnr.it) + * + * Jun 7, 2019 + */ + static class GroupResponse extends CkanResponse { + + public CkanGroup result; + + /** + * Instantiates a new group response. + */ + public GroupResponse() { + + } + } + + /** + * + * This method is private in {@link CkanClient} so I need to rewrite it + * + * + * + * Performs HTTP GET on server. If {@link CkanResponse#isSuccess()} is false + * throws {@link CkanException}. + * + * @param the generic type + * @param responseType a descendant of CkanResponse + * @param path something like /api/3/package_show + * @param params list of key, value parameters. They must be not be url + * encoded. i.e. "id","laghi-monitorati-trento" + * @return the http + * @throws CkanException on error + */ + public T getHttp(Class responseType, String path, Object... params); + + /** + * + * This method is private in {@link CkanClient} so I need to rewrite it + * + * + * Calculates a full url out of the provided params. + * + * @param path something like /api/3/package_show + * @param params list of key, value parameters. They must be not be url encoded. + * i.e. "id","laghi-monitorati-trento" + * @return the full url to be called. + * @throws JackanException if there is any error building the url + */ + public String calcFullUrl(String path, Object[] params); + + + + // WE NEED TO OVERRIDE THE FOLLOWING METHODS + + /** + * Gets the organization. + * + * @param idOrName the id or name + * @return the organization + */ + public CkanOrganization getOrganization(String idOrName); + + /** + * Gets the group. + * + * @param idOrName the id or name + * @return the group + */ + public CkanGroup getGroup(String idOrName); + +} diff --git a/src/main/java/org/gcube/datacatalogue/ckanutillibrary/ckan/SimpleExtendCkanClient.java b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/ckan/SimpleExtendCkanClient.java new file mode 100644 index 0000000..93c59cd --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/ckan/SimpleExtendCkanClient.java @@ -0,0 +1,168 @@ +package org.gcube.datacatalogue.ckanutillibrary.ckan; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URLEncoder; +import java.util.Arrays; + +import javax.annotation.Nullable; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Charsets; +import com.google.common.io.CharStreams; + +import eu.trentorise.opendata.jackan.CkanClient; +import eu.trentorise.opendata.jackan.exceptions.CkanException; +import eu.trentorise.opendata.jackan.exceptions.JackanException; +import eu.trentorise.opendata.jackan.internal.org.apache.http.client.fluent.Request; +import eu.trentorise.opendata.jackan.internal.org.apache.http.client.fluent.Response; +import eu.trentorise.opendata.jackan.model.CkanGroup; +import eu.trentorise.opendata.jackan.model.CkanOrganization; +import eu.trentorise.opendata.jackan.model.CkanResponse; + + +/** + * The Class SimpleExtendCkanClient. + * + * @author Francesco Mangiacrapa at ISTI-CNR (francesco.mangiacrapa@isti.cnr.it) + * + * Jun 7, 2019 + */ +public class SimpleExtendCkanClient extends CkanClient implements PatchedCkan{ + + + private String catalogueURL; + + @Nullable + private static ObjectMapper objectMapper; + + private static final Logger logger = LoggerFactory.getLogger(SimpleExtendCkanClient.class); + + /** + * Instantiates a new simple extend ckan client. + * + * @param catalogueURL the catalogue URL + */ + public SimpleExtendCkanClient(String catalogueURL) { + super(catalogueURL); + this.catalogueURL = catalogueURL; + } + + /* (non-Javadoc) + * @see eu.trentorise.opendata.jackan.CkanClient#getOrganization(java.lang.String) + */ + public synchronized CkanOrganization getOrganization(String idOrName) { + checkNotNull(idOrName, "Need a valid id or name!"); + logger.info("Patched read organization for id/name: {}", idOrName); + return getHttp(OrganizationResponse.class, "/api/3/action/organization_show", "id", idOrName, + "include_datasets", "false", "include_users", "true").result; + + } + + + /* (non-Javadoc) + * @see eu.trentorise.opendata.jackan.CkanClient#getGroup(java.lang.String) + */ + public synchronized CkanGroup getGroup(String idOrName) { + checkNotNull(idOrName, "Need a valid id or name!"); + logger.info("Patched read group for id/name: {}", idOrName); + return getHttp(GroupResponse.class, "/api/3/action/group_show", "id", idOrName, "include_datasets", + "false", "include_users", "true").result; + } + + + /** + * Retrieves the Jackson object mapper for reading operations. Internally, + * Object mapper is initialized at first call. + * + * @return the object mapper + */ + public static ObjectMapper getObjectMapper() { + if (objectMapper == null) { + objectMapper = new ObjectMapper(); + configureObjectMapper(objectMapper); + } + return objectMapper; + } + + /* (non-Javadoc) + * @see org.gcube.datacatalogue.ckanutillibrary.server.patch.PatchedCkan#getHttp(java.lang.Class, java.lang.String, java.lang.Object[]) + */ + public T getHttp(Class responseType, String path, Object... params) { + + checkNotNull(responseType); + checkNotNull(path); + String fullUrl = calcFullUrl(path, params); + T ckanResponse; + String returnedText; + + try { + + logger.debug("getting {}", fullUrl); + Request request = Request.Get(fullUrl); + configureRequest(request); + Response response = request.execute(); + InputStream stream = response.returnResponse() + .getEntity() + .getContent(); + + try (InputStreamReader reader = new InputStreamReader(stream, Charsets.UTF_8)) { + returnedText = CharStreams.toString(reader); + } + + logger.trace("returnedText {}", returnedText); + } catch (Exception ex) { + throw new CkanException("Error while performing GET. Request url was: " + fullUrl, this, ex); + } + try { + ckanResponse = getObjectMapper().readValue(returnedText, responseType); + } catch (Exception ex) { + throw new CkanException( + "Couldn't interpret json returned by the server! Returned text was: " + returnedText, this, ex); + } + + if (!ckanResponse.isSuccess()) { + throwCkanException("Error while performing GET. Request url was: " + fullUrl, ckanResponse); + } + return ckanResponse; + } + + + /* (non-Javadoc) + * @see org.gcube.datacatalogue.ckanutillibrary.server.patch.PatchedCkan#calcFullUrl(java.lang.String, java.lang.Object[]) + */ + public String calcFullUrl(String path, Object[] params) { + checkNotNull(path); + + try { + StringBuilder sb = new StringBuilder().append(catalogueURL) + .append(path); + for (int i = 0; i < params.length; i += 2) { + sb.append(i == 0 ? "?" : "&") + .append(URLEncoder.encode(params[i].toString(), "UTF-8")) + .append("=") + .append(URLEncoder.encode(params[i + 1].toString(), "UTF-8")); + } + return sb.toString(); + } catch (Exception ex) { + throw new JackanException("Error while building url to perform GET! \n path: " + path + " \n params: " + + Arrays.toString(params), ex); + } + } + + /** + * Gets the catalogue url. + * + * @return the catalogueURL + */ + public String getCatalogueURL() { + + return catalogueURL; + } + +} diff --git a/src/main/java/org/gcube/datacatalogue/ckanutillibrary/db/DBCaller.java b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/db/DBCaller.java new file mode 100644 index 0000000..7be49c9 --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/db/DBCaller.java @@ -0,0 +1,209 @@ +package org.gcube.datacatalogue.ckanutillibrary.db; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; + +import org.gcube.datacatalogue.ckanutillibrary.shared.RolesCkanGroupOrOrg; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DBCaller { + + private static final Logger LOG = LoggerFactory.getLogger(DBCaller.class); + + private String CKAN_DB_URL; + private Integer CKAN_DB_PORT; + private String CKAN_DB_NAME; + private String CKAN_DB_USER; + private String CKAN_DB_PASSWORD; + + public DBCaller() { + } + + public DBCaller(String cKAN_DB_URL, Integer cKAN_DB_PORT, String cKAN_DB_NAME, String cKAN_DB_USER, + String cKAN_DB_PASSWORD) { + CKAN_DB_URL = cKAN_DB_URL; + CKAN_DB_PORT = cKAN_DB_PORT; + CKAN_DB_NAME = cKAN_DB_NAME; + CKAN_DB_USER = cKAN_DB_USER; + CKAN_DB_PASSWORD = cKAN_DB_PASSWORD; + } + + + /** + * Retrieve connection from the pool + * @return a connection available within the pool + * @throws SQLException + * @throws ClassNotFoundException + */ + private Connection getConnection() throws SQLException, ClassNotFoundException{ + + LOG.debug("CONNECTION REQUEST"); + // create db connection + Class.forName("org.postgresql.Driver"); + + String dbBaseConnectionURL = String.format("jdbc:postgresql://%s", CKAN_DB_URL); + + if(CKAN_DB_PORT!=null) + dbBaseConnectionURL+=":" + CKAN_DB_PORT; + + dbBaseConnectionURL+="/" + CKAN_DB_NAME; + + Connection connection = DriverManager.getConnection(dbBaseConnectionURL, CKAN_DB_USER, CKAN_DB_PASSWORD); + +// Connection connection = DriverManager.getConnection( +// "jdbc:postgresql://" + CKAN_DB_URL + ":" + CKAN_DB_PORT + "/" + CKAN_DB_NAME, CKAN_DB_USER, CKAN_DB_PASSWORD); + return connection; + + } + + /** + * Retrieve the map (groups id, capacity) with the groups to which the user belongs by querying directly the database. + * @param username + * @return + * @throws SQLException + * @throws ClassNotFoundException + */ + public Map getGroupsByUserFromDB(String userId){ + checkNotNull(userId); + + //couples (groups id, capacity) of the user in the group + + Map toReturn = new HashMap(); + Connection connection = null; + try{ + + connection = getConnection(); + ResultSet rs; + + String joinQuery = "SELECT \"group_id\",\"capacity\" FROM \"public\".\"member\" " + + "JOIN \"public\".\"group\" ON \"member\".\"group_id\" = \"group\".\"id\" where \"table_id\"=?" + + " and \"table_name\"='user' and \"member\".\"state\"='active' and \"group\".\"state\"='active' and \"group\".\"is_organization\"=?;"; + + + PreparedStatement preparedStatement = connection.prepareStatement(joinQuery); + preparedStatement.setString(1, userId); + preparedStatement.setBoolean(2, false); + rs = preparedStatement.executeQuery(); + + while (rs.next()) { + + toReturn.put(rs.getString("group_id"), RolesCkanGroupOrOrg.convertFromCapacity(rs.getString("capacity"))); + } + + }catch(Exception e){ + LOG.error("Failed to retrieve the groups to whom the user belongs. Error is " + e.getMessage()); + return null; + }finally{ + closeConnection(connection); + } + return toReturn; + } + + /** + * Retrieve the map (organisation id, capacity) with the organisations to which the user belongs by querying directly the database. + * @param username + * @return + * @throws SQLException + * @throws ClassNotFoundException + */ + public Map getOrganizationsByUserFromDB(String userId){ + checkNotNull(userId); + + Map toReturn = new HashMap(); + Connection connection = null; + try{ + + connection = getConnection(); + ResultSet rs; + + String joinQuery = "SELECT \"group_id\",\"capacity\" FROM \"public\".\"member\" " + + "JOIN \"public\".\"group\" ON \"member\".\"group_id\" = \"group\".\"id\" where \"table_id\"=?" + + " and \"table_name\"='user' and \"member\".\"state\"='active' and \"group\".\"state\"='active' and \"group\".\"is_organization\"=?;"; + + + PreparedStatement preparedStatement = connection.prepareStatement(joinQuery); + preparedStatement.setString(1, userId); + preparedStatement.setBoolean(2, true); + rs = preparedStatement.executeQuery(); + + while (rs.next()) { + + toReturn.put(rs.getString("group_id"), RolesCkanGroupOrOrg.convertFromCapacity(rs.getString("capacity"))); + } + + }catch(Exception e){ + LOG.error("Failed to retrieve the groups to whom the user belongs. Error is " + e.getMessage()); + return null; + }finally{ + closeConnection(connection); + } + return toReturn; + } + + + public String getApiKeyFromUsername(String username, String state) { + + LOG.debug("Request api key for user = " + username); + + // checks + checkNotNull(username); + checkArgument(!username.isEmpty()); + + // the connection + Connection connection = null; + String apiToReturn = null; + + try{ + + connection = getConnection(); + + String query = "SELECT \"apikey\" FROM \"user\" WHERE \"name\"=? and \"state\"=?;"; + PreparedStatement preparedStatement = connection.prepareStatement(query); + preparedStatement.setString(1, username); + preparedStatement.setString(2, state); + + ResultSet rs = preparedStatement.executeQuery(); + while (rs.next()) { + apiToReturn = rs.getString("apikey"); + LOG.debug("Api key "+apiToReturn.substring(0,10)+" MASKED-TOKEN retrieved for user " + username); + break; + } + + return apiToReturn; + + }catch(Exception e){ + LOG.error("Unable to retrieve key for user " + username, e); + }finally{ + closeConnection(connection); + } + + return null; + } + + + /** + * Tries to close a connection + * @param connection + */ + private void closeConnection(Connection connection){ + + if(connection != null){ + try{ + connection.close(); + }catch(Exception e){ + LOG.error("Unable to close this connection ", e); + } + } + + } + +} diff --git a/src/main/java/org/gcube/datacatalogue/ckanutillibrary/gcat/GCatCaller.java b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/gcat/GCatCaller.java new file mode 100644 index 0000000..c14eca7 --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/gcat/GCatCaller.java @@ -0,0 +1,69 @@ +package org.gcube.datacatalogue.ckanutillibrary.gcat; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.util.List; + +import org.gcube.datacatalogue.ckanutillibrary.ckan.SimpleExtendCkanClient; +import org.gcube.gcat.client.Item; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.JsonMappingException; + +import eu.trentorise.opendata.jackan.model.CkanDataset; +import eu.trentorise.opendata.jackan.model.CkanLicense; + +// TODO: Auto-generated Javadoc +/** + * The Class GCatCaller. + * + * @author Francesco Mangiacrapa at ISTI-CNR Pisa (Italy) + * May 29, 2020 + */ +public class GCatCaller { + + private SimpleExtendCkanClient jackanCkanClient; + + private String catalogueURL; + + private static final Logger LOG = LoggerFactory.getLogger(GCatCaller.class); + + /** + * Instantiates a new g cat caller. + */ + public GCatCaller(String catalogueURL) { + this.catalogueURL = catalogueURL; + this.jackanCkanClient = new SimpleExtendCkanClient(catalogueURL); + } + + + /** + * Gets the dataset for name. + * + * @param datasetIdOrName the dataset id or name + * @return the dataset for name + * @throws JsonParseException the json parse exception + * @throws JsonMappingException the json mapping exception + * @throws IOException Signals that an I/O exception has occurred. + */ + public CkanDataset getDatasetForName(String datasetIdOrName) throws JsonParseException, JsonMappingException, IOException { + LOG.debug("Get dataset for name "+datasetIdOrName+ "called"); + String json = new Item().read(datasetIdOrName); + return SimpleExtendCkanClient.getObjectMapper().readValue(json, CkanDataset.class); + } + + + /** + * TODO gCAT missing + * @return + * @throws MalformedURLException + */ + + public List getLicenseList() { + LOG.debug("Get linces list called"); + return jackanCkanClient.getLicenseList(); + } + +} diff --git a/src/main/java/org/gcube/datacatalogue/ckanutillibrary/server/ApplicationProfileScopePerUrlReader.java b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/server/ApplicationProfileScopePerUrlReader.java new file mode 100644 index 0000000..e0d621e --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/server/ApplicationProfileScopePerUrlReader.java @@ -0,0 +1,105 @@ +package org.gcube.datacatalogue.ckanutillibrary.server; + +import static org.gcube.resources.discovery.icclient.ICFactory.client; + +import java.io.StringReader; +import java.util.List; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.gcube.common.portal.PortalContext; +import org.gcube.common.resources.gcore.utils.XPathHelper; +import org.gcube.common.scope.api.ScopeProvider; +import org.gcube.datacatalogue.ckanutillibrary.shared.ex.ApplicationProfileNotFoundException; +import org.gcube.resources.discovery.client.api.DiscoveryClient; +import org.gcube.resources.discovery.client.queries.api.Query; +import org.gcube.resources.discovery.client.queries.impl.QueryBox; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Node; +import org.xml.sax.InputSource; + +/** + * This class has a method that tries to read the application profile whose structure contains + * couples Scope/Current_Url to check where the the needed information needs to be discovered according to the current + * portlet url. It means that the scope in which the ckan related information will be discovered could be different wrt the running one. + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class ApplicationProfileScopePerUrlReader { + + private static final Logger logger = LoggerFactory.getLogger(ApplicationProfileScopePerUrlReader.class); + private final static String APPLICATION_PROFILE_NAME = "DataCatalogueMapScopesUrls"; + private final static String QUERY = "for $profile in collection('/db/Profiles/GenericResource')//Resource " + + "where $profile/Profile/SecondaryType/string() eq 'ApplicationProfile' and $profile/Profile/Name/string() " + + " eq '" + APPLICATION_PROFILE_NAME + "'" + + "return $profile"; + + /** + * Get the scope in which discover for this url. If the Application Profile doesn't contain it, the current scope (taken + * from ScopeProvider is returned). + * @param url + * @return the scope to be used for the given url + */ + public static String getScopePerUrl(String url){ + + logger.info("Request scope for ckan portlet at url " + url); + + String scope = ScopeProvider.instance.get(); + String scopeToReturn = scope; + String rootScopeForInfrastructure = "/" + PortalContext.getConfiguration().getInfrastructureName(); + + if(url == null || url.isEmpty()){ + + logger.info("The url passed is null or empty! Returning current scope [" + scope + "]"); + return scope; + + } + + // set this scope + ScopeProvider.instance.set(rootScopeForInfrastructure); + + logger.debug("Trying to fetch applicationProfile profile from the infrastructure for " + APPLICATION_PROFILE_NAME + " scope: " + rootScopeForInfrastructure); + + try { + Query q = new QueryBox(QUERY); + + DiscoveryClient client = client(); + List appProfile = client.submit(q); + + if (appProfile == null || appProfile.size() == 0) + throw new ApplicationProfileNotFoundException("Your applicationProfile is not registered in the infrastructure"); + else{ + String elem = appProfile.get(0); + DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + Node node = docBuilder.parse(new InputSource(new StringReader(elem))).getDocumentElement(); + XPathHelper helper = new XPathHelper(node); + + List urls = helper.evaluate("/Resource/Profile/Body/EndPoint/URL/text()"); + if (urls != null && urls.size() > 0) { + boolean foundScope = false; + for (int i = 0; i < urls.size(); i++) { + if (urls.get(i).trim().compareTo(url) == 0) { // url found + scopeToReturn = helper.evaluate("/Resource/Profile/Body/EndPoint/Scope/text()").get(i); + logger.debug("Found, returning " + scopeToReturn); + foundScope = true; + break; + } + } + if (!foundScope || scopeToReturn == null || scopeToReturn.isEmpty()){ + logger.debug("Scope is missing for url " + url + ". Returning " + scope); + } + } + else + throw + new ApplicationProfileNotFoundException("Your applicationProfile EndPoint was not found in the profile, consider adding element in "); + } + } catch (Exception e) { + logger.error("Error while trying to fetch applicationProfile profile from the infrastructure", e); + }finally{ + ScopeProvider.instance.set(scope); + } + + return scopeToReturn; + } +} diff --git a/src/main/java/org/gcube/datacatalogue/ckanutillibrary/server/CKANTokenBean.java b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/server/CKANTokenBean.java new file mode 100644 index 0000000..9cd9ba5 --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/server/CKANTokenBean.java @@ -0,0 +1,44 @@ +package org.gcube.datacatalogue.ckanutillibrary.server; + + +// TODO: Auto-generated Javadoc +/** + * The Class CKANTokenBean. + * + * @author Francesco Mangiacrapa at ISTI-CNR Pisa (Italy) + * Jun 1, 2020 + */ +public class CKANTokenBean { + + protected String apiKey; + protected long timestamp; + + /** + * Instantiates a new CKAN token bean. + * + * @param apiKey the api key + * @param timestamp the timestamp + */ + public CKANTokenBean(String apiKey, long timestamp) { + super(); + this.apiKey = apiKey; + this.timestamp = timestamp; + } + + public String getApiKey() { + return apiKey; + } + + public void setApiKey(String apiKey) { + this.apiKey = apiKey; + } + + public long getTimestamp() { + return timestamp; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + +} diff --git a/src/main/java/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogue.java b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogue.java new file mode 100644 index 0000000..800a459 --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogue.java @@ -0,0 +1,200 @@ +package org.gcube.datacatalogue.ckanutillibrary.server; + +import java.util.List; +import java.util.Map; + +import org.gcube.datacatalogue.ckanutillibrary.shared.ResourceBean; +import org.gcube.datacatalogue.ckanutillibrary.shared.RolesCkanGroupOrOrg; + +import eu.trentorise.opendata.jackan.model.CkanDataset; +import eu.trentorise.opendata.jackan.model.CkanGroup; +import eu.trentorise.opendata.jackan.model.CkanLicense; +import eu.trentorise.opendata.jackan.model.CkanOrganization; + +public interface DataCatalogue { + + /** + * Finds the id associated to the chosen license + * + * @param chosenLicense + * @return the id on success, null otherwise + */ + String findLicenseIdByLicenseTitle(String chosenLicense); + + /** + * Given the id or the name of the dataset it returns its current url by + * contacting the uri resolver. If no uri resolver is available, an url that is + * not guaranteed to be long term valid will be generated. Information are not + * encrypted. + * + * @param datasetId + * @return The url of the dataset on success, null otherwise + */ + String getUnencryptedUrlFromDatasetIdOrName(String datasetIdOrName); + + /** + * Create a dataset with those information. The method allows to have multiple + * values for the same custom field key. NOTE: unfortunately java doesn't + * support overload in java interface methods (that's way I cannot use the same + * name for the method) + * + * @param title + * @param name (unique identifier) + * @param organizationNameOrId + * @param author + * @param authorMail + * @param maintainer + * @param maintainerMail + * @param version + * @param description + * @param licenseId + * @param tags + * @param customFields + * @param resources + * @param setPublic (manage visibility: Admin role is needed) + * @return the id of the dataset on success, null otherwise + * @throws Exception + */ + String createCKanDatasetMultipleCustomFields(String title, String name, String organizationNameOrId, String author, + String authorMail, String maintainer, String maintainerMail, long version, String description, + String licenseId, List tags, Map> customFields, List resources, + boolean setPublic) throws Exception; + + /** + * Patch a product with product id productId by using the couples in + * customFieldsToChange. NOTE: only the specified custom fields will be changed. + * If a custom field with a given key already exists, and removeOld is set to + * false, the new values are added at the end of the list. Otherwise they are + * lost. + * + * @param productId + * @param customFieldsToChange + * @param removeOld + * @return + */ + boolean patchProductCustomFields(String productId, Map> customFieldsToChange, + boolean removeOld); + + /** + * Add a resource described by the bean to the dataset id into + * resource.datasetId + * + * @param resource + * @return String the id of the resource on success, null otherwise + */ + String addResourceToDataset(ResourceBean resource) throws Exception; + + /** + * Remove the resource with id resourceId from the dataset in which it is. + * + * @param resourceId + * @return true on success, false otherwise. + */ + boolean deleteResourceFromDataset(String resourceId); + + /** + * Create a dataset with those information. + * + * @param apiKey + * @param title + * @param name (unique identifier) + * @param organizationNameOrId + * @param author + * @param authorMail + * @param maintainer + * @param maintainerMail + * @param version + * @param description + * @param licenseId + * @param tags + * @param customFields + * @param resources + * @param setPublic (manage visibility: Admin role is needed) + * @return the id of the dataset on success, null otherwise + * @throws Exception + */ + + /** + * Checks if a product with such name already exists. Please remember that the + * name is unique. + * + * @param nameOrId the name or the id of the dataset to check + * @return + */ + boolean existProductWithNameOrId(String nameOrId); + + /** + * The method returns the role the user has in the groups he/she belongs to (it + * uses the db, so it is much faster) + * + * @param username + * @param apiKey + * @return + */ + Map> getUserRoleByGroup(String username, String apiKey); + + /** + * Get the list of licenses' titles. + * + * @return the list of licenses' titles + */ + List getLicenseTitles(); + + /** + * Retrieve ckan licenses + * + * @return + */ + List getLicenses(); + + /** + * Retrieve the url of the uri resolver for this catalogue instance/scope + * + * @return + */ + String getUriResolverUrl(); + + /** + * Return the manage product property + * + * @return the manage product property + */ + boolean isManageProductEnabled(); + + /** + * Return the catalogue portlet for this context(i.e. scope) + * + * @return + */ + String getPortletUrl(); + + /** + * Return the ckan catalogue url in this scope. + * + * @return the catalogue url + */ + String getCatalogueUrl(); + + + /** + * Returns the list of organizations to whom the user belongs (with any role) + * @param username + * @return a list of organizations + */ + List getOrganizationsByUser(String username); + + /** + * Returns the list of groups to whom the user belongs (with any role) + * @param username + * @return a list of groups + */ + List getGroupsByUser(String username); + + /** + * Retrieve a ckan dataset given its id + * @param datasetId + * @return + */ + CkanDataset getDataset(String datasetId, String apiKey); + +} diff --git a/src/main/java/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogueFactory.java b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogueFactory.java new file mode 100644 index 0000000..542d882 --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogueFactory.java @@ -0,0 +1,78 @@ +package org.gcube.datacatalogue.ckanutillibrary.server; + +import java.util.concurrent.ConcurrentHashMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Please invoke this method to retrieve an object of this kind per scope. + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class DataCatalogueFactory { + + private static final Logger logger = LoggerFactory.getLogger(DataCatalogueFactory.class); + private static final long MAX_LIFETIME = 1000 * 60 * 30; // 30 MINUTES + private static DataCatalogueFactory instance = new DataCatalogueFactory(); + private static ConcurrentHashMap cache; + + private class CacheBean{ + DataCatalogueImpl utils; + long ttl; + + public CacheBean(long ttl, DataCatalogueImpl utils){ + this.ttl = ttl; + this.utils = utils; + } + } + + /** + * Private constructor + */ + private DataCatalogueFactory(){ + + logger.debug("Ckan factory object build"); + cache = new ConcurrentHashMap(); + + } + + /** + * Get the factory instance + * @return + */ + public static DataCatalogueFactory getFactory(){ + logger.debug("Factory requested"); + return instance; + } + + /** + * Retrieve the ckan utils information for the given scope + * @param scope + * @return + * @throws Exception + */ + public DataCatalogueImpl getUtilsPerScope(String scope) throws Exception{ + + if(scope == null || scope.isEmpty()) + throw new IllegalArgumentException("Invalid scope given!"); + + if(cache.containsKey(scope) && !expired(cache.get(scope))){ + return cache.get(scope).utils; + } + else{ + logger.info("Creating CKAN LIB utils for scope " + scope); + DataCatalogueImpl utils = new DataCatalogueImpl(scope); + cache.put(scope, new CacheBean(System.currentTimeMillis(), utils)); + return utils; + } + } + + /** + * Check if the ckan information must be retrieved again. + * @param cacheBean + * @return + */ + private boolean expired(CacheBean cacheBean) { + return (cacheBean.ttl + MAX_LIFETIME < System.currentTimeMillis()); + } +} diff --git a/src/main/java/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogueImpl.java b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogueImpl.java new file mode 100644 index 0000000..c2f2839 --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogueImpl.java @@ -0,0 +1,543 @@ +package org.gcube.datacatalogue.ckanutillibrary.server; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.gcube.common.authorization.library.provider.SecurityTokenProvider; +import org.gcube.common.scope.impl.ScopeBean; +import org.gcube.common.scope.impl.ScopeBean.Type; +import org.gcube.datacatalogue.ckanutillibrary.ckan.ExtendCkanClient; +import org.gcube.datacatalogue.ckanutillibrary.ckan.SimpleExtendCkanClient; +import org.gcube.datacatalogue.ckanutillibrary.db.DBCaller; +import org.gcube.datacatalogue.ckanutillibrary.gcat.GCatCaller; +import org.gcube.datacatalogue.ckanutillibrary.server.DataCatalogueRunningCluster.ACCESS_LEVEL_TO_CATALOGUE_PORTLET; +import org.gcube.datacatalogue.ckanutillibrary.server.utils.CatalogueUtilMethods; +import org.gcube.datacatalogue.ckanutillibrary.server.utils.url.EntityContext; +import org.gcube.datacatalogue.ckanutillibrary.shared.ResourceBean; +import org.gcube.datacatalogue.ckanutillibrary.shared.RolesCkanGroupOrOrg; +import org.gcube.datacatalogue.ckanutillibrary.shared.State; +import org.json.simple.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import eu.trentorise.opendata.jackan.internal.org.apache.http.HttpResponse; +import eu.trentorise.opendata.jackan.internal.org.apache.http.client.methods.HttpPost; +import eu.trentorise.opendata.jackan.internal.org.apache.http.entity.ContentType; +import eu.trentorise.opendata.jackan.internal.org.apache.http.entity.StringEntity; +import eu.trentorise.opendata.jackan.internal.org.apache.http.impl.client.CloseableHttpClient; +import eu.trentorise.opendata.jackan.internal.org.apache.http.impl.client.HttpClientBuilder; +import eu.trentorise.opendata.jackan.internal.org.apache.http.util.EntityUtils; +import eu.trentorise.opendata.jackan.model.CkanDataset; +import eu.trentorise.opendata.jackan.model.CkanGroup; +import eu.trentorise.opendata.jackan.model.CkanLicense; +import eu.trentorise.opendata.jackan.model.CkanOrganization; +import eu.trentorise.opendata.jackan.model.CkanUser; + +public class DataCatalogueImpl implements DataCatalogue { + + private static final Logger LOG = LoggerFactory.getLogger(DataCatalogueImpl.class); + + private String CKAN_CATALOGUE_URL; + private String CKAN_DB_NAME; + private String CKAN_DB_USER; + private String CKAN_DB_PASSWORD; + private String CKAN_DB_URL; + private Integer CKAN_DB_PORT; + private String PORTLET_URL_FOR_SCOPE; + private String SOLR_URL; + private String CKAN_TOKEN_SYS; + private String CKAN_EMAIL; + private String URI_RESOLVER_URL; + private boolean MANAGE_PRODUCT_BUTTON; + private boolean SOCIAL_POST; + private boolean ALERT_USERS_ON_POST_CREATION; + private String CONTEXT; + private Map extendRoleInOrganization; + + public Map mapAccessURLToCatalogue; + + private static final String CATALOGUE_TAB_ENDING_URL = "/catalogue"; + + // gCat client + private GCatCaller gCatCaller; + + // db client + private DBCaller dbCaller; + + // ckan client + private SimpleExtendCkanClient ckanCaller; + + // hashmap for ckan api keys + private ConcurrentHashMap apiKeysMap; + + // apikey bean expires after X minutes in the above map + private static final int EXPIRE_KEY_TIME = 60 * 60 * 1000; + + + /** + * The ckan catalogue url and database will be discovered in this scope + * @param scope + * @throws Exception if unable to find datacatalogue info + */ + public DataCatalogueImpl(String scope) throws Exception{ + + DataCatalogueRunningCluster runningInstance = new DataCatalogueRunningCluster(scope); + + // save information + CKAN_DB_URL = runningInstance.getDatabaseHosts().get(0).trim(); + CKAN_DB_PORT = runningInstance.getDatabasePorts().get(0); + CKAN_DB_NAME = runningInstance.getDataBaseName().trim(); + CKAN_DB_USER = runningInstance.getDataBaseUser().trim(); + CKAN_DB_PASSWORD = runningInstance.getDataBasePassword().trim(); + + //CKAN_TOKEN_SYS = runningInstance.getSysAdminToken().trim(); + CKAN_EMAIL = runningInstance.getEmailCatalogue().trim(); + + CKAN_CATALOGUE_URL = runningInstance.getDataCatalogueUrl().get(0).trim(); + PORTLET_URL_FOR_SCOPE = runningInstance.getPortletUrl().trim(); + mapAccessURLToCatalogue = runningInstance.getMapAccessURLToCatalogue(); + MANAGE_PRODUCT_BUTTON = runningInstance.isManageProductEnabled(); + URI_RESOLVER_URL = runningInstance.getUrlResolver(); + SOCIAL_POST = runningInstance.isSocialPostEnabled(); + ALERT_USERS_ON_POST_CREATION = runningInstance.isAlertEnabled(); + SOLR_URL = runningInstance.getUrlSolr(); + + LOG.info("In the scope: "+scope+", I read the catalogue URL: " + CKAN_CATALOGUE_URL); + + // build the clients + gCatCaller = new GCatCaller(CKAN_CATALOGUE_URL); + + dbCaller = new DBCaller(CKAN_DB_URL, CKAN_DB_PORT, CKAN_DB_NAME, CKAN_DB_USER, CKAN_DB_PASSWORD); + + ckanCaller = new SimpleExtendCkanClient(CKAN_CATALOGUE_URL); + + // init map + apiKeysMap = new ConcurrentHashMap(); + + // save the context + CONTEXT = scope; + + // extended roles + extendRoleInOrganization = runningInstance.getExtendRoleInOrganization(); + } + + @Override + public String getCatalogueUrl() { + return CKAN_CATALOGUE_URL; + } + + + @Override + public String getPortletUrl() { + + //PATCHED By Francesco + ScopeBean context = new ScopeBean(CONTEXT); + + if(context.is(Type.INFRASTRUCTURE)) { + LOG.info("Working with the {} scope returning the path read from GR 'Ckan-Porltet': {}", Type.INFRASTRUCTURE.toString(), PORTLET_URL_FOR_SCOPE); + return PORTLET_URL_FOR_SCOPE; + } + + String vreNameLower = context.name().toLowerCase(); + //CHECKING IF THE PORTLET URL CONTAINS THE VRE NAME INTO URL + if(PORTLET_URL_FOR_SCOPE.toLowerCase().contains(vreNameLower)){ + //THE PORLTET URL READ FROM GENERIC RESOUCE 'CkanPortlet' SHOULD BE ALREADY VALID, POITING TO CKAN PORTLET + return PORTLET_URL_FOR_SCOPE; + }else{ + //ADDING VRE getApiKeyFromUsernameNAME AND THE SUFFIX 'CATALOGUE_TAB_ENDING_URL' TO URL + String buildedUrl = PORTLET_URL_FOR_SCOPE.endsWith("/") ? PORTLET_URL_FOR_SCOPE : PORTLET_URL_FOR_SCOPE + "/"; + String defaultSuffix = vreNameLower + CATALOGUE_TAB_ENDING_URL; + buildedUrl+= defaultSuffix; + LOG.warn("The Portlet URL read from Generic Resource 'Ckan-Porltet' does not contain the portlet suffix, so I added the default: "+defaultSuffix); + return buildedUrl; + } + } + + + @Override + public String findLicenseIdByLicenseTitle(String chosenLicense) { + LOG.debug("Requested license id"); + + // checks + checkNotNull(chosenLicense); + + //retrieve the list of available licenses + List licenses = ckanCaller.getLicenseList(); + + for (CkanLicense ckanLicense : licenses) { + if(ckanLicense.getTitle().equals(chosenLicense)) + return ckanLicense.getId(); + } + + return null; + } + + @Override + public List getLicenseTitles() { + + LOG.debug("Request for CKAN licenses"); + + // get the url and the api key of the user + List result = new ArrayList(); + + //retrieve the list of available licenses + List licenses = ckanCaller.getLicenseList(); + + for (CkanLicense ckanLicense : licenses) { + + result.add(ckanLicense.getTitle()); + LOG.debug("License is " + ckanLicense.getTitle() + " and id " + ckanLicense.getId()); + + } + + return result; + } + + @Override + public List getLicenses() { + LOG.debug("Request for CKAN licenses (original jackan objects are going to be retrieved)"); + + //retrieve the list of available licenses + return ckanCaller.getLicenseList(); + } + + @Override + public CkanDataset getDataset(String datasetId, String apiKey) { + + LOG.info("Request ckan dataset with id " + datasetId); + + // checks + checkNotNull(datasetId); + checkArgument(!datasetId.isEmpty()); + + try{ + + if(apiKey!=null && !apiKey.isEmpty()) { + LOG.info("API-KEY found. Calling the "+ExtendCkanClient.class.getSimpleName()); + ExtendCkanClient client = new ExtendCkanClient(CKAN_CATALOGUE_URL, apiKey); + return client.getDataset(datasetId); + } + + String authzToken = SecurityTokenProvider.instance.get(); + if(authzToken!=null && !authzToken.isEmpty()) { + LOG.info("gcube-token found. Calling the gCat client"); + return gCatCaller.getDatasetForName(datasetId); + } + + LOG.info("No api-key or gcube-token found. Calling Ckan Client without API-KEY"); + return ckanCaller.getDataset(datasetId); + + }catch(Exception e){ + LOG.error("Unable to retrieve such dataset, returning null ...", e); + } + + return null; + } + + + @Override + public String getUnencryptedUrlFromDatasetIdOrName(String datasetIdOrName) { + LOG.debug("Request coming for getting dataset url (not encrypted) of dataset with name/id " + datasetIdOrName); + + // checks + checkNotNull(datasetIdOrName); + checkArgument(!datasetIdOrName.isEmpty()); + String url = null; + try{ + + // get the dataset from name + ExtendCkanClient client = new ExtendCkanClient(CKAN_CATALOGUE_URL, CKAN_TOKEN_SYS); + CkanDataset dataset = client.getDataset(datasetIdOrName); + String name = dataset.getName(); + + if(dataset != null){ + + if(getUriResolverUrl() != null) + url = getUrlForProduct(CONTEXT, EntityContext.DATASET, name); + + if(url == null || url.isEmpty()) + url = getPortletUrl() + "?" + URLEncoder.encode("path=/dataset/" + name, "UTF-8"); + + } + }catch(Exception e){ + LOG.error("Error while retrieving dataset with id/name=" + datasetIdOrName, e); + } //requestEntity.put("clear_url", Boolean.toString(unencrypted)); + return url; + } + + public String createCKanDatasetMultipleCustomFields(String title, String name, String organizationNameOrId, + String author, String authorMail, String maintainer, String maintainerMail, long version, + String description, String licenseId, List tags, Map> customFields, + List resources, boolean setPublic) throws Exception { + return null; + } + + @Override + public Map> getUserRoleByGroup( + String username, String apiKey) { + + checkNotNull(username); + checkNotNull(apiKey); + + checkNotNull(username); + checkNotNull(apiKey); + + Map> toReturn = new HashMap>(); + + try{ + + String ckanUsername = CatalogueUtilMethods.fromUsernameToCKanUsername(username); + Map partialResult = dbCaller.getGroupsByUserFromDB(ckanUsername); + + for (String groupID : partialResult.keySet()) { + + CkanGroup group = ckanCaller.getGroup(groupID); + HashMap subMap = new HashMap(); + subMap.put(group, partialResult.get(groupID)); + toReturn.put(groupID, subMap); + } + + LOG.debug("Returning map " + toReturn); + }catch(Exception e){ + LOG.error("Failed to retrieve roles of user in his/her own groups",e); + } + + return toReturn; + + } + + + /** + * Retrieve an url for the tuple scope, entity, entity name + * @param context + * @param entityContext + * @param entityName + */ + private String getUrlForProduct(String context, EntityContext entityContext, String entityName){ + + String toReturn = null; + + try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) { + + HttpPost httpPostRequest = new HttpPost(getUriResolverUrl()); + + JSONObject requestEntity = new JSONObject(); + requestEntity.put("gcube_scope", context); + requestEntity.put("entity_context", entityContext.toString()); + requestEntity.put("entity_name", entityName); + + StringEntity params = new StringEntity(requestEntity.toJSONString(), ContentType.APPLICATION_JSON); + httpPostRequest.setEntity(params); + + HttpResponse response = httpClient.execute(httpPostRequest); + + if(response.getStatusLine().getStatusCode() != 200) + throw new Exception("There was an error while creating an url " + response.getStatusLine()); + + toReturn = EntityUtils.toString(response.getEntity()); + LOG.debug("Result is " + toReturn); + + }catch(Exception e){ + LOG.error("Failed to get an url for this product", e); + } + + return toReturn; + } + + + + @Override + public String getUriResolverUrl() { + return URI_RESOLVER_URL; + } + + /** + * Check if the manage product is enabled + * @return + */ + @Override + public boolean isManageProductEnabled() { + return MANAGE_PRODUCT_BUTTON; + } + + @Override + public List getOrganizationsByUser(String username) { + + LOG.debug("Requested organizations for user " + username); + + // checks + checkNotNull(username); + + // in order to avoid errors, the username is always converted + String ckanUsername = CatalogueUtilMethods.fromUsernameToCKanUsername(username); + + // list to return + List toReturn = new ArrayList(); + + try{ + + // get the list of all organizations + List organizations = ckanCaller.getOrganizationList(); + + // iterate over them + for (CkanOrganization ckanOrganization : organizations) { + + // get the list of users in it (if you try ckanOrganization.getUsers() it returns null.. maybe a bug TODO) + List users = ckanCaller.getOrganization(ckanOrganization.getName()).getUsers(); + + // check if the current user is among them + for (CkanUser ckanUser : users) { + if(ckanUser.getName().equals(ckanUsername)){ + + LOG.debug("User " + ckanUsername + " is into " + ckanOrganization.getName()); + toReturn.add(ckanOrganization); + break; + + } + } + } + }catch(Exception e){ + LOG.error("Unable to get user's organizations", e); + } + return toReturn; + } + + @Override + public List getGroupsByUser(String username) { + LOG.debug("Requested groups for user " + username); + + // checks + checkNotNull(username); + + // in order to avoid errors, the username is always converted + String ckanUsername = CatalogueUtilMethods.fromUsernameToCKanUsername(username); + + // list to return + List toReturn = new ArrayList(); + + try{ + + // get the list of all organizations + List groups = ckanCaller.getGroupList(); + + // iterate over them + for (CkanGroup ckanGroup : groups) { + + List users = ckanCaller.getGroup(ckanGroup.getName()).getUsers(); + + // check if the current user is among them + for (CkanUser ckanUser : users) { + if(ckanUser.getName().equals(ckanUsername)){ + + LOG.debug("User " + ckanUsername + " is into " + ckanGroup.getName()); + toReturn.add(ckanGroup); + break; + + } + } + } + }catch(Exception e){ + LOG.error("Unable to get user's groups", e); + } + return toReturn; + } + + //@Override + private String getApiKeyFromUsername(String username) { + + LOG.debug("Request api key for user = " + username); + + // checks + checkNotNull(username); + checkArgument(!username.isEmpty()); + + // in order to avoid errors, the username is always converted + String ckanUsername = CatalogueUtilMethods.fromUsernameToCKanUsername(username); + + // check in the hashmap first + if(apiKeysMap.containsKey(ckanUsername)){ + CKANTokenBean bean = apiKeysMap.get(ckanUsername); + if(bean.timestamp + EXPIRE_KEY_TIME > System.currentTimeMillis()){ // it's still ok + return bean.apiKey; + } + } + + LOG.debug("Api key was not in cache or it expired"); + + try{ + + String apiToReturn = dbCaller.getApiKeyFromUsername(username, State.ACTIVE.name().toLowerCase()); + + // save into the hashmap + if(apiToReturn != null) + apiKeysMap.put(ckanUsername, new CKANTokenBean(apiToReturn, System.currentTimeMillis())); + + return apiToReturn; + + }catch(Exception e){ + LOG.error("Unable to retrieve key for user " + ckanUsername, e); + } + + return null; + } + + + + + + + + + + + + /* + * + * + * + * + * + * + * + * + * + * WRITE OPERATIONS + * + * + * + * + * + * + * + * + * + * + */ + + + public boolean patchProductCustomFields(String productId, Map> customFieldsToChange, + boolean removeOld) { + return false; + } + + public String addResourceToDataset(ResourceBean resource) throws Exception { + return null; + } + + public boolean deleteResourceFromDataset(String resourceId) { + return false; + } + + public boolean existProductWithNameOrId(String nameOrId) { + return false; + } + + +} diff --git a/src/main/java/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogueRunningCluster.java b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogueRunningCluster.java new file mode 100644 index 0000000..3f153ea --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogueRunningCluster.java @@ -0,0 +1,690 @@ +package org.gcube.datacatalogue.ckanutillibrary.server; + +import static org.gcube.resources.discovery.icclient.ICFactory.client; +import static org.gcube.resources.discovery.icclient.ICFactory.clientFor; +import static org.gcube.resources.discovery.icclient.ICFactory.queryFor; + +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.gcube.common.encryption.StringEncrypter; +import org.gcube.common.resources.gcore.ServiceEndpoint; +import org.gcube.common.resources.gcore.ServiceEndpoint.AccessPoint; +import org.gcube.common.resources.gcore.ServiceEndpoint.Property; +import org.gcube.common.resources.gcore.utils.XPathHelper; +import org.gcube.common.scope.api.ScopeProvider; +import org.gcube.datacatalogue.ckanutillibrary.shared.ex.ApplicationProfileNotFoundException; +import org.gcube.datacatalogue.ckanutillibrary.shared.ex.NoApplicationProfileMasterException; +import org.gcube.datacatalogue.ckanutillibrary.shared.ex.NoDataCatalogueRuntimeResourceException; +import org.gcube.datacatalogue.ckanutillibrary.shared.ex.ServiceEndPointException; +import org.gcube.resources.discovery.client.api.DiscoveryClient; +import org.gcube.resources.discovery.client.queries.api.Query; +import org.gcube.resources.discovery.client.queries.api.SimpleQuery; +import org.gcube.resources.discovery.client.queries.impl.QueryBox; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +/** + * Retrieve ckan running instance information in the infrastructure (for both its database and data catalogue url). + * + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + * updated by Francesco Mangiacrapa at ISTI-CNR (francesco.mangiacrapa@isti.cnr.it) + */ +public class DataCatalogueRunningCluster { + + //logger + private static final Logger logger = LoggerFactory.getLogger(DataCatalogueRunningCluster.class); + + // database of the datacatalogue info + private final static String RUNTIME_DB_RESOURCE_NAME = "CKanDatabase"; + private final static String PLATFORM_DB_NAME = "postgres"; + private final static String APPLICATION_PROFILE_NAME = "CkanPortlet"; + + // data catalogue info + private final static String RUNTIME_CATALOGUE_RESOURCE_NAME = "CKanDataCatalogue"; + private final static String PLATFORM_CATALOGUE_NAME = "Tomcat"; + + // api key property for SYSADMIN + private final static String API_KEY_PROPERTY = "API_KEY"; + + // catalogue email + private final static String CKAN_EMAIL_PROPERTY = "CATALOGUE_EMAIL"; + + // property to retrieve the master service endpoint into the /root scope + private final static String IS_MASTER_ROOT_KEY_PROPERTY = "IS_ROOT_MASTER"; // true, false.. missing means false as well + private final static String IS_MANAGE_PRODUCT_ENABLED = "IS_MANAGE_PRODUCT_ENABLED"; // true, false.. missing means false as well (for GRSF records) + + private final static String SOCIAL_POST = "SOCIAL_POST"; + private final static String ALERT_USERS_ON_POST_CREATION = "ALERT_USERS_ON_POST_CREATION"; + private final static String SOLR_INDEX_ADDRESS = "SOLR_INDEX_ADDRESS"; + + // url of the http uri for this scope + private final static String URL_RESOLVER = "URL_RESOLVER"; + + // Other generic resource for delegating roles in groups to users + private final static String CATALOGUE_EXTENDING_ROLES = "CatalogueDelegateRoles"; + + // retrieved data + private List datacatalogueUrls = new ArrayList(); + private List hostsDB = new ArrayList(); + private List portsDB = new ArrayList(); + private String nameDB; + private String userDB; + private String passwordDB; + private String portletUrl; + private String urlSolr; + private boolean manageProductEnabled; + private String urlResolver; + private boolean socialPost; + private boolean alertUsers; + private Map extendRoleInOrganization = new HashMap(0); + + // generic role key + public static final String CKAN_GENERIC_ROLE = "*"; + public static final String ROLE_ORGANIZATION_SEPARATOR = "|"; + public static final String TUPLES_SEPARATOR = ","; + + private static final String DEFAULT_CATALOGUE_EMAIL = "catalogue@d4science.org"; + + /** + * The Enum ACCESS_LEVEL_TO_CATALOGUE_PORTLET. + * + * @author Francesco Mangiacrapa at ISTI-CNR (francesco.mangiacrapa@isti.cnr.it) + * + * Nov 12, 2019 + */ + //Added by Francesco, see #18039 + public static final String PUBLIC_VRE_CATALOGUE_URL = "public-vre-url"; + public static enum ACCESS_LEVEL_TO_CATALOGUE_PORTLET {PUBLIC_GATEWAY, PUBLIC_VRE, PRIVATE_VRE}; + public Map mapAccessURLToCatalogue; + + // this token is needed in order to assign roles to user + private String sysAdminToken; + + private String catalogueEmail; + + /** + * Instantiates a new data catalogue running cluster. + * + * @param scope the scope + * @throws Exception the exception + */ + public DataCatalogueRunningCluster(String scope) throws Exception{ + + if(scope == null || scope.isEmpty()) + throw new Exception("Invalid scope!!"); + + // retrieve the current scope and save it (it will be reset later) + String currentScope = ScopeProvider.instance.get(); + + logger.info("Retrieving ckan database service end point information for scope " + scope); + try { + + // set the scope + ScopeProvider.instance.set(scope); + + logger.debug("Retrieving database information."); + + List resources = getConfigurationFromISFORDB(); + evaluateRightConfigurationDB(resources); + + logger.debug("Retrieving ckan data catalogue service end point information and sysadmin token."); + + resources = getConfigurationFromISFORCatalogueUrl(); + evaluateRightConfigurationCatalogue(resources); + + // finally get the url in which the ckan portlet is deployed + + logger.debug("Looking for portlet url in " + ScopeProvider.instance.get() + " scope" ); + + //portletUrl = getPortletUrlFromInfrastrucure(); + mapAccessURLToCatalogue = getPortletUrlFromInfrastrucure(); + portletUrl = mapAccessURLToCatalogue.get(ACCESS_LEVEL_TO_CATALOGUE_PORTLET.PRIVATE_VRE); + + // and parse the CatalogueDelegateRole resource, if any, in this context + parseExtendingRoles(); + + }catch(Exception e) { + logger.warn("The following error occurred: " + e.toString()); + throw e; + }finally{ + ScopeProvider.instance.set(currentScope); + } + } + + + /** + * Evaluate the right configuration about ckan. + * + * @param resources the resources + * @throws NoDataCatalogueRuntimeResourceException the no data catalogue runtime resource exception + * @throws ServiceEndPointException the service end point exception + */ + private void evaluateRightConfigurationCatalogue( + List resources) throws NoDataCatalogueRuntimeResourceException, ServiceEndPointException { + if (resources.size() == 0){ + logger.error("There is no Runtime Resource having name " + RUNTIME_CATALOGUE_RESOURCE_NAME +" and Platform " + PLATFORM_CATALOGUE_NAME + " in this scope."); + throw new NoDataCatalogueRuntimeResourceException(); + } + else { + logger.debug(resources.toString()); + try{ + if(resources.size() > 1){ + boolean oneWasMaster = false; + + logger.info("Too many Runtime Resource having name " + RUNTIME_CATALOGUE_RESOURCE_NAME +" in this scope.. Looking for the one that has the property " + IS_MASTER_ROOT_KEY_PROPERTY); + + for (ServiceEndpoint res : resources) { + + Iterator accessPointIterator = res.profile().accessPoints().iterator(); + + while (accessPointIterator.hasNext()) { + ServiceEndpoint.AccessPoint accessPoint = accessPointIterator + .next(); + + // get the is master property + Property entry = accessPoint.propertyMap().get(IS_MASTER_ROOT_KEY_PROPERTY); + String isMaster = entry != null ? entry.value() : null; + + if(isMaster == null || !isMaster.equals("true")) + continue; + + // set this variable + oneWasMaster = true; + + // add this host + datacatalogueUrls.add(accessPoint.address()); + + // retrieve sys admin token + sysAdminToken = accessPoint.propertyMap().get(API_KEY_PROPERTY).value(); + sysAdminToken = StringEncrypter.getEncrypter().decrypt(sysAdminToken); + + // retrieve catalogue email + if(accessPoint.propertyMap().containsKey(CKAN_EMAIL_PROPERTY)){ + catalogueEmail = accessPoint.propertyMap().get(CKAN_EMAIL_PROPERTY).value(); + catalogueEmail = StringEncrypter.getEncrypter().decrypt(catalogueEmail); + } + + // retrieve URL_RESOLVER + if(accessPoint.propertyMap().containsKey(URL_RESOLVER)) + urlResolver = accessPoint.propertyMap().get(URL_RESOLVER).value(); + + // break now + break; + } + } + + // if none of them was master, throw an exception + if(!oneWasMaster) + throw new NoApplicationProfileMasterException("There is no application profile with MASTER property"); + + }else{ + + ServiceEndpoint res = resources.get(0); + Iterator accessPointIterator = res.profile().accessPoints().iterator(); + + while (accessPointIterator.hasNext()) { + ServiceEndpoint.AccessPoint accessPoint = accessPointIterator + .next(); + + // add this host + datacatalogueUrls.add(accessPoint.address()); + + // retrieve sys admin token + sysAdminToken = accessPoint.propertyMap().get(API_KEY_PROPERTY).value(); + sysAdminToken = StringEncrypter.getEncrypter().decrypt(sysAdminToken); + + // retrieve catalogue email + if(accessPoint.propertyMap().containsKey(CKAN_EMAIL_PROPERTY)){ + catalogueEmail = accessPoint.propertyMap().get(CKAN_EMAIL_PROPERTY).value(); + catalogueEmail = StringEncrypter.getEncrypter().decrypt(catalogueEmail); + } + + // get the is manage product property + Property entry = accessPoint.propertyMap().get(IS_MANAGE_PRODUCT_ENABLED); + String isManageProduct = entry != null ? entry.value() : null; + + if(isManageProduct != null && isManageProduct.equals("true")){ + logger.info("Manage product is enabled in this scope"); + manageProductEnabled = true; + } + + // retrieve option to check if the social post has to be made + socialPost = true; // default is true + if(accessPoint.propertyMap().containsKey(SOCIAL_POST)) + if(accessPoint.propertyMap().get(SOCIAL_POST).value().trim().equalsIgnoreCase("false")) + socialPost = false; + + + // retrieve option for user alert + if(accessPoint.propertyMap().containsKey(ALERT_USERS_ON_POST_CREATION)) + if(accessPoint.propertyMap().get(ALERT_USERS_ON_POST_CREATION).value().trim().equalsIgnoreCase("true")) + alertUsers = true; + + // retrieve URL_RESOLVER + if(accessPoint.propertyMap().containsKey(URL_RESOLVER)) + urlResolver = accessPoint.propertyMap().get(URL_RESOLVER).value(); + + // retrieve url of the solr index for further queries + if(accessPoint.propertyMap().containsKey(SOLR_INDEX_ADDRESS)) + urlSolr = accessPoint.propertyMap().get(SOLR_INDEX_ADDRESS).value(); + + } + } + + }catch(Exception e){ + throw new ServiceEndPointException("There is no service end point for such information" + e.toString()); + } + } + + + } + + /** + * Retrieve the right DB information. + * + * @param resources the resources + * @throws ServiceEndPointException the service end point exception + * @throws NoDataCatalogueRuntimeResourceException the no data catalogue runtime resource exception + */ + private void evaluateRightConfigurationDB(List resources) throws ServiceEndPointException, NoDataCatalogueRuntimeResourceException { + + if (resources.size() == 0){ + throw new NoDataCatalogueRuntimeResourceException("There is no Runtime Resource having name " + RUNTIME_DB_RESOURCE_NAME +" and Platform " + PLATFORM_DB_NAME + " in this scope."); + } + else { + try{ + + if(resources.size() > 1){ + boolean oneWasMaster = false; + + logger.info("Too many Runtime Resource having name " + RUNTIME_DB_RESOURCE_NAME +" in this scope.. Looking for the one that has the property " + IS_MASTER_ROOT_KEY_PROPERTY); + + for (ServiceEndpoint res : resources) { + + Iterator accessPointIterator = res.profile().accessPoints().iterator(); + + while (accessPointIterator.hasNext()) { + ServiceEndpoint.AccessPoint accessPoint = accessPointIterator + .next(); + + // get the is master property + Property entry = accessPoint.propertyMap().get(IS_MASTER_ROOT_KEY_PROPERTY); + String isMaster = entry != null ? entry.value() : null; + + if(isMaster == null || !isMaster.equals("true")) + continue; + + // set this variable + oneWasMaster = true; + + // add this host + hostsDB.add(accessPoint.address().split(":")[0]); + + // save the port + int port = Integer.parseInt(accessPoint.address().split(":")[1]); + portsDB.add(port); + + // save the name of the cluster (this should be unique) + nameDB = accessPoint.name(); + + // save user and password + passwordDB = StringEncrypter.getEncrypter().decrypt(accessPoint.password()); + userDB = accessPoint.username(); + + // now break + break; + } + } + + // if none of them was master, throw an exception + if(!oneWasMaster) + throw new NoApplicationProfileMasterException("There is no application profile with MASTER property"); + }else{ + logger.debug(resources.toString()); + ServiceEndpoint res = resources.get(0); + Iterator accessPointIterator = res.profile().accessPoints().iterator(); + + while (accessPointIterator.hasNext()) { + ServiceEndpoint.AccessPoint accessPoint = accessPointIterator + .next(); + + // add this host + hostsDB.add(accessPoint.address().split(":")[0]); + + // save the port + int port = Integer.parseInt(accessPoint.address().split(":")[1]); + portsDB.add(port); + + // save the name of the cluster (this should be unique) + nameDB = accessPoint.name(); + + // save user and password + passwordDB = StringEncrypter.getEncrypter().decrypt(accessPoint.password()); + userDB = accessPoint.username(); + + } + } + }catch(Exception e ){ + throw new ServiceEndPointException(e.toString()); + } + } + } + + /** + * Retrieve endpoints information from IS for DB. + * + * @return list of endpoints for ckan database + * @throws Exception the exception + */ + private static List getConfigurationFromISFORDB() throws Exception{ + + SimpleQuery query = queryFor(ServiceEndpoint.class); + query.addCondition("$resource/Profile/Name/text() eq '"+ RUNTIME_DB_RESOURCE_NAME +"'"); + query.addCondition("$resource/Profile/Platform/Name/text() eq '"+ PLATFORM_DB_NAME +"'"); + DiscoveryClient client = clientFor(ServiceEndpoint.class); + List toReturn = client.submit(query); + return toReturn; + + } + + /** + * Retrieve endpoints information from IS for DataCatalogue URL. + * + * @return list of endpoints for ckan data catalogue + * @throws Exception the exception + */ + private static List getConfigurationFromISFORCatalogueUrl() throws Exception{ + + SimpleQuery query = queryFor(ServiceEndpoint.class); + query.addCondition("$resource/Profile/Name/text() eq '"+ RUNTIME_CATALOGUE_RESOURCE_NAME +"'"); + query.addCondition("$resource/Profile/Platform/Name/text() eq '"+ PLATFORM_CATALOGUE_NAME +"'"); + DiscoveryClient client = clientFor(ServiceEndpoint.class); + List toReturn = client.submit(query); + return toReturn; + + } + + /** + * Retrieve the url of the ckan portlet deployed into this scope + * updated by Francesco see Feature #18039. + * + * @return the portlet url from infrastrucure + */ + private static Map getPortletUrlFromInfrastrucure() { + + String scope = ScopeProvider.instance.get(); + logger.debug("Trying to fetch applicationProfile profile from the infrastructure for " + APPLICATION_PROFILE_NAME + " scope: " + scope); + Map mapAccess = new HashMap(3); + try { + + Query q = new QueryBox("for $profile in collection('/db/Profiles/GenericResource')//Resource " + + "where $profile/Profile/SecondaryType/string() eq 'ApplicationProfile' and $profile/Profile/Name/string() " + + " eq '" + APPLICATION_PROFILE_NAME + "'" + + "return $profile"); + + DiscoveryClient client = client(); + List appProfile = client.submit(q); + + if (appProfile == null || appProfile.size() == 0) + throw new ApplicationProfileNotFoundException("Your applicationProfile is not registered in the infrastructure"); + else { + String elem = appProfile.get(0); + DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + Node node = docBuilder.parse(new InputSource(new StringReader(elem))).getDocumentElement(); + XPathHelper helper = new XPathHelper(node); + + List currValue = null; + currValue = helper.evaluate("/Resource/Profile/Body/url/text()"); + if (currValue != null && currValue.size() > 0) { + logger.debug("Private portlet url found is " + currValue.get(0)); + //return currValue.get(0); + mapAccess.put(ACCESS_LEVEL_TO_CATALOGUE_PORTLET.PRIVATE_VRE, currValue.get(0)); + } + + currValue = null; + currValue = helper.evaluate("/Resource/Profile/Body/"+PUBLIC_VRE_CATALOGUE_URL+"/text()"); + if (currValue != null && currValue.size() > 0) { + logger.debug(PUBLIC_VRE_CATALOGUE_URL+" portlet url found is " + currValue.get(0)); + //return currValue.get(0); + mapAccess.put(ACCESS_LEVEL_TO_CATALOGUE_PORTLET.PUBLIC_VRE, currValue.get(0)); + } + + } + + } catch (Exception e) { + logger.error("Error while trying to fetch applicationProfile profile from the infrastructure", e); + } + + return mapAccess; + + } + + /** + * Parse the CatalogueDelegateRoles in this context. + * + * @throws ParserConfigurationException the parser configuration exception + * @throws SAXException the SAX exception + * @throws IOException Signals that an I/O exception has occurred. + */ + private void parseExtendingRoles() throws ParserConfigurationException, SAXException, IOException { + + Query q = new QueryBox("for $profile in collection('/db/Profiles/GenericResource')//Resource " + + "where $profile/Profile/SecondaryType/string() eq 'ApplicationProfile' and $profile/Profile/Name/string() " + + " eq '" + CATALOGUE_EXTENDING_ROLES + "'" + + "return $profile"); + + DiscoveryClient client = client(); + List appProfile = client.submit(q); + + logger.debug("Resource for extending role has size " + appProfile.size()); + + if (appProfile == null || appProfile.size() == 0) + return; + else { + String profile = appProfile.get(0); + DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + Node node = docBuilder.parse(new InputSource(new StringReader(profile))).getDocumentElement(); + XPathHelper helper = new XPathHelper(node); + + // fetch delegate elements + NodeList delegates = helper.evaluateForNodes("/Resource/Profile/Body/delegates/delegate"); + if (delegates != null && delegates.getLength() > 0) { + + for(int i = 0; i < delegates.getLength(); i++){ + + Node nodeI = delegates.item(i); + + if(nodeI.getNodeType() == Node.ELEMENT_NODE) { + + Element elem = (Element)nodeI; + String role = elem.getElementsByTagName("sourceRole").item(0).getTextContent(); + String destOrg = elem.getElementsByTagName("destOrganization").item(0).getTextContent(); + String sourceOrg = elem.getElementsByTagName("sourceOrganization").item(0).getTextContent(); + + if(destOrg == null || sourceOrg == null){ + logger.warn("DestOrg or SourceOrg parameters missing"); + continue; + }else{ + String currentValueForKey = extendRoleInOrganization.get(sourceOrg); + + if(currentValueForKey == null) + currentValueForKey = ""; + else + currentValueForKey += TUPLES_SEPARATOR; + + currentValueForKey += destOrg + ROLE_ORGANIZATION_SEPARATOR + role; + extendRoleInOrganization.put(sourceOrg, currentValueForKey); + } + } + } + } + + } + + logger.debug("Extended role map in this scope is " + extendRoleInOrganization); + } + + /** + * Retrieve the ckan portlet url. + * + * @return the portletUrl + */ + public String getPortletUrl() { + return portletUrl; + } + + /** + * Retrieve data catalogue url. + * + * @return the data catalogue url + */ + public List getDataCatalogueUrl() { + return datacatalogueUrls; + } + + /** + * Get the hosts for such resource. + * + * @return the database hosts + */ + public List getDatabaseHosts() { + return hostsDB; + } + + /** + * Get the ports for such resource. + * + * @return the database ports + */ + public List getDatabasePorts() { + return portsDB; + } + + /** + * Get the database name. + * + * @return the data base name + */ + public String getDataBaseName() { + return nameDB; + } + + /** + * Get the database's user. + * + * @return the data base user + */ + public String getDataBaseUser() { + return userDB; + } + + /** + * Get the database's password. + * + * @return the data base password + */ + public String getDataBasePassword() { + return passwordDB; + } + + /** + * Gets the sys admin token. + * + * @return the sysAdminToken + */ + public String getSysAdminToken() { + return sysAdminToken; + } + + /** + * Is manager product enabled (e.g., for GRSF records) + * + * @return true, if is manage product enabled + */ + public boolean isManageProductEnabled() { + return manageProductEnabled; + } + + /** + * Get the url of the uri resolver for this instance/scope. + * + * @return the url resolver + */ + public String getUrlResolver() { + return urlResolver; + } + + /** + * Check if alert user members is enabled. + * + * @return true, if is alert enabled + */ + public boolean isAlertEnabled() { + return alertUsers; + } + + + /** + * Get roles to extend. + * + * @return Map + */ + public Map getExtendRoleInOrganization() { + return extendRoleInOrganization; + } + + /** + * Get the solr index base url. + * + * @return the url solr + */ + public String getUrlSolr() { + return urlSolr; + } + + + /** + * Gets the map access URL to catalogue. + * + * @return the map access URL to catalogue + */ + public Map getMapAccessURLToCatalogue() { + return mapAccessURLToCatalogue; + } + + + /** + * Get the catalogue email. Default is "catalogue@d4science.org" + * + * @return the email catalogue + */ + public String getEmailCatalogue() { + logger.debug("Read email from resource is " + catalogueEmail); + return catalogueEmail == null? DEFAULT_CATALOGUE_EMAIL : catalogueEmail; + } + + /** + * Checks if is social post enabled. + * + * @return true, if is social post enabled + */ + public boolean isSocialPostEnabled() { + return socialPost; + } + +} diff --git a/src/main/java/org/gcube/datacatalogue/ckanutillibrary/server/utils/CatalogueUtilMethods.java b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/server/utils/CatalogueUtilMethods.java new file mode 100644 index 0000000..9136507 --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/server/utils/CatalogueUtilMethods.java @@ -0,0 +1,139 @@ +package org.gcube.datacatalogue.ckanutillibrary.server.utils; + +import java.net.HttpURLConnection; +import java.net.URL; + +import org.gcube.common.scope.api.ScopeProvider; +import org.gcube.common.scope.impl.ScopeBean; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Some utility methods used within the library. + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class CatalogueUtilMethods { + + private static final Logger logger = LoggerFactory.getLogger(CatalogueUtilMethods.class); + private final static String HTTPS = "https"; + private final static String HTTP = "http"; + + /** + * Maps the scope name to the ckan organization name + * Use getCKANOrganization() which uses current scope + * @return + */ + @Deprecated + public static String getOrganizationNameFromScope(String scope){ + if(scope == null || scope.isEmpty()) { + throw new IllegalArgumentException("scope cannot be null"); + } + ScopeBean scopeBean = new ScopeBean(scope); + return scopeBean.name().toLowerCase().replace(" ", "_").replace("-", "_"); + + } + + /** + * Get CKAN Organization name by using scope name + * + */ + public static String getCKANOrganization() { + ScopeBean scopeBean = new ScopeBean(ScopeProvider.instance.get()); + return scopeBean.name().toLowerCase().replace(" ", "_").replace("-", "_"); + } + + /** + * Ckan username has _ instead of . (that is, costantino.perciante -> costantino_perciante) + * @param owner + * @return + */ + public static String fromUsernameToCKanUsername(String username){ + if(username == null) + return null; + return username.trim().replaceAll("\\.", "_"); + } + + /** + * Liferay username has . instead of _ (that is, costantino_perciante -> costantino.perciante) + * @param owner + * @return + */ + public static String fromCKanUsernameToUsername(String ckanUsername){ + if(ckanUsername == null) + return null; + + return ckanUsername.trim().replaceAll("_", "."); + } + + /** + * Generate the catalogue's dataset name from its title + * @param title + * @return + */ + public static String fromProductTitleToName(String title) { + if(title == null) + return null; + + String regexTitleNameTransform = "[^A-Za-z0-9_-]"; + return title.trim().replaceAll(regexTitleNameTransform, "_").replaceAll("_+", "_").toLowerCase(); + } + + /** + * Convert a display group name to group id + * @param groupName + * @return + */ + public static String fromGroupTitleToName(String groupName){ + if(groupName == null) + return null; + + String regexGroupNameTransform = "[^A-Za-z0-9-]"; + String modified = groupName.trim().replaceAll(regexGroupNameTransform, "-").replaceAll("-+", "-").toLowerCase(); + + if(modified.startsWith("-")) + modified = modified.substring(1); + if(modified.endsWith("-")) + modified = modified.substring(0, modified.length() -1); + + return modified; + + } + + /** + * Utility method to check if a something at this url actually exists + * @param URLName + * @return + */ + public static boolean resourceExists(String URLName){ + + if(URLName == null || URLName.isEmpty()) + return false; + + try { + // replace https + String urlToTest = URLName.replace(HTTPS, HTTP); + HttpURLConnection.setFollowRedirects(true); + HttpURLConnection con = (HttpURLConnection) new URL(urlToTest).openConnection(); + con.setRequestMethod("HEAD"); + logger.debug("Return code is " + con.getResponseCode()); + return (con.getResponseCode() == HttpURLConnection.HTTP_OK); + } + catch (Exception e) { + logger.error("Exception while checking url", e); + return false; + } + } + + /** + * Builds a string made of key + scope + * @param key + * @param scope + * @return + */ + public static String concatenateSessionKeyScope(String key, String scope){ + if(key == null || scope == null) + throw new IllegalArgumentException("Key or scope null"); + return key.concat(scope); + } + +} diff --git a/src/main/java/org/gcube/datacatalogue/ckanutillibrary/server/utils/SessionCatalogueAttributes.java b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/server/utils/SessionCatalogueAttributes.java new file mode 100644 index 0000000..a141eb9 --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/server/utils/SessionCatalogueAttributes.java @@ -0,0 +1,17 @@ +package org.gcube.datacatalogue.ckanutillibrary.server.utils; + +/** + * A list of attributes that are saved into http session. + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class SessionCatalogueAttributes { + + // CKAN KEYS (PLEASE NOTE THAT MOST OF THESE INFO ARE SAVED INTO SESSION PER SCOPE) + public static final String CKAN_ORGS_USER_KEY = "ckanOrgs"; // organizations to whom he belongs (shown into the portlet) + public static final String CKAN_HIGHEST_ROLE = "ckanHighestRole"; // editor, member, admin + public static final String CKAN_ORGANIZATIONS_PUBLISH_KEY = "ckanOrganizationsPublish"; // here he can publish (admin/editor role) + public static final String CKAN_LICENSES_KEY = "ckanLicenses"; // licenses + public static final String CKAN_PROFILES_KEY = "ckanProfiles"; // product profiles + public static final String CKAN_GROUPS_MEMBER = "ckanGroupsMember"; + public static final String CKAN_GROUPS_USER_KEY = "ckanGroups"; // to show the list of groups in the portlet +} diff --git a/src/main/java/org/gcube/datacatalogue/ckanutillibrary/server/utils/url/EntityContext.java b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/server/utils/url/EntityContext.java new file mode 100644 index 0000000..947d1d0 --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/server/utils/url/EntityContext.java @@ -0,0 +1,24 @@ +package org.gcube.datacatalogue.ckanutillibrary.server.utils.url; + +/** + * Entity context for uri resolver + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public enum EntityContext { + + DATASET("dataset"), + GROUP("group"), + ORGANIZATION("organization"); + + private String entityAsString; + + private EntityContext(String entityAsString) { + this.entityAsString = entityAsString; + } + + @Override + public String toString() { + return this.entityAsString; + } + +} diff --git a/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/CKanUserWrapper.java b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/CKanUserWrapper.java new file mode 100644 index 0000000..ccc7c49 --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/CKanUserWrapper.java @@ -0,0 +1,134 @@ +package org.gcube.datacatalogue.ckanutillibrary.shared; + +import java.io.Serializable; + +/** + * A CKan user. + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class CKanUserWrapper implements Serializable{ + + private static final long serialVersionUID = 6264706263035722775L; + + private String id; + private String name; + private String apiKey; + private long creationTimestamp; + private String about; + private String openId; + private String fullName; + private String email; + private boolean isAdmin; + + + public CKanUserWrapper() { + super(); + } + + /** Create a ckan user object. + * @param id + * @param name + * @param apiKey + * @param creationTimestamp + * @param about + * @param openId + * @param fullName + * @param email + * @param isAdmin + */ + public CKanUserWrapper(String id, String name, String apiKey, + long creationTimestamp, String about, String openId, + String fullName, String email, boolean isAdmin) { + super(); + this.id = id; + this.name = name; + this.apiKey = apiKey; + this.creationTimestamp = creationTimestamp; + this.about = about; + this.openId = openId; + this.fullName = fullName; + this.email = email; + this.isAdmin = isAdmin; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getApiKey() { + return apiKey; + } + + public void setApiKey(String apiKey) { + this.apiKey = apiKey; + } + + public long getCreationTimestamp() { + return creationTimestamp; + } + + public void setCreationTimestamp(long creationTimestamp) { + this.creationTimestamp = creationTimestamp; + } + + public String getAbout() { + return about; + } + + public void setAbout(String about) { + this.about = about; + } + + public String getOpenId() { + return openId; + } + + public void setOpenId(String openId) { + this.openId = openId; + } + + public String getFullName() { + return fullName; + } + + public void setFullName(String fullName) { + this.fullName = fullName; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public boolean isAdmin() { + return isAdmin; + } + + public void setAdmin(boolean isAdmin) { + this.isAdmin = isAdmin; + } + + @Override + public String toString() { + return "CKanUserExtended [id=" + id + ", name=" + name + ", apiKey=" + apiKey.substring(0, 5) + "****************" + + ", creationTimestamp=" + creationTimestamp + ", about=" + + about + ", openId=" + openId + ", fullName=" + fullName + + ", email=" + email + ", isAdmin=" + isAdmin + "]"; + } + +} diff --git a/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/CkanDatasetRelationship.java b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/CkanDatasetRelationship.java new file mode 100644 index 0000000..c5d12d8 --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/CkanDatasetRelationship.java @@ -0,0 +1,96 @@ +package org.gcube.datacatalogue.ckanutillibrary.shared; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.json.simple.JSONObject; + +/** + * A ckan dataset relationship. It is represented by the following fields + *
    + *
  • subject dataset id + *
  • object dataset id + *
  • type of the relationship + *
  • comment an optional comment + *
+ * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class CkanDatasetRelationship { + + private String subject; + private String object; + private String comment; + private String type; + + + public CkanDatasetRelationship(){ + super(); + } + + /** + * @param subject + * @param object + * @param comment + * @param type + */ + public CkanDatasetRelationship(String subject, String object, + String comment, String type) { + super(); + this.subject = subject; + this.object = object; + this.comment = comment; + this.type = type; + } + + /** + * From a json object that must have the properties listed in the class header (comment is optional) + * @param object + */ + public CkanDatasetRelationship(JSONObject object) { + this.comment = (String) object.get("comment"); + this.object = (String) object.get("object"); + this.subject = (String) object.get("subject"); + this.type = (String) object.get("type"); // TODO convert to one of enums DatasetRelationships + checkNotNull(object); + checkNotNull(subject); + checkNotNull(type); + } + + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public String getObject() { + return object; + } + + public void setObject(String object) { + this.object = object; + } + + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + @Override + public String toString() { + return "CkanDatasetRelationship [subject=" + subject + ", object=" + + object + ", comment=" + comment + ", type=" + type + + "]"; + } +} diff --git a/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/DatasetRelationships.java b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/DatasetRelationships.java new file mode 100644 index 0000000..657fb09 --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/DatasetRelationships.java @@ -0,0 +1,27 @@ +package org.gcube.datacatalogue.ckanutillibrary.shared; + +/** + * Allowed relationships between packages(datasets). Some of them are not supported yet due to the problem + * reported here https://support.d4science.org/issues/4455 + *
    + *
  • depends_on + *
  • dependency_of + *
  • derives_from + *
  • has_derivation + *
  • child_of + *
  • parent_of + *
  • links_to + *
  • linked_from + *
+ * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public enum DatasetRelationships { + depends_on, + dependency_of, + derives_from, + has_derivation, + child_of, + parent_of, + links_to, + linked_from +} diff --git a/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/LandingPages.java b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/LandingPages.java new file mode 100644 index 0000000..2e7695e --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/LandingPages.java @@ -0,0 +1,57 @@ +package org.gcube.datacatalogue.ckanutillibrary.shared; + +import java.io.Serializable; + +/** + * This bean offers the following information: + *
    + *
  • landing page url to the items page (within the portlet) + *
  • landing page url to the orgs page (within the portlet) + *
  • landing page url to the groups page (within the portlet) + *
  • landing page url to the types page (within the portlet) + *
+ * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class LandingPages implements Serializable { + + private static final long serialVersionUID = -5617896049674346109L; + private String urlTypes; + private String urlOrganizations; + private String urlGroups; + private String urlItems; + + public LandingPages() { + super(); + } + public String getUrlTypes() { + return urlTypes; + } + public void setUrlTypes(String urlTypes) { + this.urlTypes = urlTypes; + } + public String getUrlOrganizations() { + return urlOrganizations; + } + public void setUrlOrganizations(String urlOrganizations) { + this.urlOrganizations = urlOrganizations; + } + public String getUrlGroups() { + return urlGroups; + } + public void setUrlGroups(String urlGroups) { + this.urlGroups = urlGroups; + } + public String getUrlItems() { + return urlItems; + } + public void setUrlItems(String urlItems) { + this.urlItems = urlItems; + } + @Override + public String toString() { + return "LandingPages [urlTypes=" + urlTypes + ", urlOrganizations=" + + urlOrganizations + ", urlGroups=" + urlGroups + ", urlItems=" + + urlItems + "]"; + } + +} diff --git a/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/ResourceBean.java b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/ResourceBean.java new file mode 100644 index 0000000..2b124c3 --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/ResourceBean.java @@ -0,0 +1,152 @@ +package org.gcube.datacatalogue.ckanutillibrary.shared; + +import java.io.Serializable; + +/** + * A bean that resembles the CKanResource bean object + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class ResourceBean implements Serializable { + + private static final long serialVersionUID = -5275448097250176185L; + private String url; + private String name; + private String description; + private String id; + private String owner; + private String datasetId; + private String mimeType; + + public ResourceBean(){ + super(); + } + + /** + * @param url + * @param name + * @param description + * @param id + * @param owner + * @param datasetId + * @param mimeType + */ + public ResourceBean(String url, String name, String description, String id, + String owner, String datasetId, String mimeType) { + super(); + this.url = url; + this.name = name; + this.description = description; + this.id = id; + this.owner = owner; + this.datasetId = datasetId; + this.mimeType = mimeType; + } + + /** + * @return the url + */ + public String getUrl() { + return url; + } + + /** + * @param url the url to set + */ + public void setUrl(String url) { + this.url = url; + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the description + */ + public String getDescription() { + return description; + } + + /** + * @param description the description to set + */ + public void setDescription(String description) { + this.description = description; + } + + /** + * @return the id + */ + public String getId() { + return id; + } + + /** + * @param id the id to set + */ + public void setId(String id) { + this.id = id; + } + + /** + * @return the owner + */ + public String getOwner() { + return owner; + } + + /** + * @param owner the owner to set + */ + public void setOwner(String owner) { + this.owner = owner; + } + + /** + * @return the datasetId + */ + public String getDatasetId() { + return datasetId; + } + + /** + * @param datasetId the datasetId to set + */ + public void setDatasetId(String datasetId) { + this.datasetId = datasetId; + } + + /** + * @return the mimeType + */ + public String getMimeType() { + return mimeType; + } + + /** + * @param mimeType the mimeType to set + */ + public void setMimeType(String mimeType) { + this.mimeType = mimeType; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "ResourceBean [url=" + url + ", name=" + name + ", description=" + + description + ", id=" + id + ", owner=" + owner + + ", datasetId=" + datasetId + ", mimeType=" + mimeType + "]"; + } +} diff --git a/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/RolesCkanGroupOrOrg.java b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/RolesCkanGroupOrOrg.java new file mode 100644 index 0000000..196b655 --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/RolesCkanGroupOrOrg.java @@ -0,0 +1,39 @@ +package org.gcube.datacatalogue.ckanutillibrary.shared; + +/** + * Roles that user can have into organizations/groups. + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public enum RolesCkanGroupOrOrg{ + MEMBER, + EDITOR, + ADMIN; + + public static String convertToCkanCapacity(RolesCkanGroupOrOrg role){ + + if(role == null) + return null; + else + return role.toString().toLowerCase(); + + } + + public static RolesCkanGroupOrOrg convertFromCapacity(String capacity){ + + if(capacity == null) + return null; + else + return RolesCkanGroupOrOrg.valueOf(capacity.toUpperCase()); + + } + + /** + * Get the higher role between role1 and role2 + * @param role1 + * @param role2 + * @return the higher role + */ + public static RolesCkanGroupOrOrg getHigher(RolesCkanGroupOrOrg role1, RolesCkanGroupOrOrg role2){ + return role1.ordinal() > role2.ordinal() ? role1 : role2; + } +} \ No newline at end of file diff --git a/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/State.java b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/State.java new file mode 100644 index 0000000..ddc8fc2 --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/State.java @@ -0,0 +1,10 @@ +package org.gcube.datacatalogue.ckanutillibrary.shared; + +/** + * The current state of this group/user + * @author Costantino Perciante at ISTI-CNR + * (costantino.perciante@isti.cnr.it) + */ +public enum State{ + DELETED, ACTIVE +} \ No newline at end of file diff --git a/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/Statistics.java b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/Statistics.java new file mode 100644 index 0000000..fee060d --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/Statistics.java @@ -0,0 +1,54 @@ +package org.gcube.datacatalogue.ckanutillibrary.shared; + +import java.io.Serializable; + +/** + * This bean offers the following statistics: + *
    + *
  • number of items of the catalogue + *
  • number of organizations of the catalogue + *
  • number of groups of the catalogue + *
  • number of types of the catalogue + *
+ * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class Statistics implements Serializable{ + + private static final long serialVersionUID = 2871906712366452266L; + private long numTypes; + private long numOrganizations; + private long numGroups; + private long numItems; + + public long getNumTypes() { + return numTypes; + } + public void setNumTypes(long numTypes) { + this.numTypes = numTypes; + } + public long getNumOrganizations() { + return numOrganizations; + } + public void setNumOrganizations(long numOrganizations) { + this.numOrganizations = numOrganizations; + } + public long getNumGroups() { + return numGroups; + } + public void setNumGroups(long numGroups) { + this.numGroups = numGroups; + } + public long getNumItems() { + return numItems; + } + public void setNumItems(long numItems) { + this.numItems = numItems; + } + @Override + public String toString() { + return "Statistics [numTypes=" + numTypes + ", numOrganizations=" + + numOrganizations + ", numGroups=" + numGroups + ", numItems=" + + numItems + "]"; + } + +} diff --git a/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/ex/ApplicationProfileNotFoundException.java b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/ex/ApplicationProfileNotFoundException.java new file mode 100644 index 0000000..384b652 --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/ex/ApplicationProfileNotFoundException.java @@ -0,0 +1,12 @@ +package org.gcube.datacatalogue.ckanutillibrary.shared.ex; + +@SuppressWarnings("serial") +/** + * Thrown when no application profile with such information is found + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class ApplicationProfileNotFoundException extends Exception { + public ApplicationProfileNotFoundException(String message) { + super(message); + } +} diff --git a/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/ex/NoApplicationProfileMasterException.java b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/ex/NoApplicationProfileMasterException.java new file mode 100644 index 0000000..feca2ae --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/ex/NoApplicationProfileMasterException.java @@ -0,0 +1,20 @@ +package org.gcube.datacatalogue.ckanutillibrary.shared.ex; + +/** + * Thrown when there are more than one application profile, but none of them was set as master + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class NoApplicationProfileMasterException extends Exception { + + private static final long serialVersionUID = 5874713540422734005L; + private static final String DEFAULT_MESSAGE = "There is more than one application profile into this scope" + + " but none of them is set as master!"; + + public NoApplicationProfileMasterException(){ + super(DEFAULT_MESSAGE); + } + + public NoApplicationProfileMasterException(String message) { + super(message); + } +} diff --git a/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/ex/NoDataCatalogueRuntimeResourceException.java b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/ex/NoDataCatalogueRuntimeResourceException.java new file mode 100644 index 0000000..1f35604 --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/ex/NoDataCatalogueRuntimeResourceException.java @@ -0,0 +1,21 @@ +package org.gcube.datacatalogue.ckanutillibrary.shared.ex; + +/** + * No Data Catalogue node found. + * @author Costantino Perciante at ISTI-CNR + * (costantino.perciante@isti.cnr.it) + */ +public class NoDataCatalogueRuntimeResourceException extends Exception { + + private static final long serialVersionUID = -40748130477807648L; + + private static final String DEFAULT_MESSAGE = "No Data Catalogue instance for this scope!"; + + public NoDataCatalogueRuntimeResourceException(){ + super(DEFAULT_MESSAGE); + } + + public NoDataCatalogueRuntimeResourceException(String message) { + super(message); + } +} diff --git a/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/ex/ServiceEndPointException.java b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/ex/ServiceEndPointException.java new file mode 100644 index 0000000..bfcecaf --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/ex/ServiceEndPointException.java @@ -0,0 +1,19 @@ +package org.gcube.datacatalogue.ckanutillibrary.shared.ex; + +/** + * Exception thrown when it is not possible retrieve information from the ServiceEndpoint + * related to the Data Catalogue + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class ServiceEndPointException extends Exception { + + private static final long serialVersionUID = 7057074369001221035L; + private static final String DEFAULT_MESSAGE = "Unable to retrieve information from Data Catalogue endpoint!"; + + public ServiceEndPointException(){ + super(DEFAULT_MESSAGE); + } + public ServiceEndPointException(String string) { + super(string); + } +} diff --git a/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/ex/TooManyRunningClustersException.java b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/ex/TooManyRunningClustersException.java new file mode 100644 index 0000000..4f73f02 --- /dev/null +++ b/src/main/java/org/gcube/datacatalogue/ckanutillibrary/shared/ex/TooManyRunningClustersException.java @@ -0,0 +1,22 @@ +package org.gcube.datacatalogue.ckanutillibrary.shared.ex; + +/** + * Too many clusters in this scope exception. + * @author Costantino Perciante at ISTI-CNR + * (costantino.perciante@isti.cnr.it) + * + */ +public class TooManyRunningClustersException extends Exception { + + private static final long serialVersionUID = -7847493730006647045L; + private static final String DEFAULT_MESSAGE = "Too many CKan data catalague instances for this scope!"; + + public TooManyRunningClustersException(){ + super(DEFAULT_MESSAGE); + } + + public TooManyRunningClustersException(String message) { + super(message); + } + +} diff --git a/target/classes/META-INF/MANIFEST.MF b/target/classes/META-INF/MANIFEST.MF new file mode 100644 index 0000000..0048ac8 --- /dev/null +++ b/target/classes/META-INF/MANIFEST.MF @@ -0,0 +1,15 @@ +Manifest-Version: 1.0 +Built-By: francesco +Build-Jdk: 1.8.0_252 +Specification-Title: CKan utility library +Specification-Version: 0.1.0-SNAPSHOT +Implementation-Title: CKan utility library +Implementation-Version: 0.1.0-SNAPSHOT +Implementation-Vendor-Id: org.gcube.datacatalogue +Build-Time: 20200601-134620 +Created-By: Maven Integration for Eclipse +SCM-Branch: +SCM-Revision: +SCM-Revision-URL: https://code-repo.d4science.org/gCubeSystem/catalogu + e-util-library/commit/${buildNumber} + diff --git a/target/classes/META-INF/maven/org.gcube.datacatalogue/catalogue-util-library/pom.properties b/target/classes/META-INF/maven/org.gcube.datacatalogue/catalogue-util-library/pom.properties new file mode 100644 index 0000000..c1f2a7e --- /dev/null +++ b/target/classes/META-INF/maven/org.gcube.datacatalogue/catalogue-util-library/pom.properties @@ -0,0 +1,7 @@ +#Generated by Maven Integration for Eclipse +#Mon Jun 01 15:46:21 CEST 2020 +version=0.1.0-SNAPSHOT +groupId=org.gcube.datacatalogue +m2e.projectName=catalogue-util-library +m2e.projectLocation=/home/francesco/git/catalogue-util-library +artifactId=catalogue-util-library diff --git a/target/classes/META-INF/maven/org.gcube.datacatalogue/catalogue-util-library/pom.xml b/target/classes/META-INF/maven/org.gcube.datacatalogue/catalogue-util-library/pom.xml new file mode 100644 index 0000000..668791f --- /dev/null +++ b/target/classes/META-INF/maven/org.gcube.datacatalogue/catalogue-util-library/pom.xml @@ -0,0 +1,228 @@ + + 4.0.0 + + + maven-parent + org.gcube.tools + 1.1.0 + + + + org.gcube.datacatalogue + catalogue-util-library + 0.1.0-SNAPSHOT + + CKan utility library + + Utility library to retrieve users information, organizations information and so on from the ckan d4science datacatalogue + + + + scm:git:https://code-repo.d4science.org/gCubeSystem/${project.artifactId}.git + scm:git:https://code-repo.d4science.org/gCubeSystem/${project.artifactId}.git + https://code-repo.d4science.org/gCubeSystem/${project.artifactId} + + + + data-catalogue + 0.4.2 + 9.4.1208.jre7 + UTF-8 + UTF-8 + 2.7.0 + + + + + + org.gcube.distribution + maven-portal-bom + LATEST + pom + import + + + + + + + com.google.gwt + gwt-user + provided + + + + + eu.trentorise.opendata + jackan + ${jackanVersion} + compile + + + + org.gcube.data-publishing + gcat-client + [1.0.0, 2.0.0-SNAPSHOT) + compile + + + + + + + + + + + junit + junit + 4.8 + test + + + org.gcube.common.portal + portal-manager + provided + + + org.gcube.resources.discovery + ic-client + provided + + + org.gcube.core + common-scope-maps + provided + + + + org.gcube.core + common-encryption + provided + + + org.gcube.dvos + usermanagement-core + provided + + + net.htmlparser.jericho + jericho-html + 3.3 + compile + + + com.google.guava + guava + + + com.googlecode.json-simple + json-simple + compile + + + org.apache.solr + solr-solrj + [4.10.2,5.0.0) + + + org.slf4j + slf4j-log4j12 + provided + + + org.slf4j + slf4j-api + provided + + + + + + + src/main/java + + **/*.* + + + + + + maven-compiler-plugin + + + + org.apache.maven.plugins + maven-jar-plugin + 2.2 + + + + test-jar + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.12 + + true + + + + org.apache.maven.plugins + maven-resources-plugin + 2.5 + + + copy-profile + install + + copy-resources + + + target + + + ${distroDirectory} + true + + profile.xml + + + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + ${distroDirectory}/descriptor.xml + + + + fully.qualified.MainClass + + + + + + servicearchive + install + + single + + + + + + + diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/CkanUtilLibrary.gwt.xml b/target/classes/org/gcube/datacatalogue/ckanutillibrary/CkanUtilLibrary.gwt.xml new file mode 100644 index 0000000..a3990ca --- /dev/null +++ b/target/classes/org/gcube/datacatalogue/ckanutillibrary/CkanUtilLibrary.gwt.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/ckan/ExtendCkanClient.class b/target/classes/org/gcube/datacatalogue/ckanutillibrary/ckan/ExtendCkanClient.class new file mode 100644 index 0000000..91f6d88 Binary files /dev/null and b/target/classes/org/gcube/datacatalogue/ckanutillibrary/ckan/ExtendCkanClient.class differ diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/ckan/ExtendCkanClient.java b/target/classes/org/gcube/datacatalogue/ckanutillibrary/ckan/ExtendCkanClient.java new file mode 100644 index 0000000..43a2fce --- /dev/null +++ b/target/classes/org/gcube/datacatalogue/ckanutillibrary/ckan/ExtendCkanClient.java @@ -0,0 +1,240 @@ +/** + * + */ +package org.gcube.datacatalogue.ckanutillibrary.ckan; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URLEncoder; +import java.util.Arrays; + +import javax.annotation.Nullable; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Charsets; +import com.google.common.io.CharStreams; + +import eu.trentorise.opendata.jackan.CheckedCkanClient; +import eu.trentorise.opendata.jackan.exceptions.CkanException; +import eu.trentorise.opendata.jackan.exceptions.JackanException; +import eu.trentorise.opendata.jackan.internal.org.apache.http.client.fluent.Request; +import eu.trentorise.opendata.jackan.internal.org.apache.http.client.fluent.Response; +import eu.trentorise.opendata.jackan.model.CkanGroup; +import eu.trentorise.opendata.jackan.model.CkanOrganization; +import eu.trentorise.opendata.jackan.model.CkanResponse; + + +/** + * The Class ExtendCkanClient. + * + * @author Francesco Mangiacrapa at ISTI-CNR (francesco.mangiacrapa@isti.cnr.it) + * + * Jun 14, 2019 + */ +public class ExtendCkanClient extends CheckedCkanClient implements PatchedCkan{ + + private static final Logger logger = LoggerFactory.getLogger(ExtendCkanClient.class); + + @Nullable + private static ObjectMapper objectMapper; + + private String catalogueURL; + private String ckanToken; + private int extendTimeout = 120000; //in milliseconds + + + /** + * Instantiates a new extend ckan client. + * + * @param catalogueURL the catalogue url + */ + public ExtendCkanClient(String catalogueURL) { + super(catalogueURL); + this.catalogueURL = catalogueURL; + + } + + /** + * Instantiates a new extend ckan client. + * + * @param catalogueURL the catalogue url + * @param ckanToken the ckan token + */ + public ExtendCkanClient(String catalogueURL, @Nullable String ckanToken) { + super(catalogueURL, ckanToken); + this.catalogueURL = catalogueURL; + this.ckanToken = ckanToken; + } + + /** + * Instantiates a new extend ckan client. + * + * @param catalogueURL the catalogue url + * @param ckanToken the ckan token + * @param timeout the timeout + */ + public ExtendCkanClient(String catalogueURL, @Nullable String ckanToken, int timeout) { + super(catalogueURL, ckanToken); + this.catalogueURL = catalogueURL; + this.ckanToken = ckanToken; + this.extendTimeout = timeout; + } + + + /** + * Configures the request. Should work both for GETs and POSTs. + * + * @param request the request + * @return the request + */ + @Override + protected Request configureRequest(Request request) { + request = super.configureRequest(request); + + logger.debug("Setting timeout to {}", extendTimeout); + + request.socketTimeout(this.extendTimeout) + .connectTimeout(this.extendTimeout); + + return request; + } + + /** + * Gets the catalogue url. + * + * @return the catalogueURL + */ + public String getCatalogueURL() { + + return catalogueURL; + } + + + /** + * Gets the ckan token. + * + * @return the ckanToken + */ + @Override + public String getCkanToken() { + + return ckanToken; + } + + /** + * Gets the timeout. + * + * @return the timeout + */ + public int getTimeout() { + return extendTimeout; + } + + /* (non-Javadoc) + * @see eu.trentorise.opendata.jackan.CkanClient#getOrganization(java.lang.String) + */ + public synchronized CkanOrganization getOrganization(String idOrName) { + checkNotNull(idOrName, "Need a valid id or name!"); + logger.info("Patched read organization for id/name: {}", idOrName); + return getHttp(OrganizationResponse.class, "/api/3/action/organization_show", "id", idOrName, + "include_datasets", "false", "include_users", "true").result; + + } + + + /* (non-Javadoc) + * @see eu.trentorise.opendata.jackan.CkanClient#getGroup(java.lang.String) + */ + public synchronized CkanGroup getGroup(String idOrName) { + checkNotNull(idOrName, "Need a valid id or name!"); + logger.info("Patched read group for id/name: {}", idOrName); + return getHttp(GroupResponse.class, "/api/3/action/group_show", "id", idOrName, "include_datasets", + "false", "include_users", "true").result; + } + + + /** + * Retrieves the Jackson object mapper for reading operations. Internally, + * Object mapper is initialized at first call. + * + * @return the object mapper + */ + static ObjectMapper getObjectMapper() { + if (objectMapper == null) { + objectMapper = new ObjectMapper(); + configureObjectMapper(objectMapper); + } + return objectMapper; + } + + /* (non-Javadoc) + * @see org.gcube.datacatalogue.ckanutillibrary.server.patch.PatchedCkan#getHttp(java.lang.Class, java.lang.String, java.lang.Object[]) + */ + public T getHttp(Class responseType, String path, Object... params) { + + checkNotNull(responseType); + checkNotNull(path); + String fullUrl = calcFullUrl(path, params); + T ckanResponse; + String returnedText; + + try { + + logger.debug("getting {}", fullUrl); + Request request = Request.Get(fullUrl); + configureRequest(request); + Response response = request.execute(); + InputStream stream = response.returnResponse() + .getEntity() + .getContent(); + + try (InputStreamReader reader = new InputStreamReader(stream, Charsets.UTF_8)) { + returnedText = CharStreams.toString(reader); + } + + logger.trace("returnedText {}", returnedText); + } catch (Exception ex) { + throw new CkanException("Error while performing GET. Request url was: " + fullUrl, this, ex); + } + try { + ckanResponse = getObjectMapper().readValue(returnedText, responseType); + } catch (Exception ex) { + throw new CkanException( + "Couldn't interpret json returned by the server! Returned text was: " + returnedText, this, ex); + } + + if (!ckanResponse.isSuccess()) { + throwCkanException("Error while performing GET. Request url was: " + fullUrl, ckanResponse); + } + return ckanResponse; + } + + + /* (non-Javadoc) + * @see org.gcube.datacatalogue.ckanutillibrary.server.patch.PatchedCkan#calcFullUrl(java.lang.String, java.lang.Object[]) + */ + public String calcFullUrl(String path, Object[] params) { + checkNotNull(path); + + try { + StringBuilder sb = new StringBuilder().append(catalogueURL) + .append(path); + for (int i = 0; i < params.length; i += 2) { + sb.append(i == 0 ? "?" : "&") + .append(URLEncoder.encode(params[i].toString(), "UTF-8")) + .append("=") + .append(URLEncoder.encode(params[i + 1].toString(), "UTF-8")); + } + return sb.toString(); + } catch (Exception ex) { + throw new JackanException("Error while building url to perform GET! \n path: " + path + " \n params: " + + Arrays.toString(params), ex); + } + } + +} diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/ckan/PatchedCkan$GroupResponse.class b/target/classes/org/gcube/datacatalogue/ckanutillibrary/ckan/PatchedCkan$GroupResponse.class new file mode 100644 index 0000000..16dfde1 Binary files /dev/null and b/target/classes/org/gcube/datacatalogue/ckanutillibrary/ckan/PatchedCkan$GroupResponse.class differ diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/ckan/PatchedCkan$OrganizationResponse.class b/target/classes/org/gcube/datacatalogue/ckanutillibrary/ckan/PatchedCkan$OrganizationResponse.class new file mode 100644 index 0000000..c5f2fb1 Binary files /dev/null and b/target/classes/org/gcube/datacatalogue/ckanutillibrary/ckan/PatchedCkan$OrganizationResponse.class differ diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/ckan/PatchedCkan.class b/target/classes/org/gcube/datacatalogue/ckanutillibrary/ckan/PatchedCkan.class new file mode 100644 index 0000000..ef5b28d Binary files /dev/null and b/target/classes/org/gcube/datacatalogue/ckanutillibrary/ckan/PatchedCkan.class differ diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/ckan/PatchedCkan.java b/target/classes/org/gcube/datacatalogue/ckanutillibrary/ckan/PatchedCkan.java new file mode 100644 index 0000000..b7501fd --- /dev/null +++ b/target/classes/org/gcube/datacatalogue/ckanutillibrary/ckan/PatchedCkan.java @@ -0,0 +1,113 @@ +package org.gcube.datacatalogue.ckanutillibrary.ckan; + +import eu.trentorise.opendata.jackan.CkanClient; +import eu.trentorise.opendata.jackan.exceptions.CkanException; +import eu.trentorise.opendata.jackan.exceptions.JackanException; +import eu.trentorise.opendata.jackan.model.CkanGroup; +import eu.trentorise.opendata.jackan.model.CkanOrganization; +import eu.trentorise.opendata.jackan.model.CkanResponse; + +/** + * The Interface PatchedCkan. + * + * @author Francesco Mangiacrapa at ISTI-CNR (francesco.mangiacrapa@isti.cnr.it) + * + * Jun 14, 2019 + */ +public interface PatchedCkan { + + /** + * + * This Class is private in {@link CkanClient} so I need to rewrite it + * + * The Class OrganizationResponse. + * + * @author Francesco Mangiacrapa at ISTI-CNR (francesco.mangiacrapa@isti.cnr.it) + * + * Jun 7, 2019 + */ + static class OrganizationResponse extends CkanResponse { + + public CkanOrganization result; + + } + + /** + * + * + * This Class is private in {@link CkanClient} so I need to rewrite it + * + * + * The Class GroupResponse. + * + * @author Francesco Mangiacrapa at ISTI-CNR (francesco.mangiacrapa@isti.cnr.it) + * + * Jun 7, 2019 + */ + static class GroupResponse extends CkanResponse { + + public CkanGroup result; + + /** + * Instantiates a new group response. + */ + public GroupResponse() { + + } + } + + /** + * + * This method is private in {@link CkanClient} so I need to rewrite it + * + * + * + * Performs HTTP GET on server. If {@link CkanResponse#isSuccess()} is false + * throws {@link CkanException}. + * + * @param the generic type + * @param responseType a descendant of CkanResponse + * @param path something like /api/3/package_show + * @param params list of key, value parameters. They must be not be url + * encoded. i.e. "id","laghi-monitorati-trento" + * @return the http + * @throws CkanException on error + */ + public T getHttp(Class responseType, String path, Object... params); + + /** + * + * This method is private in {@link CkanClient} so I need to rewrite it + * + * + * Calculates a full url out of the provided params. + * + * @param path something like /api/3/package_show + * @param params list of key, value parameters. They must be not be url encoded. + * i.e. "id","laghi-monitorati-trento" + * @return the full url to be called. + * @throws JackanException if there is any error building the url + */ + public String calcFullUrl(String path, Object[] params); + + + + // WE NEED TO OVERRIDE THE FOLLOWING METHODS + + /** + * Gets the organization. + * + * @param idOrName the id or name + * @return the organization + */ + public CkanOrganization getOrganization(String idOrName); + + /** + * Gets the group. + * + * @param idOrName the id or name + * @return the group + */ + public CkanGroup getGroup(String idOrName); + +} diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/ckan/SimpleExtendCkanClient.class b/target/classes/org/gcube/datacatalogue/ckanutillibrary/ckan/SimpleExtendCkanClient.class new file mode 100644 index 0000000..bd71c39 Binary files /dev/null and b/target/classes/org/gcube/datacatalogue/ckanutillibrary/ckan/SimpleExtendCkanClient.class differ diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/ckan/SimpleExtendCkanClient.java b/target/classes/org/gcube/datacatalogue/ckanutillibrary/ckan/SimpleExtendCkanClient.java new file mode 100644 index 0000000..93c59cd --- /dev/null +++ b/target/classes/org/gcube/datacatalogue/ckanutillibrary/ckan/SimpleExtendCkanClient.java @@ -0,0 +1,168 @@ +package org.gcube.datacatalogue.ckanutillibrary.ckan; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URLEncoder; +import java.util.Arrays; + +import javax.annotation.Nullable; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Charsets; +import com.google.common.io.CharStreams; + +import eu.trentorise.opendata.jackan.CkanClient; +import eu.trentorise.opendata.jackan.exceptions.CkanException; +import eu.trentorise.opendata.jackan.exceptions.JackanException; +import eu.trentorise.opendata.jackan.internal.org.apache.http.client.fluent.Request; +import eu.trentorise.opendata.jackan.internal.org.apache.http.client.fluent.Response; +import eu.trentorise.opendata.jackan.model.CkanGroup; +import eu.trentorise.opendata.jackan.model.CkanOrganization; +import eu.trentorise.opendata.jackan.model.CkanResponse; + + +/** + * The Class SimpleExtendCkanClient. + * + * @author Francesco Mangiacrapa at ISTI-CNR (francesco.mangiacrapa@isti.cnr.it) + * + * Jun 7, 2019 + */ +public class SimpleExtendCkanClient extends CkanClient implements PatchedCkan{ + + + private String catalogueURL; + + @Nullable + private static ObjectMapper objectMapper; + + private static final Logger logger = LoggerFactory.getLogger(SimpleExtendCkanClient.class); + + /** + * Instantiates a new simple extend ckan client. + * + * @param catalogueURL the catalogue URL + */ + public SimpleExtendCkanClient(String catalogueURL) { + super(catalogueURL); + this.catalogueURL = catalogueURL; + } + + /* (non-Javadoc) + * @see eu.trentorise.opendata.jackan.CkanClient#getOrganization(java.lang.String) + */ + public synchronized CkanOrganization getOrganization(String idOrName) { + checkNotNull(idOrName, "Need a valid id or name!"); + logger.info("Patched read organization for id/name: {}", idOrName); + return getHttp(OrganizationResponse.class, "/api/3/action/organization_show", "id", idOrName, + "include_datasets", "false", "include_users", "true").result; + + } + + + /* (non-Javadoc) + * @see eu.trentorise.opendata.jackan.CkanClient#getGroup(java.lang.String) + */ + public synchronized CkanGroup getGroup(String idOrName) { + checkNotNull(idOrName, "Need a valid id or name!"); + logger.info("Patched read group for id/name: {}", idOrName); + return getHttp(GroupResponse.class, "/api/3/action/group_show", "id", idOrName, "include_datasets", + "false", "include_users", "true").result; + } + + + /** + * Retrieves the Jackson object mapper for reading operations. Internally, + * Object mapper is initialized at first call. + * + * @return the object mapper + */ + public static ObjectMapper getObjectMapper() { + if (objectMapper == null) { + objectMapper = new ObjectMapper(); + configureObjectMapper(objectMapper); + } + return objectMapper; + } + + /* (non-Javadoc) + * @see org.gcube.datacatalogue.ckanutillibrary.server.patch.PatchedCkan#getHttp(java.lang.Class, java.lang.String, java.lang.Object[]) + */ + public T getHttp(Class responseType, String path, Object... params) { + + checkNotNull(responseType); + checkNotNull(path); + String fullUrl = calcFullUrl(path, params); + T ckanResponse; + String returnedText; + + try { + + logger.debug("getting {}", fullUrl); + Request request = Request.Get(fullUrl); + configureRequest(request); + Response response = request.execute(); + InputStream stream = response.returnResponse() + .getEntity() + .getContent(); + + try (InputStreamReader reader = new InputStreamReader(stream, Charsets.UTF_8)) { + returnedText = CharStreams.toString(reader); + } + + logger.trace("returnedText {}", returnedText); + } catch (Exception ex) { + throw new CkanException("Error while performing GET. Request url was: " + fullUrl, this, ex); + } + try { + ckanResponse = getObjectMapper().readValue(returnedText, responseType); + } catch (Exception ex) { + throw new CkanException( + "Couldn't interpret json returned by the server! Returned text was: " + returnedText, this, ex); + } + + if (!ckanResponse.isSuccess()) { + throwCkanException("Error while performing GET. Request url was: " + fullUrl, ckanResponse); + } + return ckanResponse; + } + + + /* (non-Javadoc) + * @see org.gcube.datacatalogue.ckanutillibrary.server.patch.PatchedCkan#calcFullUrl(java.lang.String, java.lang.Object[]) + */ + public String calcFullUrl(String path, Object[] params) { + checkNotNull(path); + + try { + StringBuilder sb = new StringBuilder().append(catalogueURL) + .append(path); + for (int i = 0; i < params.length; i += 2) { + sb.append(i == 0 ? "?" : "&") + .append(URLEncoder.encode(params[i].toString(), "UTF-8")) + .append("=") + .append(URLEncoder.encode(params[i + 1].toString(), "UTF-8")); + } + return sb.toString(); + } catch (Exception ex) { + throw new JackanException("Error while building url to perform GET! \n path: " + path + " \n params: " + + Arrays.toString(params), ex); + } + } + + /** + * Gets the catalogue url. + * + * @return the catalogueURL + */ + public String getCatalogueURL() { + + return catalogueURL; + } + +} diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/db/DBCaller.class b/target/classes/org/gcube/datacatalogue/ckanutillibrary/db/DBCaller.class new file mode 100644 index 0000000..ab42840 Binary files /dev/null and b/target/classes/org/gcube/datacatalogue/ckanutillibrary/db/DBCaller.class differ diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/db/DBCaller.java b/target/classes/org/gcube/datacatalogue/ckanutillibrary/db/DBCaller.java new file mode 100644 index 0000000..7be49c9 --- /dev/null +++ b/target/classes/org/gcube/datacatalogue/ckanutillibrary/db/DBCaller.java @@ -0,0 +1,209 @@ +package org.gcube.datacatalogue.ckanutillibrary.db; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; + +import org.gcube.datacatalogue.ckanutillibrary.shared.RolesCkanGroupOrOrg; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DBCaller { + + private static final Logger LOG = LoggerFactory.getLogger(DBCaller.class); + + private String CKAN_DB_URL; + private Integer CKAN_DB_PORT; + private String CKAN_DB_NAME; + private String CKAN_DB_USER; + private String CKAN_DB_PASSWORD; + + public DBCaller() { + } + + public DBCaller(String cKAN_DB_URL, Integer cKAN_DB_PORT, String cKAN_DB_NAME, String cKAN_DB_USER, + String cKAN_DB_PASSWORD) { + CKAN_DB_URL = cKAN_DB_URL; + CKAN_DB_PORT = cKAN_DB_PORT; + CKAN_DB_NAME = cKAN_DB_NAME; + CKAN_DB_USER = cKAN_DB_USER; + CKAN_DB_PASSWORD = cKAN_DB_PASSWORD; + } + + + /** + * Retrieve connection from the pool + * @return a connection available within the pool + * @throws SQLException + * @throws ClassNotFoundException + */ + private Connection getConnection() throws SQLException, ClassNotFoundException{ + + LOG.debug("CONNECTION REQUEST"); + // create db connection + Class.forName("org.postgresql.Driver"); + + String dbBaseConnectionURL = String.format("jdbc:postgresql://%s", CKAN_DB_URL); + + if(CKAN_DB_PORT!=null) + dbBaseConnectionURL+=":" + CKAN_DB_PORT; + + dbBaseConnectionURL+="/" + CKAN_DB_NAME; + + Connection connection = DriverManager.getConnection(dbBaseConnectionURL, CKAN_DB_USER, CKAN_DB_PASSWORD); + +// Connection connection = DriverManager.getConnection( +// "jdbc:postgresql://" + CKAN_DB_URL + ":" + CKAN_DB_PORT + "/" + CKAN_DB_NAME, CKAN_DB_USER, CKAN_DB_PASSWORD); + return connection; + + } + + /** + * Retrieve the map (groups id, capacity) with the groups to which the user belongs by querying directly the database. + * @param username + * @return + * @throws SQLException + * @throws ClassNotFoundException + */ + public Map getGroupsByUserFromDB(String userId){ + checkNotNull(userId); + + //couples (groups id, capacity) of the user in the group + + Map toReturn = new HashMap(); + Connection connection = null; + try{ + + connection = getConnection(); + ResultSet rs; + + String joinQuery = "SELECT \"group_id\",\"capacity\" FROM \"public\".\"member\" " + + "JOIN \"public\".\"group\" ON \"member\".\"group_id\" = \"group\".\"id\" where \"table_id\"=?" + + " and \"table_name\"='user' and \"member\".\"state\"='active' and \"group\".\"state\"='active' and \"group\".\"is_organization\"=?;"; + + + PreparedStatement preparedStatement = connection.prepareStatement(joinQuery); + preparedStatement.setString(1, userId); + preparedStatement.setBoolean(2, false); + rs = preparedStatement.executeQuery(); + + while (rs.next()) { + + toReturn.put(rs.getString("group_id"), RolesCkanGroupOrOrg.convertFromCapacity(rs.getString("capacity"))); + } + + }catch(Exception e){ + LOG.error("Failed to retrieve the groups to whom the user belongs. Error is " + e.getMessage()); + return null; + }finally{ + closeConnection(connection); + } + return toReturn; + } + + /** + * Retrieve the map (organisation id, capacity) with the organisations to which the user belongs by querying directly the database. + * @param username + * @return + * @throws SQLException + * @throws ClassNotFoundException + */ + public Map getOrganizationsByUserFromDB(String userId){ + checkNotNull(userId); + + Map toReturn = new HashMap(); + Connection connection = null; + try{ + + connection = getConnection(); + ResultSet rs; + + String joinQuery = "SELECT \"group_id\",\"capacity\" FROM \"public\".\"member\" " + + "JOIN \"public\".\"group\" ON \"member\".\"group_id\" = \"group\".\"id\" where \"table_id\"=?" + + " and \"table_name\"='user' and \"member\".\"state\"='active' and \"group\".\"state\"='active' and \"group\".\"is_organization\"=?;"; + + + PreparedStatement preparedStatement = connection.prepareStatement(joinQuery); + preparedStatement.setString(1, userId); + preparedStatement.setBoolean(2, true); + rs = preparedStatement.executeQuery(); + + while (rs.next()) { + + toReturn.put(rs.getString("group_id"), RolesCkanGroupOrOrg.convertFromCapacity(rs.getString("capacity"))); + } + + }catch(Exception e){ + LOG.error("Failed to retrieve the groups to whom the user belongs. Error is " + e.getMessage()); + return null; + }finally{ + closeConnection(connection); + } + return toReturn; + } + + + public String getApiKeyFromUsername(String username, String state) { + + LOG.debug("Request api key for user = " + username); + + // checks + checkNotNull(username); + checkArgument(!username.isEmpty()); + + // the connection + Connection connection = null; + String apiToReturn = null; + + try{ + + connection = getConnection(); + + String query = "SELECT \"apikey\" FROM \"user\" WHERE \"name\"=? and \"state\"=?;"; + PreparedStatement preparedStatement = connection.prepareStatement(query); + preparedStatement.setString(1, username); + preparedStatement.setString(2, state); + + ResultSet rs = preparedStatement.executeQuery(); + while (rs.next()) { + apiToReturn = rs.getString("apikey"); + LOG.debug("Api key "+apiToReturn.substring(0,10)+" MASKED-TOKEN retrieved for user " + username); + break; + } + + return apiToReturn; + + }catch(Exception e){ + LOG.error("Unable to retrieve key for user " + username, e); + }finally{ + closeConnection(connection); + } + + return null; + } + + + /** + * Tries to close a connection + * @param connection + */ + private void closeConnection(Connection connection){ + + if(connection != null){ + try{ + connection.close(); + }catch(Exception e){ + LOG.error("Unable to close this connection ", e); + } + } + + } + +} diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/gcat/GCatCaller.class b/target/classes/org/gcube/datacatalogue/ckanutillibrary/gcat/GCatCaller.class new file mode 100644 index 0000000..19c51a3 Binary files /dev/null and b/target/classes/org/gcube/datacatalogue/ckanutillibrary/gcat/GCatCaller.class differ diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/gcat/GCatCaller.java b/target/classes/org/gcube/datacatalogue/ckanutillibrary/gcat/GCatCaller.java new file mode 100644 index 0000000..c14eca7 --- /dev/null +++ b/target/classes/org/gcube/datacatalogue/ckanutillibrary/gcat/GCatCaller.java @@ -0,0 +1,69 @@ +package org.gcube.datacatalogue.ckanutillibrary.gcat; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.util.List; + +import org.gcube.datacatalogue.ckanutillibrary.ckan.SimpleExtendCkanClient; +import org.gcube.gcat.client.Item; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.JsonMappingException; + +import eu.trentorise.opendata.jackan.model.CkanDataset; +import eu.trentorise.opendata.jackan.model.CkanLicense; + +// TODO: Auto-generated Javadoc +/** + * The Class GCatCaller. + * + * @author Francesco Mangiacrapa at ISTI-CNR Pisa (Italy) + * May 29, 2020 + */ +public class GCatCaller { + + private SimpleExtendCkanClient jackanCkanClient; + + private String catalogueURL; + + private static final Logger LOG = LoggerFactory.getLogger(GCatCaller.class); + + /** + * Instantiates a new g cat caller. + */ + public GCatCaller(String catalogueURL) { + this.catalogueURL = catalogueURL; + this.jackanCkanClient = new SimpleExtendCkanClient(catalogueURL); + } + + + /** + * Gets the dataset for name. + * + * @param datasetIdOrName the dataset id or name + * @return the dataset for name + * @throws JsonParseException the json parse exception + * @throws JsonMappingException the json mapping exception + * @throws IOException Signals that an I/O exception has occurred. + */ + public CkanDataset getDatasetForName(String datasetIdOrName) throws JsonParseException, JsonMappingException, IOException { + LOG.debug("Get dataset for name "+datasetIdOrName+ "called"); + String json = new Item().read(datasetIdOrName); + return SimpleExtendCkanClient.getObjectMapper().readValue(json, CkanDataset.class); + } + + + /** + * TODO gCAT missing + * @return + * @throws MalformedURLException + */ + + public List getLicenseList() { + LOG.debug("Get linces list called"); + return jackanCkanClient.getLicenseList(); + } + +} diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/ApplicationProfileScopePerUrlReader.class b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/ApplicationProfileScopePerUrlReader.class new file mode 100644 index 0000000..63cb4e9 Binary files /dev/null and b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/ApplicationProfileScopePerUrlReader.class differ diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/ApplicationProfileScopePerUrlReader.java b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/ApplicationProfileScopePerUrlReader.java new file mode 100644 index 0000000..e0d621e --- /dev/null +++ b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/ApplicationProfileScopePerUrlReader.java @@ -0,0 +1,105 @@ +package org.gcube.datacatalogue.ckanutillibrary.server; + +import static org.gcube.resources.discovery.icclient.ICFactory.client; + +import java.io.StringReader; +import java.util.List; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.gcube.common.portal.PortalContext; +import org.gcube.common.resources.gcore.utils.XPathHelper; +import org.gcube.common.scope.api.ScopeProvider; +import org.gcube.datacatalogue.ckanutillibrary.shared.ex.ApplicationProfileNotFoundException; +import org.gcube.resources.discovery.client.api.DiscoveryClient; +import org.gcube.resources.discovery.client.queries.api.Query; +import org.gcube.resources.discovery.client.queries.impl.QueryBox; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Node; +import org.xml.sax.InputSource; + +/** + * This class has a method that tries to read the application profile whose structure contains + * couples Scope/Current_Url to check where the the needed information needs to be discovered according to the current + * portlet url. It means that the scope in which the ckan related information will be discovered could be different wrt the running one. + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class ApplicationProfileScopePerUrlReader { + + private static final Logger logger = LoggerFactory.getLogger(ApplicationProfileScopePerUrlReader.class); + private final static String APPLICATION_PROFILE_NAME = "DataCatalogueMapScopesUrls"; + private final static String QUERY = "for $profile in collection('/db/Profiles/GenericResource')//Resource " + + "where $profile/Profile/SecondaryType/string() eq 'ApplicationProfile' and $profile/Profile/Name/string() " + + " eq '" + APPLICATION_PROFILE_NAME + "'" + + "return $profile"; + + /** + * Get the scope in which discover for this url. If the Application Profile doesn't contain it, the current scope (taken + * from ScopeProvider is returned). + * @param url + * @return the scope to be used for the given url + */ + public static String getScopePerUrl(String url){ + + logger.info("Request scope for ckan portlet at url " + url); + + String scope = ScopeProvider.instance.get(); + String scopeToReturn = scope; + String rootScopeForInfrastructure = "/" + PortalContext.getConfiguration().getInfrastructureName(); + + if(url == null || url.isEmpty()){ + + logger.info("The url passed is null or empty! Returning current scope [" + scope + "]"); + return scope; + + } + + // set this scope + ScopeProvider.instance.set(rootScopeForInfrastructure); + + logger.debug("Trying to fetch applicationProfile profile from the infrastructure for " + APPLICATION_PROFILE_NAME + " scope: " + rootScopeForInfrastructure); + + try { + Query q = new QueryBox(QUERY); + + DiscoveryClient client = client(); + List appProfile = client.submit(q); + + if (appProfile == null || appProfile.size() == 0) + throw new ApplicationProfileNotFoundException("Your applicationProfile is not registered in the infrastructure"); + else{ + String elem = appProfile.get(0); + DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + Node node = docBuilder.parse(new InputSource(new StringReader(elem))).getDocumentElement(); + XPathHelper helper = new XPathHelper(node); + + List urls = helper.evaluate("/Resource/Profile/Body/EndPoint/URL/text()"); + if (urls != null && urls.size() > 0) { + boolean foundScope = false; + for (int i = 0; i < urls.size(); i++) { + if (urls.get(i).trim().compareTo(url) == 0) { // url found + scopeToReturn = helper.evaluate("/Resource/Profile/Body/EndPoint/Scope/text()").get(i); + logger.debug("Found, returning " + scopeToReturn); + foundScope = true; + break; + } + } + if (!foundScope || scopeToReturn == null || scopeToReturn.isEmpty()){ + logger.debug("Scope is missing for url " + url + ". Returning " + scope); + } + } + else + throw + new ApplicationProfileNotFoundException("Your applicationProfile EndPoint was not found in the profile, consider adding element in "); + } + } catch (Exception e) { + logger.error("Error while trying to fetch applicationProfile profile from the infrastructure", e); + }finally{ + ScopeProvider.instance.set(scope); + } + + return scopeToReturn; + } +} diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/CKANTokenBean.class b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/CKANTokenBean.class new file mode 100644 index 0000000..fd89206 Binary files /dev/null and b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/CKANTokenBean.class differ diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/CKANTokenBean.java b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/CKANTokenBean.java new file mode 100644 index 0000000..9cd9ba5 --- /dev/null +++ b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/CKANTokenBean.java @@ -0,0 +1,44 @@ +package org.gcube.datacatalogue.ckanutillibrary.server; + + +// TODO: Auto-generated Javadoc +/** + * The Class CKANTokenBean. + * + * @author Francesco Mangiacrapa at ISTI-CNR Pisa (Italy) + * Jun 1, 2020 + */ +public class CKANTokenBean { + + protected String apiKey; + protected long timestamp; + + /** + * Instantiates a new CKAN token bean. + * + * @param apiKey the api key + * @param timestamp the timestamp + */ + public CKANTokenBean(String apiKey, long timestamp) { + super(); + this.apiKey = apiKey; + this.timestamp = timestamp; + } + + public String getApiKey() { + return apiKey; + } + + public void setApiKey(String apiKey) { + this.apiKey = apiKey; + } + + public long getTimestamp() { + return timestamp; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + +} diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogue.class b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogue.class new file mode 100644 index 0000000..dc5abaf Binary files /dev/null and b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogue.class differ diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogue.java b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogue.java new file mode 100644 index 0000000..800a459 --- /dev/null +++ b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogue.java @@ -0,0 +1,200 @@ +package org.gcube.datacatalogue.ckanutillibrary.server; + +import java.util.List; +import java.util.Map; + +import org.gcube.datacatalogue.ckanutillibrary.shared.ResourceBean; +import org.gcube.datacatalogue.ckanutillibrary.shared.RolesCkanGroupOrOrg; + +import eu.trentorise.opendata.jackan.model.CkanDataset; +import eu.trentorise.opendata.jackan.model.CkanGroup; +import eu.trentorise.opendata.jackan.model.CkanLicense; +import eu.trentorise.opendata.jackan.model.CkanOrganization; + +public interface DataCatalogue { + + /** + * Finds the id associated to the chosen license + * + * @param chosenLicense + * @return the id on success, null otherwise + */ + String findLicenseIdByLicenseTitle(String chosenLicense); + + /** + * Given the id or the name of the dataset it returns its current url by + * contacting the uri resolver. If no uri resolver is available, an url that is + * not guaranteed to be long term valid will be generated. Information are not + * encrypted. + * + * @param datasetId + * @return The url of the dataset on success, null otherwise + */ + String getUnencryptedUrlFromDatasetIdOrName(String datasetIdOrName); + + /** + * Create a dataset with those information. The method allows to have multiple + * values for the same custom field key. NOTE: unfortunately java doesn't + * support overload in java interface methods (that's way I cannot use the same + * name for the method) + * + * @param title + * @param name (unique identifier) + * @param organizationNameOrId + * @param author + * @param authorMail + * @param maintainer + * @param maintainerMail + * @param version + * @param description + * @param licenseId + * @param tags + * @param customFields + * @param resources + * @param setPublic (manage visibility: Admin role is needed) + * @return the id of the dataset on success, null otherwise + * @throws Exception + */ + String createCKanDatasetMultipleCustomFields(String title, String name, String organizationNameOrId, String author, + String authorMail, String maintainer, String maintainerMail, long version, String description, + String licenseId, List tags, Map> customFields, List resources, + boolean setPublic) throws Exception; + + /** + * Patch a product with product id productId by using the couples in + * customFieldsToChange. NOTE: only the specified custom fields will be changed. + * If a custom field with a given key already exists, and removeOld is set to + * false, the new values are added at the end of the list. Otherwise they are + * lost. + * + * @param productId + * @param customFieldsToChange + * @param removeOld + * @return + */ + boolean patchProductCustomFields(String productId, Map> customFieldsToChange, + boolean removeOld); + + /** + * Add a resource described by the bean to the dataset id into + * resource.datasetId + * + * @param resource + * @return String the id of the resource on success, null otherwise + */ + String addResourceToDataset(ResourceBean resource) throws Exception; + + /** + * Remove the resource with id resourceId from the dataset in which it is. + * + * @param resourceId + * @return true on success, false otherwise. + */ + boolean deleteResourceFromDataset(String resourceId); + + /** + * Create a dataset with those information. + * + * @param apiKey + * @param title + * @param name (unique identifier) + * @param organizationNameOrId + * @param author + * @param authorMail + * @param maintainer + * @param maintainerMail + * @param version + * @param description + * @param licenseId + * @param tags + * @param customFields + * @param resources + * @param setPublic (manage visibility: Admin role is needed) + * @return the id of the dataset on success, null otherwise + * @throws Exception + */ + + /** + * Checks if a product with such name already exists. Please remember that the + * name is unique. + * + * @param nameOrId the name or the id of the dataset to check + * @return + */ + boolean existProductWithNameOrId(String nameOrId); + + /** + * The method returns the role the user has in the groups he/she belongs to (it + * uses the db, so it is much faster) + * + * @param username + * @param apiKey + * @return + */ + Map> getUserRoleByGroup(String username, String apiKey); + + /** + * Get the list of licenses' titles. + * + * @return the list of licenses' titles + */ + List getLicenseTitles(); + + /** + * Retrieve ckan licenses + * + * @return + */ + List getLicenses(); + + /** + * Retrieve the url of the uri resolver for this catalogue instance/scope + * + * @return + */ + String getUriResolverUrl(); + + /** + * Return the manage product property + * + * @return the manage product property + */ + boolean isManageProductEnabled(); + + /** + * Return the catalogue portlet for this context(i.e. scope) + * + * @return + */ + String getPortletUrl(); + + /** + * Return the ckan catalogue url in this scope. + * + * @return the catalogue url + */ + String getCatalogueUrl(); + + + /** + * Returns the list of organizations to whom the user belongs (with any role) + * @param username + * @return a list of organizations + */ + List getOrganizationsByUser(String username); + + /** + * Returns the list of groups to whom the user belongs (with any role) + * @param username + * @return a list of groups + */ + List getGroupsByUser(String username); + + /** + * Retrieve a ckan dataset given its id + * @param datasetId + * @return + */ + CkanDataset getDataset(String datasetId, String apiKey); + +} diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogueFactory$CacheBean.class b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogueFactory$CacheBean.class new file mode 100644 index 0000000..e346cc4 Binary files /dev/null and b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogueFactory$CacheBean.class differ diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogueFactory.class b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogueFactory.class new file mode 100644 index 0000000..b92917d Binary files /dev/null and b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogueFactory.class differ diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogueFactory.java b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogueFactory.java new file mode 100644 index 0000000..542d882 --- /dev/null +++ b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogueFactory.java @@ -0,0 +1,78 @@ +package org.gcube.datacatalogue.ckanutillibrary.server; + +import java.util.concurrent.ConcurrentHashMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Please invoke this method to retrieve an object of this kind per scope. + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class DataCatalogueFactory { + + private static final Logger logger = LoggerFactory.getLogger(DataCatalogueFactory.class); + private static final long MAX_LIFETIME = 1000 * 60 * 30; // 30 MINUTES + private static DataCatalogueFactory instance = new DataCatalogueFactory(); + private static ConcurrentHashMap cache; + + private class CacheBean{ + DataCatalogueImpl utils; + long ttl; + + public CacheBean(long ttl, DataCatalogueImpl utils){ + this.ttl = ttl; + this.utils = utils; + } + } + + /** + * Private constructor + */ + private DataCatalogueFactory(){ + + logger.debug("Ckan factory object build"); + cache = new ConcurrentHashMap(); + + } + + /** + * Get the factory instance + * @return + */ + public static DataCatalogueFactory getFactory(){ + logger.debug("Factory requested"); + return instance; + } + + /** + * Retrieve the ckan utils information for the given scope + * @param scope + * @return + * @throws Exception + */ + public DataCatalogueImpl getUtilsPerScope(String scope) throws Exception{ + + if(scope == null || scope.isEmpty()) + throw new IllegalArgumentException("Invalid scope given!"); + + if(cache.containsKey(scope) && !expired(cache.get(scope))){ + return cache.get(scope).utils; + } + else{ + logger.info("Creating CKAN LIB utils for scope " + scope); + DataCatalogueImpl utils = new DataCatalogueImpl(scope); + cache.put(scope, new CacheBean(System.currentTimeMillis(), utils)); + return utils; + } + } + + /** + * Check if the ckan information must be retrieved again. + * @param cacheBean + * @return + */ + private boolean expired(CacheBean cacheBean) { + return (cacheBean.ttl + MAX_LIFETIME < System.currentTimeMillis()); + } +} diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogueImpl.class b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogueImpl.class new file mode 100644 index 0000000..80c093a Binary files /dev/null and b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogueImpl.class differ diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogueImpl.java b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogueImpl.java new file mode 100644 index 0000000..c2f2839 --- /dev/null +++ b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogueImpl.java @@ -0,0 +1,543 @@ +package org.gcube.datacatalogue.ckanutillibrary.server; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.gcube.common.authorization.library.provider.SecurityTokenProvider; +import org.gcube.common.scope.impl.ScopeBean; +import org.gcube.common.scope.impl.ScopeBean.Type; +import org.gcube.datacatalogue.ckanutillibrary.ckan.ExtendCkanClient; +import org.gcube.datacatalogue.ckanutillibrary.ckan.SimpleExtendCkanClient; +import org.gcube.datacatalogue.ckanutillibrary.db.DBCaller; +import org.gcube.datacatalogue.ckanutillibrary.gcat.GCatCaller; +import org.gcube.datacatalogue.ckanutillibrary.server.DataCatalogueRunningCluster.ACCESS_LEVEL_TO_CATALOGUE_PORTLET; +import org.gcube.datacatalogue.ckanutillibrary.server.utils.CatalogueUtilMethods; +import org.gcube.datacatalogue.ckanutillibrary.server.utils.url.EntityContext; +import org.gcube.datacatalogue.ckanutillibrary.shared.ResourceBean; +import org.gcube.datacatalogue.ckanutillibrary.shared.RolesCkanGroupOrOrg; +import org.gcube.datacatalogue.ckanutillibrary.shared.State; +import org.json.simple.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import eu.trentorise.opendata.jackan.internal.org.apache.http.HttpResponse; +import eu.trentorise.opendata.jackan.internal.org.apache.http.client.methods.HttpPost; +import eu.trentorise.opendata.jackan.internal.org.apache.http.entity.ContentType; +import eu.trentorise.opendata.jackan.internal.org.apache.http.entity.StringEntity; +import eu.trentorise.opendata.jackan.internal.org.apache.http.impl.client.CloseableHttpClient; +import eu.trentorise.opendata.jackan.internal.org.apache.http.impl.client.HttpClientBuilder; +import eu.trentorise.opendata.jackan.internal.org.apache.http.util.EntityUtils; +import eu.trentorise.opendata.jackan.model.CkanDataset; +import eu.trentorise.opendata.jackan.model.CkanGroup; +import eu.trentorise.opendata.jackan.model.CkanLicense; +import eu.trentorise.opendata.jackan.model.CkanOrganization; +import eu.trentorise.opendata.jackan.model.CkanUser; + +public class DataCatalogueImpl implements DataCatalogue { + + private static final Logger LOG = LoggerFactory.getLogger(DataCatalogueImpl.class); + + private String CKAN_CATALOGUE_URL; + private String CKAN_DB_NAME; + private String CKAN_DB_USER; + private String CKAN_DB_PASSWORD; + private String CKAN_DB_URL; + private Integer CKAN_DB_PORT; + private String PORTLET_URL_FOR_SCOPE; + private String SOLR_URL; + private String CKAN_TOKEN_SYS; + private String CKAN_EMAIL; + private String URI_RESOLVER_URL; + private boolean MANAGE_PRODUCT_BUTTON; + private boolean SOCIAL_POST; + private boolean ALERT_USERS_ON_POST_CREATION; + private String CONTEXT; + private Map extendRoleInOrganization; + + public Map mapAccessURLToCatalogue; + + private static final String CATALOGUE_TAB_ENDING_URL = "/catalogue"; + + // gCat client + private GCatCaller gCatCaller; + + // db client + private DBCaller dbCaller; + + // ckan client + private SimpleExtendCkanClient ckanCaller; + + // hashmap for ckan api keys + private ConcurrentHashMap apiKeysMap; + + // apikey bean expires after X minutes in the above map + private static final int EXPIRE_KEY_TIME = 60 * 60 * 1000; + + + /** + * The ckan catalogue url and database will be discovered in this scope + * @param scope + * @throws Exception if unable to find datacatalogue info + */ + public DataCatalogueImpl(String scope) throws Exception{ + + DataCatalogueRunningCluster runningInstance = new DataCatalogueRunningCluster(scope); + + // save information + CKAN_DB_URL = runningInstance.getDatabaseHosts().get(0).trim(); + CKAN_DB_PORT = runningInstance.getDatabasePorts().get(0); + CKAN_DB_NAME = runningInstance.getDataBaseName().trim(); + CKAN_DB_USER = runningInstance.getDataBaseUser().trim(); + CKAN_DB_PASSWORD = runningInstance.getDataBasePassword().trim(); + + //CKAN_TOKEN_SYS = runningInstance.getSysAdminToken().trim(); + CKAN_EMAIL = runningInstance.getEmailCatalogue().trim(); + + CKAN_CATALOGUE_URL = runningInstance.getDataCatalogueUrl().get(0).trim(); + PORTLET_URL_FOR_SCOPE = runningInstance.getPortletUrl().trim(); + mapAccessURLToCatalogue = runningInstance.getMapAccessURLToCatalogue(); + MANAGE_PRODUCT_BUTTON = runningInstance.isManageProductEnabled(); + URI_RESOLVER_URL = runningInstance.getUrlResolver(); + SOCIAL_POST = runningInstance.isSocialPostEnabled(); + ALERT_USERS_ON_POST_CREATION = runningInstance.isAlertEnabled(); + SOLR_URL = runningInstance.getUrlSolr(); + + LOG.info("In the scope: "+scope+", I read the catalogue URL: " + CKAN_CATALOGUE_URL); + + // build the clients + gCatCaller = new GCatCaller(CKAN_CATALOGUE_URL); + + dbCaller = new DBCaller(CKAN_DB_URL, CKAN_DB_PORT, CKAN_DB_NAME, CKAN_DB_USER, CKAN_DB_PASSWORD); + + ckanCaller = new SimpleExtendCkanClient(CKAN_CATALOGUE_URL); + + // init map + apiKeysMap = new ConcurrentHashMap(); + + // save the context + CONTEXT = scope; + + // extended roles + extendRoleInOrganization = runningInstance.getExtendRoleInOrganization(); + } + + @Override + public String getCatalogueUrl() { + return CKAN_CATALOGUE_URL; + } + + + @Override + public String getPortletUrl() { + + //PATCHED By Francesco + ScopeBean context = new ScopeBean(CONTEXT); + + if(context.is(Type.INFRASTRUCTURE)) { + LOG.info("Working with the {} scope returning the path read from GR 'Ckan-Porltet': {}", Type.INFRASTRUCTURE.toString(), PORTLET_URL_FOR_SCOPE); + return PORTLET_URL_FOR_SCOPE; + } + + String vreNameLower = context.name().toLowerCase(); + //CHECKING IF THE PORTLET URL CONTAINS THE VRE NAME INTO URL + if(PORTLET_URL_FOR_SCOPE.toLowerCase().contains(vreNameLower)){ + //THE PORLTET URL READ FROM GENERIC RESOUCE 'CkanPortlet' SHOULD BE ALREADY VALID, POITING TO CKAN PORTLET + return PORTLET_URL_FOR_SCOPE; + }else{ + //ADDING VRE getApiKeyFromUsernameNAME AND THE SUFFIX 'CATALOGUE_TAB_ENDING_URL' TO URL + String buildedUrl = PORTLET_URL_FOR_SCOPE.endsWith("/") ? PORTLET_URL_FOR_SCOPE : PORTLET_URL_FOR_SCOPE + "/"; + String defaultSuffix = vreNameLower + CATALOGUE_TAB_ENDING_URL; + buildedUrl+= defaultSuffix; + LOG.warn("The Portlet URL read from Generic Resource 'Ckan-Porltet' does not contain the portlet suffix, so I added the default: "+defaultSuffix); + return buildedUrl; + } + } + + + @Override + public String findLicenseIdByLicenseTitle(String chosenLicense) { + LOG.debug("Requested license id"); + + // checks + checkNotNull(chosenLicense); + + //retrieve the list of available licenses + List licenses = ckanCaller.getLicenseList(); + + for (CkanLicense ckanLicense : licenses) { + if(ckanLicense.getTitle().equals(chosenLicense)) + return ckanLicense.getId(); + } + + return null; + } + + @Override + public List getLicenseTitles() { + + LOG.debug("Request for CKAN licenses"); + + // get the url and the api key of the user + List result = new ArrayList(); + + //retrieve the list of available licenses + List licenses = ckanCaller.getLicenseList(); + + for (CkanLicense ckanLicense : licenses) { + + result.add(ckanLicense.getTitle()); + LOG.debug("License is " + ckanLicense.getTitle() + " and id " + ckanLicense.getId()); + + } + + return result; + } + + @Override + public List getLicenses() { + LOG.debug("Request for CKAN licenses (original jackan objects are going to be retrieved)"); + + //retrieve the list of available licenses + return ckanCaller.getLicenseList(); + } + + @Override + public CkanDataset getDataset(String datasetId, String apiKey) { + + LOG.info("Request ckan dataset with id " + datasetId); + + // checks + checkNotNull(datasetId); + checkArgument(!datasetId.isEmpty()); + + try{ + + if(apiKey!=null && !apiKey.isEmpty()) { + LOG.info("API-KEY found. Calling the "+ExtendCkanClient.class.getSimpleName()); + ExtendCkanClient client = new ExtendCkanClient(CKAN_CATALOGUE_URL, apiKey); + return client.getDataset(datasetId); + } + + String authzToken = SecurityTokenProvider.instance.get(); + if(authzToken!=null && !authzToken.isEmpty()) { + LOG.info("gcube-token found. Calling the gCat client"); + return gCatCaller.getDatasetForName(datasetId); + } + + LOG.info("No api-key or gcube-token found. Calling Ckan Client without API-KEY"); + return ckanCaller.getDataset(datasetId); + + }catch(Exception e){ + LOG.error("Unable to retrieve such dataset, returning null ...", e); + } + + return null; + } + + + @Override + public String getUnencryptedUrlFromDatasetIdOrName(String datasetIdOrName) { + LOG.debug("Request coming for getting dataset url (not encrypted) of dataset with name/id " + datasetIdOrName); + + // checks + checkNotNull(datasetIdOrName); + checkArgument(!datasetIdOrName.isEmpty()); + String url = null; + try{ + + // get the dataset from name + ExtendCkanClient client = new ExtendCkanClient(CKAN_CATALOGUE_URL, CKAN_TOKEN_SYS); + CkanDataset dataset = client.getDataset(datasetIdOrName); + String name = dataset.getName(); + + if(dataset != null){ + + if(getUriResolverUrl() != null) + url = getUrlForProduct(CONTEXT, EntityContext.DATASET, name); + + if(url == null || url.isEmpty()) + url = getPortletUrl() + "?" + URLEncoder.encode("path=/dataset/" + name, "UTF-8"); + + } + }catch(Exception e){ + LOG.error("Error while retrieving dataset with id/name=" + datasetIdOrName, e); + } //requestEntity.put("clear_url", Boolean.toString(unencrypted)); + return url; + } + + public String createCKanDatasetMultipleCustomFields(String title, String name, String organizationNameOrId, + String author, String authorMail, String maintainer, String maintainerMail, long version, + String description, String licenseId, List tags, Map> customFields, + List resources, boolean setPublic) throws Exception { + return null; + } + + @Override + public Map> getUserRoleByGroup( + String username, String apiKey) { + + checkNotNull(username); + checkNotNull(apiKey); + + checkNotNull(username); + checkNotNull(apiKey); + + Map> toReturn = new HashMap>(); + + try{ + + String ckanUsername = CatalogueUtilMethods.fromUsernameToCKanUsername(username); + Map partialResult = dbCaller.getGroupsByUserFromDB(ckanUsername); + + for (String groupID : partialResult.keySet()) { + + CkanGroup group = ckanCaller.getGroup(groupID); + HashMap subMap = new HashMap(); + subMap.put(group, partialResult.get(groupID)); + toReturn.put(groupID, subMap); + } + + LOG.debug("Returning map " + toReturn); + }catch(Exception e){ + LOG.error("Failed to retrieve roles of user in his/her own groups",e); + } + + return toReturn; + + } + + + /** + * Retrieve an url for the tuple scope, entity, entity name + * @param context + * @param entityContext + * @param entityName + */ + private String getUrlForProduct(String context, EntityContext entityContext, String entityName){ + + String toReturn = null; + + try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) { + + HttpPost httpPostRequest = new HttpPost(getUriResolverUrl()); + + JSONObject requestEntity = new JSONObject(); + requestEntity.put("gcube_scope", context); + requestEntity.put("entity_context", entityContext.toString()); + requestEntity.put("entity_name", entityName); + + StringEntity params = new StringEntity(requestEntity.toJSONString(), ContentType.APPLICATION_JSON); + httpPostRequest.setEntity(params); + + HttpResponse response = httpClient.execute(httpPostRequest); + + if(response.getStatusLine().getStatusCode() != 200) + throw new Exception("There was an error while creating an url " + response.getStatusLine()); + + toReturn = EntityUtils.toString(response.getEntity()); + LOG.debug("Result is " + toReturn); + + }catch(Exception e){ + LOG.error("Failed to get an url for this product", e); + } + + return toReturn; + } + + + + @Override + public String getUriResolverUrl() { + return URI_RESOLVER_URL; + } + + /** + * Check if the manage product is enabled + * @return + */ + @Override + public boolean isManageProductEnabled() { + return MANAGE_PRODUCT_BUTTON; + } + + @Override + public List getOrganizationsByUser(String username) { + + LOG.debug("Requested organizations for user " + username); + + // checks + checkNotNull(username); + + // in order to avoid errors, the username is always converted + String ckanUsername = CatalogueUtilMethods.fromUsernameToCKanUsername(username); + + // list to return + List toReturn = new ArrayList(); + + try{ + + // get the list of all organizations + List organizations = ckanCaller.getOrganizationList(); + + // iterate over them + for (CkanOrganization ckanOrganization : organizations) { + + // get the list of users in it (if you try ckanOrganization.getUsers() it returns null.. maybe a bug TODO) + List users = ckanCaller.getOrganization(ckanOrganization.getName()).getUsers(); + + // check if the current user is among them + for (CkanUser ckanUser : users) { + if(ckanUser.getName().equals(ckanUsername)){ + + LOG.debug("User " + ckanUsername + " is into " + ckanOrganization.getName()); + toReturn.add(ckanOrganization); + break; + + } + } + } + }catch(Exception e){ + LOG.error("Unable to get user's organizations", e); + } + return toReturn; + } + + @Override + public List getGroupsByUser(String username) { + LOG.debug("Requested groups for user " + username); + + // checks + checkNotNull(username); + + // in order to avoid errors, the username is always converted + String ckanUsername = CatalogueUtilMethods.fromUsernameToCKanUsername(username); + + // list to return + List toReturn = new ArrayList(); + + try{ + + // get the list of all organizations + List groups = ckanCaller.getGroupList(); + + // iterate over them + for (CkanGroup ckanGroup : groups) { + + List users = ckanCaller.getGroup(ckanGroup.getName()).getUsers(); + + // check if the current user is among them + for (CkanUser ckanUser : users) { + if(ckanUser.getName().equals(ckanUsername)){ + + LOG.debug("User " + ckanUsername + " is into " + ckanGroup.getName()); + toReturn.add(ckanGroup); + break; + + } + } + } + }catch(Exception e){ + LOG.error("Unable to get user's groups", e); + } + return toReturn; + } + + //@Override + private String getApiKeyFromUsername(String username) { + + LOG.debug("Request api key for user = " + username); + + // checks + checkNotNull(username); + checkArgument(!username.isEmpty()); + + // in order to avoid errors, the username is always converted + String ckanUsername = CatalogueUtilMethods.fromUsernameToCKanUsername(username); + + // check in the hashmap first + if(apiKeysMap.containsKey(ckanUsername)){ + CKANTokenBean bean = apiKeysMap.get(ckanUsername); + if(bean.timestamp + EXPIRE_KEY_TIME > System.currentTimeMillis()){ // it's still ok + return bean.apiKey; + } + } + + LOG.debug("Api key was not in cache or it expired"); + + try{ + + String apiToReturn = dbCaller.getApiKeyFromUsername(username, State.ACTIVE.name().toLowerCase()); + + // save into the hashmap + if(apiToReturn != null) + apiKeysMap.put(ckanUsername, new CKANTokenBean(apiToReturn, System.currentTimeMillis())); + + return apiToReturn; + + }catch(Exception e){ + LOG.error("Unable to retrieve key for user " + ckanUsername, e); + } + + return null; + } + + + + + + + + + + + + /* + * + * + * + * + * + * + * + * + * + * WRITE OPERATIONS + * + * + * + * + * + * + * + * + * + * + */ + + + public boolean patchProductCustomFields(String productId, Map> customFieldsToChange, + boolean removeOld) { + return false; + } + + public String addResourceToDataset(ResourceBean resource) throws Exception { + return null; + } + + public boolean deleteResourceFromDataset(String resourceId) { + return false; + } + + public boolean existProductWithNameOrId(String nameOrId) { + return false; + } + + +} diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogueRunningCluster$ACCESS_LEVEL_TO_CATALOGUE_PORTLET.class b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogueRunningCluster$ACCESS_LEVEL_TO_CATALOGUE_PORTLET.class new file mode 100644 index 0000000..91d0aee Binary files /dev/null and b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogueRunningCluster$ACCESS_LEVEL_TO_CATALOGUE_PORTLET.class differ diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogueRunningCluster.class b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogueRunningCluster.class new file mode 100644 index 0000000..15a2ffd Binary files /dev/null and b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogueRunningCluster.class differ diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogueRunningCluster.java b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogueRunningCluster.java new file mode 100644 index 0000000..3f153ea --- /dev/null +++ b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/DataCatalogueRunningCluster.java @@ -0,0 +1,690 @@ +package org.gcube.datacatalogue.ckanutillibrary.server; + +import static org.gcube.resources.discovery.icclient.ICFactory.client; +import static org.gcube.resources.discovery.icclient.ICFactory.clientFor; +import static org.gcube.resources.discovery.icclient.ICFactory.queryFor; + +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.gcube.common.encryption.StringEncrypter; +import org.gcube.common.resources.gcore.ServiceEndpoint; +import org.gcube.common.resources.gcore.ServiceEndpoint.AccessPoint; +import org.gcube.common.resources.gcore.ServiceEndpoint.Property; +import org.gcube.common.resources.gcore.utils.XPathHelper; +import org.gcube.common.scope.api.ScopeProvider; +import org.gcube.datacatalogue.ckanutillibrary.shared.ex.ApplicationProfileNotFoundException; +import org.gcube.datacatalogue.ckanutillibrary.shared.ex.NoApplicationProfileMasterException; +import org.gcube.datacatalogue.ckanutillibrary.shared.ex.NoDataCatalogueRuntimeResourceException; +import org.gcube.datacatalogue.ckanutillibrary.shared.ex.ServiceEndPointException; +import org.gcube.resources.discovery.client.api.DiscoveryClient; +import org.gcube.resources.discovery.client.queries.api.Query; +import org.gcube.resources.discovery.client.queries.api.SimpleQuery; +import org.gcube.resources.discovery.client.queries.impl.QueryBox; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +/** + * Retrieve ckan running instance information in the infrastructure (for both its database and data catalogue url). + * + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + * updated by Francesco Mangiacrapa at ISTI-CNR (francesco.mangiacrapa@isti.cnr.it) + */ +public class DataCatalogueRunningCluster { + + //logger + private static final Logger logger = LoggerFactory.getLogger(DataCatalogueRunningCluster.class); + + // database of the datacatalogue info + private final static String RUNTIME_DB_RESOURCE_NAME = "CKanDatabase"; + private final static String PLATFORM_DB_NAME = "postgres"; + private final static String APPLICATION_PROFILE_NAME = "CkanPortlet"; + + // data catalogue info + private final static String RUNTIME_CATALOGUE_RESOURCE_NAME = "CKanDataCatalogue"; + private final static String PLATFORM_CATALOGUE_NAME = "Tomcat"; + + // api key property for SYSADMIN + private final static String API_KEY_PROPERTY = "API_KEY"; + + // catalogue email + private final static String CKAN_EMAIL_PROPERTY = "CATALOGUE_EMAIL"; + + // property to retrieve the master service endpoint into the /root scope + private final static String IS_MASTER_ROOT_KEY_PROPERTY = "IS_ROOT_MASTER"; // true, false.. missing means false as well + private final static String IS_MANAGE_PRODUCT_ENABLED = "IS_MANAGE_PRODUCT_ENABLED"; // true, false.. missing means false as well (for GRSF records) + + private final static String SOCIAL_POST = "SOCIAL_POST"; + private final static String ALERT_USERS_ON_POST_CREATION = "ALERT_USERS_ON_POST_CREATION"; + private final static String SOLR_INDEX_ADDRESS = "SOLR_INDEX_ADDRESS"; + + // url of the http uri for this scope + private final static String URL_RESOLVER = "URL_RESOLVER"; + + // Other generic resource for delegating roles in groups to users + private final static String CATALOGUE_EXTENDING_ROLES = "CatalogueDelegateRoles"; + + // retrieved data + private List datacatalogueUrls = new ArrayList(); + private List hostsDB = new ArrayList(); + private List portsDB = new ArrayList(); + private String nameDB; + private String userDB; + private String passwordDB; + private String portletUrl; + private String urlSolr; + private boolean manageProductEnabled; + private String urlResolver; + private boolean socialPost; + private boolean alertUsers; + private Map extendRoleInOrganization = new HashMap(0); + + // generic role key + public static final String CKAN_GENERIC_ROLE = "*"; + public static final String ROLE_ORGANIZATION_SEPARATOR = "|"; + public static final String TUPLES_SEPARATOR = ","; + + private static final String DEFAULT_CATALOGUE_EMAIL = "catalogue@d4science.org"; + + /** + * The Enum ACCESS_LEVEL_TO_CATALOGUE_PORTLET. + * + * @author Francesco Mangiacrapa at ISTI-CNR (francesco.mangiacrapa@isti.cnr.it) + * + * Nov 12, 2019 + */ + //Added by Francesco, see #18039 + public static final String PUBLIC_VRE_CATALOGUE_URL = "public-vre-url"; + public static enum ACCESS_LEVEL_TO_CATALOGUE_PORTLET {PUBLIC_GATEWAY, PUBLIC_VRE, PRIVATE_VRE}; + public Map mapAccessURLToCatalogue; + + // this token is needed in order to assign roles to user + private String sysAdminToken; + + private String catalogueEmail; + + /** + * Instantiates a new data catalogue running cluster. + * + * @param scope the scope + * @throws Exception the exception + */ + public DataCatalogueRunningCluster(String scope) throws Exception{ + + if(scope == null || scope.isEmpty()) + throw new Exception("Invalid scope!!"); + + // retrieve the current scope and save it (it will be reset later) + String currentScope = ScopeProvider.instance.get(); + + logger.info("Retrieving ckan database service end point information for scope " + scope); + try { + + // set the scope + ScopeProvider.instance.set(scope); + + logger.debug("Retrieving database information."); + + List resources = getConfigurationFromISFORDB(); + evaluateRightConfigurationDB(resources); + + logger.debug("Retrieving ckan data catalogue service end point information and sysadmin token."); + + resources = getConfigurationFromISFORCatalogueUrl(); + evaluateRightConfigurationCatalogue(resources); + + // finally get the url in which the ckan portlet is deployed + + logger.debug("Looking for portlet url in " + ScopeProvider.instance.get() + " scope" ); + + //portletUrl = getPortletUrlFromInfrastrucure(); + mapAccessURLToCatalogue = getPortletUrlFromInfrastrucure(); + portletUrl = mapAccessURLToCatalogue.get(ACCESS_LEVEL_TO_CATALOGUE_PORTLET.PRIVATE_VRE); + + // and parse the CatalogueDelegateRole resource, if any, in this context + parseExtendingRoles(); + + }catch(Exception e) { + logger.warn("The following error occurred: " + e.toString()); + throw e; + }finally{ + ScopeProvider.instance.set(currentScope); + } + } + + + /** + * Evaluate the right configuration about ckan. + * + * @param resources the resources + * @throws NoDataCatalogueRuntimeResourceException the no data catalogue runtime resource exception + * @throws ServiceEndPointException the service end point exception + */ + private void evaluateRightConfigurationCatalogue( + List resources) throws NoDataCatalogueRuntimeResourceException, ServiceEndPointException { + if (resources.size() == 0){ + logger.error("There is no Runtime Resource having name " + RUNTIME_CATALOGUE_RESOURCE_NAME +" and Platform " + PLATFORM_CATALOGUE_NAME + " in this scope."); + throw new NoDataCatalogueRuntimeResourceException(); + } + else { + logger.debug(resources.toString()); + try{ + if(resources.size() > 1){ + boolean oneWasMaster = false; + + logger.info("Too many Runtime Resource having name " + RUNTIME_CATALOGUE_RESOURCE_NAME +" in this scope.. Looking for the one that has the property " + IS_MASTER_ROOT_KEY_PROPERTY); + + for (ServiceEndpoint res : resources) { + + Iterator accessPointIterator = res.profile().accessPoints().iterator(); + + while (accessPointIterator.hasNext()) { + ServiceEndpoint.AccessPoint accessPoint = accessPointIterator + .next(); + + // get the is master property + Property entry = accessPoint.propertyMap().get(IS_MASTER_ROOT_KEY_PROPERTY); + String isMaster = entry != null ? entry.value() : null; + + if(isMaster == null || !isMaster.equals("true")) + continue; + + // set this variable + oneWasMaster = true; + + // add this host + datacatalogueUrls.add(accessPoint.address()); + + // retrieve sys admin token + sysAdminToken = accessPoint.propertyMap().get(API_KEY_PROPERTY).value(); + sysAdminToken = StringEncrypter.getEncrypter().decrypt(sysAdminToken); + + // retrieve catalogue email + if(accessPoint.propertyMap().containsKey(CKAN_EMAIL_PROPERTY)){ + catalogueEmail = accessPoint.propertyMap().get(CKAN_EMAIL_PROPERTY).value(); + catalogueEmail = StringEncrypter.getEncrypter().decrypt(catalogueEmail); + } + + // retrieve URL_RESOLVER + if(accessPoint.propertyMap().containsKey(URL_RESOLVER)) + urlResolver = accessPoint.propertyMap().get(URL_RESOLVER).value(); + + // break now + break; + } + } + + // if none of them was master, throw an exception + if(!oneWasMaster) + throw new NoApplicationProfileMasterException("There is no application profile with MASTER property"); + + }else{ + + ServiceEndpoint res = resources.get(0); + Iterator accessPointIterator = res.profile().accessPoints().iterator(); + + while (accessPointIterator.hasNext()) { + ServiceEndpoint.AccessPoint accessPoint = accessPointIterator + .next(); + + // add this host + datacatalogueUrls.add(accessPoint.address()); + + // retrieve sys admin token + sysAdminToken = accessPoint.propertyMap().get(API_KEY_PROPERTY).value(); + sysAdminToken = StringEncrypter.getEncrypter().decrypt(sysAdminToken); + + // retrieve catalogue email + if(accessPoint.propertyMap().containsKey(CKAN_EMAIL_PROPERTY)){ + catalogueEmail = accessPoint.propertyMap().get(CKAN_EMAIL_PROPERTY).value(); + catalogueEmail = StringEncrypter.getEncrypter().decrypt(catalogueEmail); + } + + // get the is manage product property + Property entry = accessPoint.propertyMap().get(IS_MANAGE_PRODUCT_ENABLED); + String isManageProduct = entry != null ? entry.value() : null; + + if(isManageProduct != null && isManageProduct.equals("true")){ + logger.info("Manage product is enabled in this scope"); + manageProductEnabled = true; + } + + // retrieve option to check if the social post has to be made + socialPost = true; // default is true + if(accessPoint.propertyMap().containsKey(SOCIAL_POST)) + if(accessPoint.propertyMap().get(SOCIAL_POST).value().trim().equalsIgnoreCase("false")) + socialPost = false; + + + // retrieve option for user alert + if(accessPoint.propertyMap().containsKey(ALERT_USERS_ON_POST_CREATION)) + if(accessPoint.propertyMap().get(ALERT_USERS_ON_POST_CREATION).value().trim().equalsIgnoreCase("true")) + alertUsers = true; + + // retrieve URL_RESOLVER + if(accessPoint.propertyMap().containsKey(URL_RESOLVER)) + urlResolver = accessPoint.propertyMap().get(URL_RESOLVER).value(); + + // retrieve url of the solr index for further queries + if(accessPoint.propertyMap().containsKey(SOLR_INDEX_ADDRESS)) + urlSolr = accessPoint.propertyMap().get(SOLR_INDEX_ADDRESS).value(); + + } + } + + }catch(Exception e){ + throw new ServiceEndPointException("There is no service end point for such information" + e.toString()); + } + } + + + } + + /** + * Retrieve the right DB information. + * + * @param resources the resources + * @throws ServiceEndPointException the service end point exception + * @throws NoDataCatalogueRuntimeResourceException the no data catalogue runtime resource exception + */ + private void evaluateRightConfigurationDB(List resources) throws ServiceEndPointException, NoDataCatalogueRuntimeResourceException { + + if (resources.size() == 0){ + throw new NoDataCatalogueRuntimeResourceException("There is no Runtime Resource having name " + RUNTIME_DB_RESOURCE_NAME +" and Platform " + PLATFORM_DB_NAME + " in this scope."); + } + else { + try{ + + if(resources.size() > 1){ + boolean oneWasMaster = false; + + logger.info("Too many Runtime Resource having name " + RUNTIME_DB_RESOURCE_NAME +" in this scope.. Looking for the one that has the property " + IS_MASTER_ROOT_KEY_PROPERTY); + + for (ServiceEndpoint res : resources) { + + Iterator accessPointIterator = res.profile().accessPoints().iterator(); + + while (accessPointIterator.hasNext()) { + ServiceEndpoint.AccessPoint accessPoint = accessPointIterator + .next(); + + // get the is master property + Property entry = accessPoint.propertyMap().get(IS_MASTER_ROOT_KEY_PROPERTY); + String isMaster = entry != null ? entry.value() : null; + + if(isMaster == null || !isMaster.equals("true")) + continue; + + // set this variable + oneWasMaster = true; + + // add this host + hostsDB.add(accessPoint.address().split(":")[0]); + + // save the port + int port = Integer.parseInt(accessPoint.address().split(":")[1]); + portsDB.add(port); + + // save the name of the cluster (this should be unique) + nameDB = accessPoint.name(); + + // save user and password + passwordDB = StringEncrypter.getEncrypter().decrypt(accessPoint.password()); + userDB = accessPoint.username(); + + // now break + break; + } + } + + // if none of them was master, throw an exception + if(!oneWasMaster) + throw new NoApplicationProfileMasterException("There is no application profile with MASTER property"); + }else{ + logger.debug(resources.toString()); + ServiceEndpoint res = resources.get(0); + Iterator accessPointIterator = res.profile().accessPoints().iterator(); + + while (accessPointIterator.hasNext()) { + ServiceEndpoint.AccessPoint accessPoint = accessPointIterator + .next(); + + // add this host + hostsDB.add(accessPoint.address().split(":")[0]); + + // save the port + int port = Integer.parseInt(accessPoint.address().split(":")[1]); + portsDB.add(port); + + // save the name of the cluster (this should be unique) + nameDB = accessPoint.name(); + + // save user and password + passwordDB = StringEncrypter.getEncrypter().decrypt(accessPoint.password()); + userDB = accessPoint.username(); + + } + } + }catch(Exception e ){ + throw new ServiceEndPointException(e.toString()); + } + } + } + + /** + * Retrieve endpoints information from IS for DB. + * + * @return list of endpoints for ckan database + * @throws Exception the exception + */ + private static List getConfigurationFromISFORDB() throws Exception{ + + SimpleQuery query = queryFor(ServiceEndpoint.class); + query.addCondition("$resource/Profile/Name/text() eq '"+ RUNTIME_DB_RESOURCE_NAME +"'"); + query.addCondition("$resource/Profile/Platform/Name/text() eq '"+ PLATFORM_DB_NAME +"'"); + DiscoveryClient client = clientFor(ServiceEndpoint.class); + List toReturn = client.submit(query); + return toReturn; + + } + + /** + * Retrieve endpoints information from IS for DataCatalogue URL. + * + * @return list of endpoints for ckan data catalogue + * @throws Exception the exception + */ + private static List getConfigurationFromISFORCatalogueUrl() throws Exception{ + + SimpleQuery query = queryFor(ServiceEndpoint.class); + query.addCondition("$resource/Profile/Name/text() eq '"+ RUNTIME_CATALOGUE_RESOURCE_NAME +"'"); + query.addCondition("$resource/Profile/Platform/Name/text() eq '"+ PLATFORM_CATALOGUE_NAME +"'"); + DiscoveryClient client = clientFor(ServiceEndpoint.class); + List toReturn = client.submit(query); + return toReturn; + + } + + /** + * Retrieve the url of the ckan portlet deployed into this scope + * updated by Francesco see Feature #18039. + * + * @return the portlet url from infrastrucure + */ + private static Map getPortletUrlFromInfrastrucure() { + + String scope = ScopeProvider.instance.get(); + logger.debug("Trying to fetch applicationProfile profile from the infrastructure for " + APPLICATION_PROFILE_NAME + " scope: " + scope); + Map mapAccess = new HashMap(3); + try { + + Query q = new QueryBox("for $profile in collection('/db/Profiles/GenericResource')//Resource " + + "where $profile/Profile/SecondaryType/string() eq 'ApplicationProfile' and $profile/Profile/Name/string() " + + " eq '" + APPLICATION_PROFILE_NAME + "'" + + "return $profile"); + + DiscoveryClient client = client(); + List appProfile = client.submit(q); + + if (appProfile == null || appProfile.size() == 0) + throw new ApplicationProfileNotFoundException("Your applicationProfile is not registered in the infrastructure"); + else { + String elem = appProfile.get(0); + DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + Node node = docBuilder.parse(new InputSource(new StringReader(elem))).getDocumentElement(); + XPathHelper helper = new XPathHelper(node); + + List currValue = null; + currValue = helper.evaluate("/Resource/Profile/Body/url/text()"); + if (currValue != null && currValue.size() > 0) { + logger.debug("Private portlet url found is " + currValue.get(0)); + //return currValue.get(0); + mapAccess.put(ACCESS_LEVEL_TO_CATALOGUE_PORTLET.PRIVATE_VRE, currValue.get(0)); + } + + currValue = null; + currValue = helper.evaluate("/Resource/Profile/Body/"+PUBLIC_VRE_CATALOGUE_URL+"/text()"); + if (currValue != null && currValue.size() > 0) { + logger.debug(PUBLIC_VRE_CATALOGUE_URL+" portlet url found is " + currValue.get(0)); + //return currValue.get(0); + mapAccess.put(ACCESS_LEVEL_TO_CATALOGUE_PORTLET.PUBLIC_VRE, currValue.get(0)); + } + + } + + } catch (Exception e) { + logger.error("Error while trying to fetch applicationProfile profile from the infrastructure", e); + } + + return mapAccess; + + } + + /** + * Parse the CatalogueDelegateRoles in this context. + * + * @throws ParserConfigurationException the parser configuration exception + * @throws SAXException the SAX exception + * @throws IOException Signals that an I/O exception has occurred. + */ + private void parseExtendingRoles() throws ParserConfigurationException, SAXException, IOException { + + Query q = new QueryBox("for $profile in collection('/db/Profiles/GenericResource')//Resource " + + "where $profile/Profile/SecondaryType/string() eq 'ApplicationProfile' and $profile/Profile/Name/string() " + + " eq '" + CATALOGUE_EXTENDING_ROLES + "'" + + "return $profile"); + + DiscoveryClient client = client(); + List appProfile = client.submit(q); + + logger.debug("Resource for extending role has size " + appProfile.size()); + + if (appProfile == null || appProfile.size() == 0) + return; + else { + String profile = appProfile.get(0); + DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + Node node = docBuilder.parse(new InputSource(new StringReader(profile))).getDocumentElement(); + XPathHelper helper = new XPathHelper(node); + + // fetch delegate elements + NodeList delegates = helper.evaluateForNodes("/Resource/Profile/Body/delegates/delegate"); + if (delegates != null && delegates.getLength() > 0) { + + for(int i = 0; i < delegates.getLength(); i++){ + + Node nodeI = delegates.item(i); + + if(nodeI.getNodeType() == Node.ELEMENT_NODE) { + + Element elem = (Element)nodeI; + String role = elem.getElementsByTagName("sourceRole").item(0).getTextContent(); + String destOrg = elem.getElementsByTagName("destOrganization").item(0).getTextContent(); + String sourceOrg = elem.getElementsByTagName("sourceOrganization").item(0).getTextContent(); + + if(destOrg == null || sourceOrg == null){ + logger.warn("DestOrg or SourceOrg parameters missing"); + continue; + }else{ + String currentValueForKey = extendRoleInOrganization.get(sourceOrg); + + if(currentValueForKey == null) + currentValueForKey = ""; + else + currentValueForKey += TUPLES_SEPARATOR; + + currentValueForKey += destOrg + ROLE_ORGANIZATION_SEPARATOR + role; + extendRoleInOrganization.put(sourceOrg, currentValueForKey); + } + } + } + } + + } + + logger.debug("Extended role map in this scope is " + extendRoleInOrganization); + } + + /** + * Retrieve the ckan portlet url. + * + * @return the portletUrl + */ + public String getPortletUrl() { + return portletUrl; + } + + /** + * Retrieve data catalogue url. + * + * @return the data catalogue url + */ + public List getDataCatalogueUrl() { + return datacatalogueUrls; + } + + /** + * Get the hosts for such resource. + * + * @return the database hosts + */ + public List getDatabaseHosts() { + return hostsDB; + } + + /** + * Get the ports for such resource. + * + * @return the database ports + */ + public List getDatabasePorts() { + return portsDB; + } + + /** + * Get the database name. + * + * @return the data base name + */ + public String getDataBaseName() { + return nameDB; + } + + /** + * Get the database's user. + * + * @return the data base user + */ + public String getDataBaseUser() { + return userDB; + } + + /** + * Get the database's password. + * + * @return the data base password + */ + public String getDataBasePassword() { + return passwordDB; + } + + /** + * Gets the sys admin token. + * + * @return the sysAdminToken + */ + public String getSysAdminToken() { + return sysAdminToken; + } + + /** + * Is manager product enabled (e.g., for GRSF records) + * + * @return true, if is manage product enabled + */ + public boolean isManageProductEnabled() { + return manageProductEnabled; + } + + /** + * Get the url of the uri resolver for this instance/scope. + * + * @return the url resolver + */ + public String getUrlResolver() { + return urlResolver; + } + + /** + * Check if alert user members is enabled. + * + * @return true, if is alert enabled + */ + public boolean isAlertEnabled() { + return alertUsers; + } + + + /** + * Get roles to extend. + * + * @return Map + */ + public Map getExtendRoleInOrganization() { + return extendRoleInOrganization; + } + + /** + * Get the solr index base url. + * + * @return the url solr + */ + public String getUrlSolr() { + return urlSolr; + } + + + /** + * Gets the map access URL to catalogue. + * + * @return the map access URL to catalogue + */ + public Map getMapAccessURLToCatalogue() { + return mapAccessURLToCatalogue; + } + + + /** + * Get the catalogue email. Default is "catalogue@d4science.org" + * + * @return the email catalogue + */ + public String getEmailCatalogue() { + logger.debug("Read email from resource is " + catalogueEmail); + return catalogueEmail == null? DEFAULT_CATALOGUE_EMAIL : catalogueEmail; + } + + /** + * Checks if is social post enabled. + * + * @return true, if is social post enabled + */ + public boolean isSocialPostEnabled() { + return socialPost; + } + +} diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/utils/CatalogueUtilMethods.class b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/utils/CatalogueUtilMethods.class new file mode 100644 index 0000000..4638243 Binary files /dev/null and b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/utils/CatalogueUtilMethods.class differ diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/utils/CatalogueUtilMethods.java b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/utils/CatalogueUtilMethods.java new file mode 100644 index 0000000..9136507 --- /dev/null +++ b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/utils/CatalogueUtilMethods.java @@ -0,0 +1,139 @@ +package org.gcube.datacatalogue.ckanutillibrary.server.utils; + +import java.net.HttpURLConnection; +import java.net.URL; + +import org.gcube.common.scope.api.ScopeProvider; +import org.gcube.common.scope.impl.ScopeBean; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Some utility methods used within the library. + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class CatalogueUtilMethods { + + private static final Logger logger = LoggerFactory.getLogger(CatalogueUtilMethods.class); + private final static String HTTPS = "https"; + private final static String HTTP = "http"; + + /** + * Maps the scope name to the ckan organization name + * Use getCKANOrganization() which uses current scope + * @return + */ + @Deprecated + public static String getOrganizationNameFromScope(String scope){ + if(scope == null || scope.isEmpty()) { + throw new IllegalArgumentException("scope cannot be null"); + } + ScopeBean scopeBean = new ScopeBean(scope); + return scopeBean.name().toLowerCase().replace(" ", "_").replace("-", "_"); + + } + + /** + * Get CKAN Organization name by using scope name + * + */ + public static String getCKANOrganization() { + ScopeBean scopeBean = new ScopeBean(ScopeProvider.instance.get()); + return scopeBean.name().toLowerCase().replace(" ", "_").replace("-", "_"); + } + + /** + * Ckan username has _ instead of . (that is, costantino.perciante -> costantino_perciante) + * @param owner + * @return + */ + public static String fromUsernameToCKanUsername(String username){ + if(username == null) + return null; + return username.trim().replaceAll("\\.", "_"); + } + + /** + * Liferay username has . instead of _ (that is, costantino_perciante -> costantino.perciante) + * @param owner + * @return + */ + public static String fromCKanUsernameToUsername(String ckanUsername){ + if(ckanUsername == null) + return null; + + return ckanUsername.trim().replaceAll("_", "."); + } + + /** + * Generate the catalogue's dataset name from its title + * @param title + * @return + */ + public static String fromProductTitleToName(String title) { + if(title == null) + return null; + + String regexTitleNameTransform = "[^A-Za-z0-9_-]"; + return title.trim().replaceAll(regexTitleNameTransform, "_").replaceAll("_+", "_").toLowerCase(); + } + + /** + * Convert a display group name to group id + * @param groupName + * @return + */ + public static String fromGroupTitleToName(String groupName){ + if(groupName == null) + return null; + + String regexGroupNameTransform = "[^A-Za-z0-9-]"; + String modified = groupName.trim().replaceAll(regexGroupNameTransform, "-").replaceAll("-+", "-").toLowerCase(); + + if(modified.startsWith("-")) + modified = modified.substring(1); + if(modified.endsWith("-")) + modified = modified.substring(0, modified.length() -1); + + return modified; + + } + + /** + * Utility method to check if a something at this url actually exists + * @param URLName + * @return + */ + public static boolean resourceExists(String URLName){ + + if(URLName == null || URLName.isEmpty()) + return false; + + try { + // replace https + String urlToTest = URLName.replace(HTTPS, HTTP); + HttpURLConnection.setFollowRedirects(true); + HttpURLConnection con = (HttpURLConnection) new URL(urlToTest).openConnection(); + con.setRequestMethod("HEAD"); + logger.debug("Return code is " + con.getResponseCode()); + return (con.getResponseCode() == HttpURLConnection.HTTP_OK); + } + catch (Exception e) { + logger.error("Exception while checking url", e); + return false; + } + } + + /** + * Builds a string made of key + scope + * @param key + * @param scope + * @return + */ + public static String concatenateSessionKeyScope(String key, String scope){ + if(key == null || scope == null) + throw new IllegalArgumentException("Key or scope null"); + return key.concat(scope); + } + +} diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/utils/SessionCatalogueAttributes.class b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/utils/SessionCatalogueAttributes.class new file mode 100644 index 0000000..ef5abee Binary files /dev/null and b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/utils/SessionCatalogueAttributes.class differ diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/utils/SessionCatalogueAttributes.java b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/utils/SessionCatalogueAttributes.java new file mode 100644 index 0000000..a141eb9 --- /dev/null +++ b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/utils/SessionCatalogueAttributes.java @@ -0,0 +1,17 @@ +package org.gcube.datacatalogue.ckanutillibrary.server.utils; + +/** + * A list of attributes that are saved into http session. + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class SessionCatalogueAttributes { + + // CKAN KEYS (PLEASE NOTE THAT MOST OF THESE INFO ARE SAVED INTO SESSION PER SCOPE) + public static final String CKAN_ORGS_USER_KEY = "ckanOrgs"; // organizations to whom he belongs (shown into the portlet) + public static final String CKAN_HIGHEST_ROLE = "ckanHighestRole"; // editor, member, admin + public static final String CKAN_ORGANIZATIONS_PUBLISH_KEY = "ckanOrganizationsPublish"; // here he can publish (admin/editor role) + public static final String CKAN_LICENSES_KEY = "ckanLicenses"; // licenses + public static final String CKAN_PROFILES_KEY = "ckanProfiles"; // product profiles + public static final String CKAN_GROUPS_MEMBER = "ckanGroupsMember"; + public static final String CKAN_GROUPS_USER_KEY = "ckanGroups"; // to show the list of groups in the portlet +} diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/utils/url/EntityContext.class b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/utils/url/EntityContext.class new file mode 100644 index 0000000..83efa90 Binary files /dev/null and b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/utils/url/EntityContext.class differ diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/utils/url/EntityContext.java b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/utils/url/EntityContext.java new file mode 100644 index 0000000..947d1d0 --- /dev/null +++ b/target/classes/org/gcube/datacatalogue/ckanutillibrary/server/utils/url/EntityContext.java @@ -0,0 +1,24 @@ +package org.gcube.datacatalogue.ckanutillibrary.server.utils.url; + +/** + * Entity context for uri resolver + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public enum EntityContext { + + DATASET("dataset"), + GROUP("group"), + ORGANIZATION("organization"); + + private String entityAsString; + + private EntityContext(String entityAsString) { + this.entityAsString = entityAsString; + } + + @Override + public String toString() { + return this.entityAsString; + } + +} diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/CKanUserWrapper.class b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/CKanUserWrapper.class new file mode 100644 index 0000000..9109cbd Binary files /dev/null and b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/CKanUserWrapper.class differ diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/CKanUserWrapper.java b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/CKanUserWrapper.java new file mode 100644 index 0000000..ccc7c49 --- /dev/null +++ b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/CKanUserWrapper.java @@ -0,0 +1,134 @@ +package org.gcube.datacatalogue.ckanutillibrary.shared; + +import java.io.Serializable; + +/** + * A CKan user. + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class CKanUserWrapper implements Serializable{ + + private static final long serialVersionUID = 6264706263035722775L; + + private String id; + private String name; + private String apiKey; + private long creationTimestamp; + private String about; + private String openId; + private String fullName; + private String email; + private boolean isAdmin; + + + public CKanUserWrapper() { + super(); + } + + /** Create a ckan user object. + * @param id + * @param name + * @param apiKey + * @param creationTimestamp + * @param about + * @param openId + * @param fullName + * @param email + * @param isAdmin + */ + public CKanUserWrapper(String id, String name, String apiKey, + long creationTimestamp, String about, String openId, + String fullName, String email, boolean isAdmin) { + super(); + this.id = id; + this.name = name; + this.apiKey = apiKey; + this.creationTimestamp = creationTimestamp; + this.about = about; + this.openId = openId; + this.fullName = fullName; + this.email = email; + this.isAdmin = isAdmin; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getApiKey() { + return apiKey; + } + + public void setApiKey(String apiKey) { + this.apiKey = apiKey; + } + + public long getCreationTimestamp() { + return creationTimestamp; + } + + public void setCreationTimestamp(long creationTimestamp) { + this.creationTimestamp = creationTimestamp; + } + + public String getAbout() { + return about; + } + + public void setAbout(String about) { + this.about = about; + } + + public String getOpenId() { + return openId; + } + + public void setOpenId(String openId) { + this.openId = openId; + } + + public String getFullName() { + return fullName; + } + + public void setFullName(String fullName) { + this.fullName = fullName; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public boolean isAdmin() { + return isAdmin; + } + + public void setAdmin(boolean isAdmin) { + this.isAdmin = isAdmin; + } + + @Override + public String toString() { + return "CKanUserExtended [id=" + id + ", name=" + name + ", apiKey=" + apiKey.substring(0, 5) + "****************" + + ", creationTimestamp=" + creationTimestamp + ", about=" + + about + ", openId=" + openId + ", fullName=" + fullName + + ", email=" + email + ", isAdmin=" + isAdmin + "]"; + } + +} diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/CkanDatasetRelationship.class b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/CkanDatasetRelationship.class new file mode 100644 index 0000000..4be9dfe Binary files /dev/null and b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/CkanDatasetRelationship.class differ diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/CkanDatasetRelationship.java b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/CkanDatasetRelationship.java new file mode 100644 index 0000000..c5d12d8 --- /dev/null +++ b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/CkanDatasetRelationship.java @@ -0,0 +1,96 @@ +package org.gcube.datacatalogue.ckanutillibrary.shared; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.json.simple.JSONObject; + +/** + * A ckan dataset relationship. It is represented by the following fields + *
    + *
  • subject dataset id + *
  • object dataset id + *
  • type of the relationship + *
  • comment an optional comment + *
+ * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class CkanDatasetRelationship { + + private String subject; + private String object; + private String comment; + private String type; + + + public CkanDatasetRelationship(){ + super(); + } + + /** + * @param subject + * @param object + * @param comment + * @param type + */ + public CkanDatasetRelationship(String subject, String object, + String comment, String type) { + super(); + this.subject = subject; + this.object = object; + this.comment = comment; + this.type = type; + } + + /** + * From a json object that must have the properties listed in the class header (comment is optional) + * @param object + */ + public CkanDatasetRelationship(JSONObject object) { + this.comment = (String) object.get("comment"); + this.object = (String) object.get("object"); + this.subject = (String) object.get("subject"); + this.type = (String) object.get("type"); // TODO convert to one of enums DatasetRelationships + checkNotNull(object); + checkNotNull(subject); + checkNotNull(type); + } + + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public String getObject() { + return object; + } + + public void setObject(String object) { + this.object = object; + } + + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + @Override + public String toString() { + return "CkanDatasetRelationship [subject=" + subject + ", object=" + + object + ", comment=" + comment + ", type=" + type + + "]"; + } +} diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/DatasetRelationships.class b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/DatasetRelationships.class new file mode 100644 index 0000000..9b4c981 Binary files /dev/null and b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/DatasetRelationships.class differ diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/DatasetRelationships.java b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/DatasetRelationships.java new file mode 100644 index 0000000..657fb09 --- /dev/null +++ b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/DatasetRelationships.java @@ -0,0 +1,27 @@ +package org.gcube.datacatalogue.ckanutillibrary.shared; + +/** + * Allowed relationships between packages(datasets). Some of them are not supported yet due to the problem + * reported here https://support.d4science.org/issues/4455 + *
    + *
  • depends_on + *
  • dependency_of + *
  • derives_from + *
  • has_derivation + *
  • child_of + *
  • parent_of + *
  • links_to + *
  • linked_from + *
+ * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public enum DatasetRelationships { + depends_on, + dependency_of, + derives_from, + has_derivation, + child_of, + parent_of, + links_to, + linked_from +} diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/LandingPages.class b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/LandingPages.class new file mode 100644 index 0000000..fde731f Binary files /dev/null and b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/LandingPages.class differ diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/LandingPages.java b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/LandingPages.java new file mode 100644 index 0000000..2e7695e --- /dev/null +++ b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/LandingPages.java @@ -0,0 +1,57 @@ +package org.gcube.datacatalogue.ckanutillibrary.shared; + +import java.io.Serializable; + +/** + * This bean offers the following information: + *
    + *
  • landing page url to the items page (within the portlet) + *
  • landing page url to the orgs page (within the portlet) + *
  • landing page url to the groups page (within the portlet) + *
  • landing page url to the types page (within the portlet) + *
+ * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class LandingPages implements Serializable { + + private static final long serialVersionUID = -5617896049674346109L; + private String urlTypes; + private String urlOrganizations; + private String urlGroups; + private String urlItems; + + public LandingPages() { + super(); + } + public String getUrlTypes() { + return urlTypes; + } + public void setUrlTypes(String urlTypes) { + this.urlTypes = urlTypes; + } + public String getUrlOrganizations() { + return urlOrganizations; + } + public void setUrlOrganizations(String urlOrganizations) { + this.urlOrganizations = urlOrganizations; + } + public String getUrlGroups() { + return urlGroups; + } + public void setUrlGroups(String urlGroups) { + this.urlGroups = urlGroups; + } + public String getUrlItems() { + return urlItems; + } + public void setUrlItems(String urlItems) { + this.urlItems = urlItems; + } + @Override + public String toString() { + return "LandingPages [urlTypes=" + urlTypes + ", urlOrganizations=" + + urlOrganizations + ", urlGroups=" + urlGroups + ", urlItems=" + + urlItems + "]"; + } + +} diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ResourceBean.class b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ResourceBean.class new file mode 100644 index 0000000..e1b4ccc Binary files /dev/null and b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ResourceBean.class differ diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ResourceBean.java b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ResourceBean.java new file mode 100644 index 0000000..2b124c3 --- /dev/null +++ b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ResourceBean.java @@ -0,0 +1,152 @@ +package org.gcube.datacatalogue.ckanutillibrary.shared; + +import java.io.Serializable; + +/** + * A bean that resembles the CKanResource bean object + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class ResourceBean implements Serializable { + + private static final long serialVersionUID = -5275448097250176185L; + private String url; + private String name; + private String description; + private String id; + private String owner; + private String datasetId; + private String mimeType; + + public ResourceBean(){ + super(); + } + + /** + * @param url + * @param name + * @param description + * @param id + * @param owner + * @param datasetId + * @param mimeType + */ + public ResourceBean(String url, String name, String description, String id, + String owner, String datasetId, String mimeType) { + super(); + this.url = url; + this.name = name; + this.description = description; + this.id = id; + this.owner = owner; + this.datasetId = datasetId; + this.mimeType = mimeType; + } + + /** + * @return the url + */ + public String getUrl() { + return url; + } + + /** + * @param url the url to set + */ + public void setUrl(String url) { + this.url = url; + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the description + */ + public String getDescription() { + return description; + } + + /** + * @param description the description to set + */ + public void setDescription(String description) { + this.description = description; + } + + /** + * @return the id + */ + public String getId() { + return id; + } + + /** + * @param id the id to set + */ + public void setId(String id) { + this.id = id; + } + + /** + * @return the owner + */ + public String getOwner() { + return owner; + } + + /** + * @param owner the owner to set + */ + public void setOwner(String owner) { + this.owner = owner; + } + + /** + * @return the datasetId + */ + public String getDatasetId() { + return datasetId; + } + + /** + * @param datasetId the datasetId to set + */ + public void setDatasetId(String datasetId) { + this.datasetId = datasetId; + } + + /** + * @return the mimeType + */ + public String getMimeType() { + return mimeType; + } + + /** + * @param mimeType the mimeType to set + */ + public void setMimeType(String mimeType) { + this.mimeType = mimeType; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "ResourceBean [url=" + url + ", name=" + name + ", description=" + + description + ", id=" + id + ", owner=" + owner + + ", datasetId=" + datasetId + ", mimeType=" + mimeType + "]"; + } +} diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/RolesCkanGroupOrOrg.class b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/RolesCkanGroupOrOrg.class new file mode 100644 index 0000000..c1c2b1c Binary files /dev/null and b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/RolesCkanGroupOrOrg.class differ diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/RolesCkanGroupOrOrg.java b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/RolesCkanGroupOrOrg.java new file mode 100644 index 0000000..196b655 --- /dev/null +++ b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/RolesCkanGroupOrOrg.java @@ -0,0 +1,39 @@ +package org.gcube.datacatalogue.ckanutillibrary.shared; + +/** + * Roles that user can have into organizations/groups. + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public enum RolesCkanGroupOrOrg{ + MEMBER, + EDITOR, + ADMIN; + + public static String convertToCkanCapacity(RolesCkanGroupOrOrg role){ + + if(role == null) + return null; + else + return role.toString().toLowerCase(); + + } + + public static RolesCkanGroupOrOrg convertFromCapacity(String capacity){ + + if(capacity == null) + return null; + else + return RolesCkanGroupOrOrg.valueOf(capacity.toUpperCase()); + + } + + /** + * Get the higher role between role1 and role2 + * @param role1 + * @param role2 + * @return the higher role + */ + public static RolesCkanGroupOrOrg getHigher(RolesCkanGroupOrOrg role1, RolesCkanGroupOrOrg role2){ + return role1.ordinal() > role2.ordinal() ? role1 : role2; + } +} \ No newline at end of file diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/State.class b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/State.class new file mode 100644 index 0000000..859c1c2 Binary files /dev/null and b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/State.class differ diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/State.java b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/State.java new file mode 100644 index 0000000..ddc8fc2 --- /dev/null +++ b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/State.java @@ -0,0 +1,10 @@ +package org.gcube.datacatalogue.ckanutillibrary.shared; + +/** + * The current state of this group/user + * @author Costantino Perciante at ISTI-CNR + * (costantino.perciante@isti.cnr.it) + */ +public enum State{ + DELETED, ACTIVE +} \ No newline at end of file diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/Statistics.class b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/Statistics.class new file mode 100644 index 0000000..143b400 Binary files /dev/null and b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/Statistics.class differ diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/Statistics.java b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/Statistics.java new file mode 100644 index 0000000..fee060d --- /dev/null +++ b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/Statistics.java @@ -0,0 +1,54 @@ +package org.gcube.datacatalogue.ckanutillibrary.shared; + +import java.io.Serializable; + +/** + * This bean offers the following statistics: + *
    + *
  • number of items of the catalogue + *
  • number of organizations of the catalogue + *
  • number of groups of the catalogue + *
  • number of types of the catalogue + *
+ * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class Statistics implements Serializable{ + + private static final long serialVersionUID = 2871906712366452266L; + private long numTypes; + private long numOrganizations; + private long numGroups; + private long numItems; + + public long getNumTypes() { + return numTypes; + } + public void setNumTypes(long numTypes) { + this.numTypes = numTypes; + } + public long getNumOrganizations() { + return numOrganizations; + } + public void setNumOrganizations(long numOrganizations) { + this.numOrganizations = numOrganizations; + } + public long getNumGroups() { + return numGroups; + } + public void setNumGroups(long numGroups) { + this.numGroups = numGroups; + } + public long getNumItems() { + return numItems; + } + public void setNumItems(long numItems) { + this.numItems = numItems; + } + @Override + public String toString() { + return "Statistics [numTypes=" + numTypes + ", numOrganizations=" + + numOrganizations + ", numGroups=" + numGroups + ", numItems=" + + numItems + "]"; + } + +} diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ex/ApplicationProfileNotFoundException.class b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ex/ApplicationProfileNotFoundException.class new file mode 100644 index 0000000..2716046 Binary files /dev/null and b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ex/ApplicationProfileNotFoundException.class differ diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ex/ApplicationProfileNotFoundException.java b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ex/ApplicationProfileNotFoundException.java new file mode 100644 index 0000000..384b652 --- /dev/null +++ b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ex/ApplicationProfileNotFoundException.java @@ -0,0 +1,12 @@ +package org.gcube.datacatalogue.ckanutillibrary.shared.ex; + +@SuppressWarnings("serial") +/** + * Thrown when no application profile with such information is found + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class ApplicationProfileNotFoundException extends Exception { + public ApplicationProfileNotFoundException(String message) { + super(message); + } +} diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ex/NoApplicationProfileMasterException.class b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ex/NoApplicationProfileMasterException.class new file mode 100644 index 0000000..fb41953 Binary files /dev/null and b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ex/NoApplicationProfileMasterException.class differ diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ex/NoApplicationProfileMasterException.java b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ex/NoApplicationProfileMasterException.java new file mode 100644 index 0000000..feca2ae --- /dev/null +++ b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ex/NoApplicationProfileMasterException.java @@ -0,0 +1,20 @@ +package org.gcube.datacatalogue.ckanutillibrary.shared.ex; + +/** + * Thrown when there are more than one application profile, but none of them was set as master + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class NoApplicationProfileMasterException extends Exception { + + private static final long serialVersionUID = 5874713540422734005L; + private static final String DEFAULT_MESSAGE = "There is more than one application profile into this scope" + + " but none of them is set as master!"; + + public NoApplicationProfileMasterException(){ + super(DEFAULT_MESSAGE); + } + + public NoApplicationProfileMasterException(String message) { + super(message); + } +} diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ex/NoDataCatalogueRuntimeResourceException.class b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ex/NoDataCatalogueRuntimeResourceException.class new file mode 100644 index 0000000..019fabc Binary files /dev/null and b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ex/NoDataCatalogueRuntimeResourceException.class differ diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ex/NoDataCatalogueRuntimeResourceException.java b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ex/NoDataCatalogueRuntimeResourceException.java new file mode 100644 index 0000000..1f35604 --- /dev/null +++ b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ex/NoDataCatalogueRuntimeResourceException.java @@ -0,0 +1,21 @@ +package org.gcube.datacatalogue.ckanutillibrary.shared.ex; + +/** + * No Data Catalogue node found. + * @author Costantino Perciante at ISTI-CNR + * (costantino.perciante@isti.cnr.it) + */ +public class NoDataCatalogueRuntimeResourceException extends Exception { + + private static final long serialVersionUID = -40748130477807648L; + + private static final String DEFAULT_MESSAGE = "No Data Catalogue instance for this scope!"; + + public NoDataCatalogueRuntimeResourceException(){ + super(DEFAULT_MESSAGE); + } + + public NoDataCatalogueRuntimeResourceException(String message) { + super(message); + } +} diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ex/ServiceEndPointException.class b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ex/ServiceEndPointException.class new file mode 100644 index 0000000..f05e43c Binary files /dev/null and b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ex/ServiceEndPointException.class differ diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ex/ServiceEndPointException.java b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ex/ServiceEndPointException.java new file mode 100644 index 0000000..bfcecaf --- /dev/null +++ b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ex/ServiceEndPointException.java @@ -0,0 +1,19 @@ +package org.gcube.datacatalogue.ckanutillibrary.shared.ex; + +/** + * Exception thrown when it is not possible retrieve information from the ServiceEndpoint + * related to the Data Catalogue + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class ServiceEndPointException extends Exception { + + private static final long serialVersionUID = 7057074369001221035L; + private static final String DEFAULT_MESSAGE = "Unable to retrieve information from Data Catalogue endpoint!"; + + public ServiceEndPointException(){ + super(DEFAULT_MESSAGE); + } + public ServiceEndPointException(String string) { + super(string); + } +} diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ex/TooManyRunningClustersException.class b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ex/TooManyRunningClustersException.class new file mode 100644 index 0000000..1c62e48 Binary files /dev/null and b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ex/TooManyRunningClustersException.class differ diff --git a/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ex/TooManyRunningClustersException.java b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ex/TooManyRunningClustersException.java new file mode 100644 index 0000000..4f73f02 --- /dev/null +++ b/target/classes/org/gcube/datacatalogue/ckanutillibrary/shared/ex/TooManyRunningClustersException.java @@ -0,0 +1,22 @@ +package org.gcube.datacatalogue.ckanutillibrary.shared.ex; + +/** + * Too many clusters in this scope exception. + * @author Costantino Perciante at ISTI-CNR + * (costantino.perciante@isti.cnr.it) + * + */ +public class TooManyRunningClustersException extends Exception { + + private static final long serialVersionUID = -7847493730006647045L; + private static final String DEFAULT_MESSAGE = "Too many CKan data catalague instances for this scope!"; + + public TooManyRunningClustersException(){ + super(DEFAULT_MESSAGE); + } + + public TooManyRunningClustersException(String message) { + super(message); + } + +}