diff --git a/core/src/main/java/org/fao/geonet/kernel/SchemaManager.java b/core/src/main/java/org/fao/geonet/kernel/SchemaManager.java index 1489c367132..77173317ffa 100644 --- a/core/src/main/java/org/fao/geonet/kernel/SchemaManager.java +++ b/core/src/main/java/org/fao/geonet/kernel/SchemaManager.java @@ -1,29 +1,25 @@ -//============================================================================= -//=== -//=== SchemaManager -//=== -//============================================================================= -//=== Copyright (C) 2001-2011 Food and Agriculture Organization of the -//=== United Nations (FAO-UN), United Nations World Food Programme (WFP) -//=== and United Nations Environment Programme (UNEP) -//=== -//=== This program is free software; you can redistribute it and/or modify -//=== it under the terms of the GNU General Public License as published by -//=== the Free Software Foundation; either version 2 of the License, or (at -//=== your option) any later version. -//=== -//=== This program is distributed in the hope that it will be useful, but -//=== WITHOUT ANY WARRANTY; without even the implied warranty of -//=== MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -//=== General Public License for more details. -//=== -//=== You should have received a copy of the GNU General Public License -//=== along with this program; if not, write to the Free Software -//=== Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA -//=== -//=== Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, -//=== Rome - Italy. email: geonetwork@osgeo.org -//============================================================================== +/* + * Copyright (C) 2001-2023 Food and Agriculture Organization of the + * United Nations (FAO-UN), United Nations World Food Programme (WFP) + * and United Nations Environment Programme (UNEP) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, + * Rome - Italy. email: geonetwork@osgeo.org + */ package org.fao.geonet.kernel; @@ -42,6 +38,7 @@ import org.fao.geonet.exceptions.OperationAbortedEx; import org.fao.geonet.exceptions.SchemaMatchConflictException; import org.fao.geonet.kernel.schema.MetadataSchema; +import org.fao.geonet.kernel.schema.MetadataSchemaOperationFilter; import org.fao.geonet.kernel.schema.SchemaLoader; import org.fao.geonet.kernel.schema.SchemaPlugin; import org.fao.geonet.kernel.setting.SettingInfo; @@ -107,8 +104,8 @@ public class SchemaManager { * Active writers count */ private static int activeWriters = 0; - private Map hmSchemas = new HashMap(); - private Map hmSchemasTypenames = new HashMap(); + private Map hmSchemas = new HashMap<>(); + private Map hmSchemasTypenames = new HashMap<>(); private String[] fnames = {"labels.xml", "codelists.xml", "strings.xml"}; private Path schemaPluginsDir; private Path schemaPluginsCat; @@ -271,11 +268,11 @@ public MetadataSchema getSchema(String name) { try { Schema schema = hmSchemas.get(name); - if (schema == null) + if (schema == null) { throw new IllegalArgumentException("Schema not registered : " + name); + } - final MetadataSchema mds = schema.getMetadataSchema(); - return mds; + return schema.getMetadataSchema(); } finally { afterRead(); } @@ -288,7 +285,7 @@ public MetadataSchema getSchema(String name) { */ public Set getDependencies(String name) { - Set dependencies = new HashSet(); + Set dependencies = new HashSet<>(); beforeRead(); try { @@ -541,7 +538,7 @@ public List getConversionElements(String name) throws Exception { try { Schema schema = hmSchemas.get(name); List childs = schema.getConversionElements(); - List dChilds = new ArrayList(); + List dChilds = new ArrayList<>(); for (Element child : childs) { if (child != null) dChilds.add((Element) child.clone()); } @@ -712,40 +709,35 @@ public String autodetectSchema(Element md, String defaultSchema) throws SchemaMa // -- specific test first, then in order of increasing generality, // -- first match wins schema = compareElementsAndAttributes(md, MODE_ATTRIBUTEWITHVALUE); - if (schema != null) { - if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) - Log.debug(Geonet.SCHEMA_MANAGER, " => Found schema " + schema + " using AUTODETECT(attributes) examination"); + if (schema != null && Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) { + Log.debug(Geonet.SCHEMA_MANAGER, " => Found schema " + schema + " using AUTODETECT(attributes) examination"); } if (schema == null) { schema = compareElementsAndAttributes(md, MODE_NEEDLEWITHVALUE); - if (schema != null) { - if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) - Log.debug(Geonet.SCHEMA_MANAGER, " => Found schema " + schema + " using AUTODETECT(elements with value) examination"); + if (schema != null && Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) { + Log.debug(Geonet.SCHEMA_MANAGER, " => Found schema " + schema + " using AUTODETECT(elements with value) examination"); } } if (schema == null) { schema = compareElementsAndAttributes(md, MODE_NEEDLE); - if (schema != null) { - if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) - Log.debug(Geonet.SCHEMA_MANAGER, " => Found schema " + schema + " using AUTODETECT(elements) examination"); + if (schema != null && Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) { + Log.debug(Geonet.SCHEMA_MANAGER, " => Found schema " + schema + " using AUTODETECT(elements) examination"); } } if (schema == null) { schema = compareElementsAndAttributes(md, MODE_ROOT); - if (schema != null) { - if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) - Log.debug(Geonet.SCHEMA_MANAGER, " => Found schema " + schema + " using AUTODETECT(elements with root) examination"); + if (schema != null && Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) { + Log.debug(Geonet.SCHEMA_MANAGER, " => Found schema " + schema + " using AUTODETECT(elements with root) examination"); } } if (schema == null) { schema = compareElementsAndAttributes(md, MODE_NAMESPACE); - if (schema != null) { - if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) - Log.debug(Geonet.SCHEMA_MANAGER, " => Found schema " + schema + " using AUTODETECT(namespaces) examination"); + if (schema != null && Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) { + Log.debug(Geonet.SCHEMA_MANAGER, " => Found schema " + schema + " using AUTODETECT(namespaces) examination"); } } @@ -789,8 +781,9 @@ private String checkNamespace(Element md, String schema) { MetadataSchema mds = getSchema(schema); if (mds != null) { String primeNs = mds.getPrimeNS(); - if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) + if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) { Log.debug(Geonet.SCHEMA_MANAGER, " primeNs " + primeNs + " for schema " + schema); + } if (md.getNamespace().getURI().equals(primeNs)) { result = schema; } else { @@ -800,8 +793,9 @@ private String checkNamespace(Element md, String schema) { Schema sch = hmSchemas.get(schema); List dependsList = sch.getDependElements(); for (Element depends : dependsList) { - if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) + if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) { Log.debug(Geonet.SCHEMA_MANAGER, " checkNamespace for dependency: " + depends.getText()); + } return checkNamespace(md, depends.getText()); } } @@ -869,7 +863,7 @@ private void realDeletePluginSchema(String name, boolean doDependencies) throws if (schema != null) { if (doDependencies) { List dependsOnMe = getSchemasThatDependOnMe(name); - if (dependsOnMe.size() > 0) { + if (!dependsOnMe.isEmpty()) { String errStr = "Cannot remove schema " + name + " because the following schemas list it as a dependency: " + dependsOnMe; Log.error(Geonet.SCHEMA_MANAGER, errStr); throw new OperationAbortedEx(errStr); @@ -959,19 +953,21 @@ private void addSchema(ApplicationContext applicationContext, Path schemaDir, El final String schemaName = schemaDir.getFileName().toString(); Path locBase = schemaDir.resolve("loc"); - Map xfMap = new HashMap(); + Map xfMap = new HashMap<>(); for (String fname : fnames) { Path filePath = path.resolve("loc").resolve(defaultLang).resolve(fname); - if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) + if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) { Log.debug(Geonet.SCHEMA_MANAGER, "Searching for " + filePath); + } if (Files.exists(filePath)) { Element config = new Element("xml"); config.setAttribute("name", schemaName); config.setAttribute("base", locBase.toUri().toString()); config.setAttribute("file", fname); - if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) + if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) { Log.debug(Geonet.SCHEMA_MANAGER, "Adding XmlFile " + Xml.getString(config)); + } XmlFile xf = new XmlFile(config, defaultLang, true); xfMap.put(fname, xf); } else { @@ -1078,8 +1074,9 @@ private Element deleteSchemaFromPluginCatalog(String name, Element root) throws else continue; // skip this if (!uri.getName().equals("uri") || !uri.getNamespace().equals(Namespaces.OASIS_CATALOG)) { - if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) + if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) { Log.debug(Geonet.SCHEMA_MANAGER, "Skipping element " + uri.getQualifiedName() + ":" + uri.getNamespace()); + } continue; } @@ -1113,8 +1110,9 @@ private int getHighestSchemaPluginCatalogId(String name, Element root) throws Ex else continue; // skip this if (!uri.getName().equals("rewriteURI") || !uri.getNamespace().equals(Namespaces.OASIS_CATALOG)) { - if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) + if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) { Log.debug(Geonet.SCHEMA_MANAGER, "Skipping element " + uri.getQualifiedName() + ":" + uri.getNamespace()); + } continue; } @@ -1122,8 +1120,9 @@ private int getHighestSchemaPluginCatalogId(String name, Element root) throws Ex if (uri.getAttributeValue("rewritePrefix").equals(ourUri.toString())) return -1; String nameAttr = uri.getAttributeValue("uriStartString"); - if (nameAttr.startsWith(Geonet.File.METADATA_BLANK)) { - if (nameAttr.compareTo(baseBlank) > 0) baseBlank = nameAttr; + if (nameAttr.startsWith(Geonet.File.METADATA_BLANK) && + nameAttr.compareTo(baseBlank) > 0) { + baseBlank = nameAttr; } } @@ -1318,17 +1317,17 @@ private void processSchema(ApplicationContext applicationContext, Path schemasDi * Check dependencies for all schemas - remove those that fail. */ private void checkDependencies(Element schemaPluginCatRoot) throws Exception { - List removes = new ArrayList(); + List removes = new ArrayList<>(); // process each schema to see whether its dependencies are present - for (String schemaName : hmSchemas.keySet()) { - Schema schema = hmSchemas.get(schemaName); + for (Map.Entry schemaInfo : hmSchemas.entrySet()) { + Schema schema = schemaInfo.getValue(); try { - checkDepends(schemaName, schema.getDependElements()); + checkDepends(schemaInfo.getKey(), schema.getDependElements()); } catch (Exception e) { Log.error(Geonet.SCHEMA_MANAGER, "check dependencies failed: " + e.getMessage()); // add the schema to list for removal - removes.add(schemaName); + removes.add(schemaInfo.getKey()); } } @@ -1341,7 +1340,7 @@ private void checkDependencies(Element schemaPluginCatRoot) throws Exception { } private void checkAppSupported(Element schemaPluginCatRoot) throws Exception { - List removes = new ArrayList(); + List removes = new ArrayList<>(); final SystemInfo systemInfo = ApplicationContextHolder.get().getBean(SystemInfo.class); @@ -1349,17 +1348,17 @@ private void checkAppSupported(Element schemaPluginCatRoot) throws Exception { Version appVersion = Version.parseVersionNumber(version); // process each schema to see whether its dependencies are present - for (String schemaName : hmSchemas.keySet()) { - Schema schema = hmSchemas.get(schemaName); + for (Map.Entry schemaInfo : hmSchemas.entrySet()) { + Schema schema = schemaInfo.getValue(); String minorAppVersionSupported = schema.getMetadataSchema().getAppMinorVersionSupported(); Version schemaMinorAppVersion = Version.parseVersionNumber(minorAppVersionSupported); if (appVersion.compareTo(schemaMinorAppVersion) < 0) { - Log.error(Geonet.SCHEMA_MANAGER, "Schema " + schemaName + + Log.error(Geonet.SCHEMA_MANAGER, "Schema " + schemaInfo.getKey() + " requires min Geonetwork version: " + minorAppVersionSupported + ", current is: " + version + ". Skip load schema."); - removes.add(schemaName); + removes.add(schemaInfo.getKey()); continue; } @@ -1368,10 +1367,10 @@ private void checkAppSupported(Element schemaPluginCatRoot) throws Exception { Version schemaMajorAppVersion = Version.parseVersionNumber(majorAppVersionSupported); if (appVersion.compareTo(schemaMajorAppVersion) > 0) { - Log.error(Geonet.SCHEMA_MANAGER, "Schema " + schemaName + + Log.error(Geonet.SCHEMA_MANAGER, "Schema " + schemaInfo.getKey() + " requires max Geonetwork version: " + majorAppVersionSupported + ", current is: " + version + ". Skip load schema."); - removes.add(schemaName); + removes.add(schemaInfo.getKey()); continue; } } @@ -1396,17 +1395,17 @@ private void checkAppSupported(Element schemaPluginCatRoot) throws Exception { */ public List getSchemasThatDependOnMe(String schemaName) { - List myDepends = new ArrayList(); + List myDepends = new ArrayList<>(); // process each schema to see whether its dependencies are present - for (String schemaNameToTest : hmSchemas.keySet()) { - if (schemaNameToTest.equals(schemaName)) continue; + for (Map.Entry schemaInfoToTest : hmSchemas.entrySet()) { + if (schemaInfoToTest.getKey().equals(schemaName)) continue; - Schema schema = hmSchemas.get(schemaNameToTest); + Schema schema = schemaInfoToTest.getValue(); List dependsList = schema.getDependElements(); for (Element depends : dependsList) { if (depends.getText().equals(schemaName)) { - myDepends.add(schemaNameToTest); + myDepends.add(schemaInfoToTest.getKey()); } } } @@ -1424,7 +1423,7 @@ private void checkDepends(String thisSchema, List dependsList) throws E // process each dependency to see whether it is present for (Element depends : dependsList) { String schema = depends.getText(); - if (schema.length() > 0) { + if (StringUtils.isNotBlank(schema)) { if (!hmSchemas.containsKey(schema)) { throw new IllegalArgumentException("Schema " + thisSchema + " depends on " + schema + ", but that schema is not loaded"); } @@ -1444,7 +1443,7 @@ private List extractDepends(Path xmlIdFile) throws Exception { // get list of depends elements from schema-ident.xml List dependsList = root.getChildren("depends", GEONET_SCHEMA_NS); - if (dependsList.size() == 0) { + if (dependsList.isEmpty()) { dependsList = root.getChildren("depends", GEONET_SCHEMA_PREFIX_NS); } return dependsList; @@ -1502,11 +1501,11 @@ private Map getSchemaIdentMultilingualProperty(Element root, Str * true if schema requires to synch the uuid column schema info with the uuid in the metadata * record (updated on editing or in UFO). */ - private Map> extractOperationFilters(Path xmlIdFile) throws Exception { + private Map extractOperationFilters(Path xmlIdFile) throws Exception { Element root = Xml.loadFile(xmlIdFile); Element filters = root.getChild("filters", GEONET_SCHEMA_NS); - Map> filterRules = - new HashMap>(); + Map filterRules = + new HashMap<>(); if (filters == null) { return filterRules; } else { @@ -1514,11 +1513,14 @@ private Map> extractOperationFilters(Path xmlIdFil if (rule instanceof Element) { Element ruleElement = (Element) rule; String xpath = ruleElement.getAttributeValue("xpath"); + String jsonpath = ruleElement.getAttributeValue("jsonpath"); String ifNotOperation = ruleElement.getAttributeValue("ifNotOperation"); Element markedElement = ruleElement.getChild("keepMarkedElement", GEONET_SCHEMA_NS); + if (StringUtils.isNotBlank(ifNotOperation) && StringUtils.isNotBlank(xpath)) { - filterRules.put(ifNotOperation, Pair.read(xpath, markedElement)); + MetadataSchemaOperationFilter filter = new MetadataSchemaOperationFilter(xpath, jsonpath, ifNotOperation, markedElement); + filterRules.put(ifNotOperation, filter); } } } @@ -1574,11 +1576,12 @@ private List extractConvElements(Path xmlConvFile) throws Exception { if (!Files.exists(xmlConvFile)) { if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) Log.debug(Geonet.SCHEMA_MANAGER, "Schema conversions file not present"); - return new ArrayList(); + return new ArrayList<>(); } else { Element root = Xml.loadFile(xmlConvFile); - if (root.getName() != "conversions") + if (!root.getName().equals("conversions")) { throw new IllegalArgumentException("Schema conversions file " + xmlConvFile + " is invalid, no root element"); + } @SuppressWarnings("unchecked") List result = root.getChildren(); return result; @@ -1609,8 +1612,9 @@ private String compareElementsAndAttributes(Element md, int mode) throws SchemaM Set allSchemas = getSchemas(); List matches = new ArrayList<>(); - if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) + if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) { Log.debug(Geonet.SCHEMA_MANAGER, "Schema autodetection starting on " + md.getName() + " (Namespace: " + md.getNamespace() + ") using mode: " + mode + "..."); + } for (String schemaName : allSchemas) { if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) @@ -1619,8 +1623,9 @@ private String compareElementsAndAttributes(Element md, int mode) throws SchemaM List adElems = schema.getAutodetectElements(); for (Element elem : adElems) { - if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) + if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) { Log.debug(Geonet.SCHEMA_MANAGER, " Checking autodetect element " + Xml.getString(elem) + " with name " + elem.getName()); + } @SuppressWarnings("unchecked") List elemKids = elem.getChildren(); @@ -1629,12 +1634,13 @@ private String compareElementsAndAttributes(Element md, int mode) throws SchemaM Attribute type = elem.getAttribute("type"); // --- try and find the attribute and value in md - if (mode == MODE_ATTRIBUTEWITHVALUE && elem.getName() == "attributes") { + if (mode == MODE_ATTRIBUTEWITHVALUE && elem.getName().equals("attributes")) { @SuppressWarnings("unchecked") List atts = elem.getAttributes(); for (Attribute searchAtt : atts) { - if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) + if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) { Log.debug(Geonet.SCHEMA_MANAGER, " Finding attribute " + searchAtt.toString()); + } if (isMatchingAttributeInMetadata(searchAtt, md)) { match = true; @@ -1649,8 +1655,9 @@ private String compareElementsAndAttributes(Element md, int mode) throws SchemaM @SuppressWarnings("unchecked") List nss = elem.getAdditionalNamespaces(); for (Namespace ns : nss) { - if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) + if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) { Log.debug(Geonet.SCHEMA_MANAGER, " Finding namespace " + ns.toString()); + } if (isMatchingNamespaceInMetadata(ns, md)) { match = true; @@ -1664,8 +1671,9 @@ private String compareElementsAndAttributes(Element md, int mode) throws SchemaM // --- is the kid the same as the root of the md if (mode == MODE_ROOT && type != null && "root".equals(type.getValue())) { - if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) + if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) { Log.debug(Geonet.SCHEMA_MANAGER, " Comparing " + Xml.getString(kid) + " with " + md.getName() + " with namespace " + md.getNamespace() + " : " + (kid.getName().equals(md.getName()) && kid.getNamespace().equals(md.getNamespace()))); + } if (kid.getName().equals(md.getName()) && kid.getNamespace().equals(md.getNamespace())) { match = true; @@ -1675,8 +1683,9 @@ private String compareElementsAndAttributes(Element md, int mode) throws SchemaM } // --- try and find the kid in the md (kid only, not value) } else if (mode == MODE_NEEDLE && type != null && "search".equals(type.getValue())) { - if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) + if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) { Log.debug(Geonet.SCHEMA_MANAGER, " Comparing " + Xml.getString(kid) + " with " + md.getName() + " with namespace " + md.getNamespace() + " : " + (kid.getName().equals(md.getName()) && kid.getNamespace().equals(md.getNamespace()))); + } if (isMatchingElementInMetadata(kid, md, false)) { match = true; @@ -1720,8 +1729,9 @@ private boolean isMatchingAttributeInMetadata(Attribute needle, Element haystack @SuppressWarnings("unchecked") Iterator haystackIterator = haystack.getDescendants(new ElementFilter()); - if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) + if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) { Log.debug(Geonet.SCHEMA_MANAGER, "Matching " + needle.toString()); + } while (haystackIterator.hasNext()) { Element tempElement = haystackIterator.next(); @@ -1742,8 +1752,9 @@ private boolean isMatchingAttributeInMetadata(Attribute needle, Element haystack * @param haystack the XML metadata record we are searching */ private boolean isMatchingNamespaceInMetadata(Namespace needle, Element haystack) { - if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) + if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) { Log.debug(Geonet.SCHEMA_MANAGER, "Matching " + needle.toString()); + } if (checkNamespacesOnElement(needle, haystack)) return true; @@ -1797,8 +1808,9 @@ private boolean isMatchingElementInMetadata(Element needle, Element haystack, bo if (tempElement.getName().equals(needleName) && tempElement.getNamespace().equals(needleNS)) { if (checkValue) { - if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) + if (Log.isDebugEnabled(Geonet.SCHEMA_MANAGER)) { Log.debug(Geonet.SCHEMA_MANAGER, " Searching value for element: " + tempElement.getName()); + } String needleVal = StringUtils.deleteWhitespace(needle.getValue()); String tempVal = StringUtils.deleteWhitespace(tempElement.getValue()); @@ -1923,7 +1935,6 @@ public List getListOfTypeNames() { List listOfTypenames = new ArrayList<>(); while (iterator.hasNext()) { String typeName = iterator.next(); - Namespace ns = hmSchemasTypenames.get(typeName); listOfTypenames.add(typeName); } return listOfTypenames; diff --git a/core/src/main/java/org/fao/geonet/kernel/XmlSerializer.java b/core/src/main/java/org/fao/geonet/kernel/XmlSerializer.java index 4e0840d5a68..762afc9b551 100644 --- a/core/src/main/java/org/fao/geonet/kernel/XmlSerializer.java +++ b/core/src/main/java/org/fao/geonet/kernel/XmlSerializer.java @@ -36,6 +36,7 @@ import org.fao.geonet.kernel.datamanager.IMetadataManager; import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.kernel.schema.MetadataSchema; +import org.fao.geonet.kernel.schema.MetadataSchemaOperationFilter; import org.fao.geonet.kernel.setting.SettingManager; import org.fao.geonet.kernel.setting.Settings; import org.fao.geonet.utils.Log; @@ -70,13 +71,13 @@ public static void clearThreadLocal() { } public static void removeFilteredElement(Element metadata, - final Pair xPathAndMarkedElement, + final MetadataSchemaOperationFilter filter, List namespaces) throws JDOMException { // xPathAndMarkedElement seem can be null in some schemas like dublin core - if (xPathAndMarkedElement == null) return; + if (filter == null) return; - String xpath = xPathAndMarkedElement.one(); - Element mark = xPathAndMarkedElement.two(); + String xpath = filter.getXpath(); + Element mark = filter.getMarkedElement(); List nodes = Xml.selectNodes(metadata, xpath, @@ -89,13 +90,13 @@ public static void removeFilteredElement(Element metadata, // Remove attributes @SuppressWarnings("unchecked") - List atts = new ArrayList(element.getAttributes()); + List atts = new ArrayList<>(element.getAttributes()); for (Attribute attribute : atts) { attribute.detach(); } // Insert attributes or children element of the mark - List markAtts = new ArrayList(mark.getAttributes()); + List markAtts = new ArrayList<>(mark.getAttributes()); for (Attribute attribute : markAtts) { element.setAttribute((Attribute) attribute.clone()); } @@ -182,33 +183,42 @@ public Element removeHiddenElements(boolean isIndexingTask, AbstractMetadata met // Check if a filter is defined for this schema // for the editing operation ie. user who can not edit // will not see those elements. - Pair editXpathFilter = mds.getOperationFilter(ReservedOperation.editing); - boolean filterEditOperationElements = editXpathFilter != null; + MetadataSchemaOperationFilter editFilter = mds.getOperationFilter(ReservedOperation.editing); + boolean filterEditOperationElements = editFilter != null; List namespaces = mds.getNamespaces(); if (context != null) { - if (editXpathFilter != null) { + if (editFilter != null) { boolean canEdit = accessManager.canEdit(context, id); if (canEdit) { filterEditOperationElements = false; } } - Pair downloadXpathFilter = mds.getOperationFilter(ReservedOperation.download); - if (downloadXpathFilter != null) { + + MetadataSchemaOperationFilter authenticatedFilter = mds.getOperationFilter("authenticated"); + if (authenticatedFilter != null) { + boolean isAuthenticated = context.getUserSession().isAuthenticated(); + if (!isAuthenticated) { + removeFilteredElement(metadataXml, authenticatedFilter, namespaces); + } + } + + MetadataSchemaOperationFilter downloadFilter = mds.getOperationFilter(ReservedOperation.download); + if (downloadFilter != null) { boolean canDownload = accessManager.canDownload(context, id); if (!canDownload) { - removeFilteredElement(metadataXml, downloadXpathFilter, namespaces); + removeFilteredElement(metadataXml, downloadFilter, namespaces); } } - Pair dynamicXpathFilter = mds.getOperationFilter(ReservedOperation.dynamic); - if (dynamicXpathFilter != null) { + MetadataSchemaOperationFilter dynamicFilter = mds.getOperationFilter(ReservedOperation.dynamic); + if (dynamicFilter != null) { boolean canDynamic = accessManager.canDynamic(context, id); if (!canDynamic) { - removeFilteredElement(metadataXml, dynamicXpathFilter, namespaces); + removeFilteredElement(metadataXml, dynamicFilter, namespaces); } } } if (filterEditOperationElements || (getThreadLocal(false) != null && getThreadLocal(false).forceFilterEditOperation)) { - removeFilteredElement(metadataXml, editXpathFilter, namespaces); + removeFilteredElement(metadataXml, editFilter, namespaces); } } return metadataXml; diff --git a/core/src/main/java/org/fao/geonet/kernel/mef/ExportFormat.java b/core/src/main/java/org/fao/geonet/kernel/mef/ExportFormat.java index 5fd16fdb577..02fe7637483 100644 --- a/core/src/main/java/org/fao/geonet/kernel/mef/ExportFormat.java +++ b/core/src/main/java/org/fao/geonet/kernel/mef/ExportFormat.java @@ -31,6 +31,7 @@ import java.util.Set; import org.fao.geonet.GeonetContext; +import org.fao.geonet.constants.Edit; import org.fao.geonet.constants.Geonet; import org.fao.geonet.domain.AbstractMetadata; import org.fao.geonet.domain.Metadata; @@ -72,7 +73,7 @@ public static Iterable> getFormats(ServiceContext context, String outputFileName = entry.getValue(); Path path = metadataSchema.getSchemaDir().resolve(xslFileName); if (Files.isRegularFile(path)) { - String outputData = formatData(metadata, true, path); + String outputData = formatData(context, metadata, true, path); allExports.add(Pair.read(outputFileName, outputData)); } else { // A conversion that does not exist @@ -96,10 +97,9 @@ public static Iterable> getFormats(ServiceContext context, * * @return ByteArrayInputStream */ - public static String formatData(AbstractMetadata metadata, boolean transform, Path stylePath) throws Exception { - String xmlData = metadata.getData(); - - Element md = Xml.loadString(xmlData, false); + public static String formatData(ServiceContext context, AbstractMetadata metadata, boolean transform, Path stylePath) throws Exception { + Element md = context.getBean(DataManager.class).getMetadata(context, metadata.getId() + "", false, false, true); + md.removeChild("info", Edit.NAMESPACE); // Apply a stylesheet transformation when schema is ISO profil if (transform) { diff --git a/core/src/main/java/org/fao/geonet/kernel/schema/MetadataSchema.java b/core/src/main/java/org/fao/geonet/kernel/schema/MetadataSchema.java index d07dccc595c..3f8e038a88d 100644 --- a/core/src/main/java/org/fao/geonet/kernel/schema/MetadataSchema.java +++ b/core/src/main/java/org/fao/geonet/kernel/schema/MetadataSchema.java @@ -1,29 +1,25 @@ -//============================================================================== -//=== -//=== MetadataSchema -//=== -//============================================================================== -//=== Copyright (C) 2001-2007 Food and Agriculture Organization of the -//=== United Nations (FAO-UN), United Nations World Food Programme (WFP) -//=== and United Nations Environment Programme (UNEP) -//=== -//=== This program is free software; you can redistribute it and/or modify -//=== it under the terms of the GNU General Public License as published by -//=== the Free Software Foundation; either version 2 of the License, or (at -//=== your option) any later version. -//=== -//=== This program is distributed in the hope that it will be useful, but -//=== WITHOUT ANY WARRANTY; without even the implied warranty of -//=== MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -//=== General Public License for more details. -//=== -//=== You should have received a copy of the GNU General Public License -//=== along with this program; if not, write to the Free Software -//=== Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA -//=== -//=== Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, -//=== Rome - Italy. email: geonetwork@osgeo.org -//============================================================================== +/* + * Copyright (C) 2001-2023 Food and Agriculture Organization of the + * United Nations (FAO-UN), United Nations World Food Programme (WFP) + * and United Nations Environment Programme (UNEP) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, + * Rome - Italy. email: geonetwork@osgeo.org + */ package org.fao.geonet.kernel.schema; @@ -34,9 +30,9 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import org.apache.commons.lang.StringUtils; import org.fao.geonet.api.exception.ResourceNotFoundException; import org.fao.geonet.constants.Geonet; -import org.fao.geonet.domain.Pair; import org.fao.geonet.domain.ReservedOperation; import org.fao.geonet.domain.Schematron; import org.fao.geonet.domain.SchematronCriteria; @@ -82,15 +78,15 @@ public class MetadataSchema { private static final String XSL_FILE_EXTENSION = ".xsl"; private static final String SCH_FILE_EXTENSION = ".sch"; private static final String SCHEMATRON_RULE_FILE_PREFIX = "schematron-rules"; - private Map> hmElements = new HashMap>(); - private Map>> hmRestric = new HashMap>>(); - private Map hmTypes = new HashMap(); - private Map> hmSubs = new HashMap>(); - private Map hmSubsLink = new HashMap(); - private Map hmNameSpaces = new HashMap(); - private Map hmPrefixes = new HashMap(); - private Map> hmOperationFilters = - new HashMap>(); + private Map> hmElements = new HashMap<>(); + private Map>> hmRestric = new HashMap<>(); + private Map hmTypes = new HashMap<>(); + private Map> hmSubs = new HashMap<>(); + private Map hmSubsLink = new HashMap<>(); + private Map hmNameSpaces = new HashMap<>(); + private Map hmPrefixes = new HashMap<>(); + private Map hmOperationFilters = + new HashMap<>(); private String schemaName; private Path schemaDir; private String standardUrl; @@ -294,8 +290,8 @@ void addElement(String name, String type, List alValues, List al // first just add the subs - because these are for global elements we // never have a clash because global elements are all in the same scope // and are thus unique - if (alSubs != null && alSubs.size() > 0) hmSubs.put(name, alSubs); - if (subLink != null && subLink.length() > 0) hmSubsLink.put(name, subLink); + if (alSubs != null && !alSubs.isEmpty()) hmSubs.put(name, alSubs); + if (subLink != null && StringUtils.isNotBlank(subLink)) hmSubsLink.put(name, subLink); List exType = hmElements.get(name); @@ -309,7 +305,8 @@ void addElement(String name, String type, List alValues, List al // it's not there so add a new list } else { - hmElements.put(name, exType = new ArrayList()); + exType = new ArrayList<>(); + hmElements.put(name, exType); } exType.add(type); @@ -323,7 +320,8 @@ void addElement(String name, String type, List alValues, List al // it's not there so add a new list of lists } else { - hmRestric.put(restricName, exValues = new ArrayList>()); + exValues = new ArrayList<>(); + hmRestric.put(restricName, exValues); } exValues.add(alValues); } @@ -361,7 +359,7 @@ public String getNS(String targetNSPrefix) { */ @JsonIgnore public List getNamespaces() { - List list = new ArrayList(hmNameSpaces.size()); + List list = new ArrayList<>(hmNameSpaces.size()); for (Namespace ns : hmNameSpaces.values()) { list.add(ns); } @@ -382,12 +380,12 @@ public String getPrefix(String theNSUri) { //--------------------------------------------------------------------------- @JsonIgnore public List getSchemaNS() { - return new ArrayList(hmPrefixes.values()); + return new ArrayList<>(hmPrefixes.values()); } @JsonProperty(value = "namespaces") public Map getSchemaNSWithPrefix() { - Map mapNs = new HashMap(); + Map mapNs = new HashMap<>(); List schemaNsList = getSchemaNS(); for (Namespace ns : schemaNsList) { @@ -518,7 +516,7 @@ private void setSchematronPriorities() { this.schemaRepo.saveAll(updated); } - public void setOperationFilters(Map> operationFilters) { + public void setOperationFilters(Map operationFilters) { this.hmOperationFilters = operationFilters; } @@ -527,10 +525,14 @@ public void setOperationFilters(Map> operationFilt * * @return The XPath to select element to filter or null */ - public Pair getOperationFilter(ReservedOperation operation) { + public MetadataSchemaOperationFilter getOperationFilter(ReservedOperation operation) { return hmOperationFilters.get(operation.name()); } + public MetadataSchemaOperationFilter getOperationFilter(String operation) { + return hmOperationFilters.get(operation); + } + @JsonIgnore public SchemaPlugin getSchemaPlugin() { return schemaPlugin; diff --git a/core/src/main/java/org/fao/geonet/kernel/schema/MetadataSchemaOperationFilter.java b/core/src/main/java/org/fao/geonet/kernel/schema/MetadataSchemaOperationFilter.java new file mode 100644 index 00000000000..d296b5fa9ee --- /dev/null +++ b/core/src/main/java/org/fao/geonet/kernel/schema/MetadataSchemaOperationFilter.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2001-2023 Food and Agriculture Organization of the + * United Nations (FAO-UN), United Nations World Food Programme (WFP) + * and United Nations Environment Programme (UNEP) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, + * Rome - Italy. email: geonetwork@osgeo.org + */ +package org.fao.geonet.kernel.schema; + +import org.jdom.Element; + +public class MetadataSchemaOperationFilter { + private String xpath; + private String jsonpath; + private String ifNotOperation; + private Element markedElement; + + + public MetadataSchemaOperationFilter(String xpath, String jsonpath, String ifNotOperation) { + this(xpath, jsonpath, ifNotOperation, null); + } + + public MetadataSchemaOperationFilter(String xpath, String jsonpath, String ifNotOperation, Element markedElement) { + this.xpath = xpath; + this.jsonpath = jsonpath; + this.ifNotOperation = ifNotOperation; + this.markedElement = markedElement; + + } + + public String getXpath() { + return xpath; + } + + public String getJsonpath() { + return jsonpath; + } + + public String getIfNotOperation() { + return ifNotOperation; + } + + public Element getMarkedElement() { + return markedElement; + } +} diff --git a/core/src/test/java/org/fao/geonet/kernel/XmlSerializerIntegrationTest.java b/core/src/test/java/org/fao/geonet/kernel/XmlSerializerIntegrationTest.java index 453098e09a7..b16e898db5a 100644 --- a/core/src/test/java/org/fao/geonet/kernel/XmlSerializerIntegrationTest.java +++ b/core/src/test/java/org/fao/geonet/kernel/XmlSerializerIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001-2016 Food and Agriculture Organization of the + * Copyright (C) 2001-2023 Food and Agriculture Organization of the * United Nations (FAO-UN), United Nations World Food Programme (WFP) * and United Nations Environment Programme (UNEP) * @@ -45,6 +45,7 @@ import org.fao.geonet.domain.Pair; import org.fao.geonet.kernel.datamanager.IMetadataManager; import org.fao.geonet.kernel.schema.MetadataSchema; +import org.fao.geonet.kernel.schema.MetadataSchemaOperationFilter; import org.fao.geonet.utils.Xml; import org.jdom.Element; import org.jdom.Namespace; @@ -101,23 +102,31 @@ public class XmlSerializerIntegrationTest extends AbstractCoreIntegrationTest { public void setSchemaFilters(boolean withHeld, boolean keepMarkedElement) { MetadataSchema mds = _dataManager.getSchema(metadata.getDataInfo().getSchemaId()); - Map> filters = new HashMap>(); + Map filters = new HashMap<>(); if (withHeld) { if (keepMarkedElement) { Element mark = new Element("keepMarkedElement"); mark.setAttribute("nilReason", "withheld", Geonet.Namespaces.GCO); - filters.put("editing", - Pair.read(XPATH_WITHHELD, mark)); + + MetadataSchemaOperationFilter editFilter = new MetadataSchemaOperationFilter(XPATH_WITHHELD, "", "editing", mark); + + filters.put(editFilter.getIfNotOperation(), + editFilter); } else { - filters.put("editing", - Pair.read(XPATH_WITHHELD, null)); + MetadataSchemaOperationFilter editFilter = new MetadataSchemaOperationFilter(XPATH_WITHHELD, "", "editing", null); + + filters.put(editFilter.getIfNotOperation(), + editFilter); } } - filters.put("download", - Pair.read(XPATH_DOWNLOAD, null)); - filters.put("dynamic", - Pair.read(XPATH_DYNAMIC, null)); + MetadataSchemaOperationFilter downloadFilter = new MetadataSchemaOperationFilter(XPATH_DOWNLOAD, "", "download", null); + filters.put(downloadFilter.getIfNotOperation(), + downloadFilter); + + MetadataSchemaOperationFilter dynamicFilter = new MetadataSchemaOperationFilter(XPATH_DYNAMIC, "", "dynamic", null); + filters.put(dynamicFilter.getIfNotOperation(), + dynamicFilter); mds.setOperationFilters(filters); } diff --git a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/index-fields/index.xsl b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/index-fields/index.xsl index 7d1ace956e9..e39d3e65831 100644 --- a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/index-fields/index.xsl +++ b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/index-fields/index.xsl @@ -1067,6 +1067,9 @@ "descriptionObject": , + + "nilReason": "", + "applicationProfile": "" } @@ -1179,6 +1182,9 @@ "descriptionObject": , + + "nilReason": "", + "function":"", "applicationProfile":"", "group": @@ -1330,6 +1336,7 @@ + { @@ -1345,6 +1352,9 @@ "position":"", "phone":"", "address":"" + + ,"nilReason": "" + ,"identifiers":[ diff --git a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/index-fields/link-utility.xsl b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/index-fields/link-utility.xsl index c6695bce12f..a2674cc7121 100644 --- a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/index-fields/link-utility.xsl +++ b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/index-fields/link-utility.xsl @@ -151,6 +151,9 @@ + + + diff --git a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/schema-ident.xml b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/schema-ident.xml index ec945b04f60..d59eee679a7 100644 --- a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/schema-ident.xml +++ b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/schema-ident.xml @@ -128,13 +128,14 @@ Ce schéma est également composé des normes : --> - diff --git a/schemas/iso19139/src/main/plugin/iso19139/index-fields/index.xsl b/schemas/iso19139/src/main/plugin/iso19139/index-fields/index.xsl index 915d80b9265..b50c4e92af0 100644 --- a/schemas/iso19139/src/main/plugin/iso19139/index-fields/index.xsl +++ b/schemas/iso19139/src/main/plugin/iso19139/index-fields/index.xsl @@ -1006,7 +1006,7 @@ /gmd:distributionOrderProcess/*/gmd:orderingInstructions"> - + @@ -1045,6 +1045,9 @@ "descriptionObject": , + + "nilReason": "", + "function":"", "applicationProfile":"", "group": @@ -1192,6 +1195,7 @@ + { @@ -1206,6 +1210,9 @@ "position":"", "phone":"", "address":"" + + ,"nilReason": "" + } diff --git a/schemas/iso19139/src/main/plugin/iso19139/schema-ident.xml b/schemas/iso19139/src/main/plugin/iso19139/schema-ident.xml index 8312ac09f88..482eaf6f730 100644 --- a/schemas/iso19139/src/main/plugin/iso19139/schema-ident.xml +++ b/schemas/iso19139/src/main/plugin/iso19139/schema-ident.xml @@ -85,6 +85,7 @@ diff --git a/services/pom.xml b/services/pom.xml index 6bc2231b1b2..f846fb44761 100644 --- a/services/pom.xml +++ b/services/pom.xml @@ -136,7 +136,6 @@ com.jayway.jsonpath json-path 2.4.0 - test com.jayway.jsonpath diff --git a/services/src/main/java/org/fao/geonet/api/es/EsHTTPProxy.java b/services/src/main/java/org/fao/geonet/api/es/EsHTTPProxy.java index c93077de558..5d34c51b2e5 100644 --- a/services/src/main/java/org/fao/geonet/api/es/EsHTTPProxy.java +++ b/services/src/main/java/org/fao/geonet/api/es/EsHTTPProxy.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001-2016 Food and Agriculture Organization of the + * Copyright (C) 2001-2023 Food and Agriculture Organization of the * United Nations (FAO-UN), United Nations World Food Programme (WFP) * and United Nations Environment Programme (UNEP) * @@ -26,12 +26,15 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.MappingIterator; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.collect.Sets; +import com.jayway.jsonpath.DocumentContext; +import com.jayway.jsonpath.JsonPath; import io.swagger.v3.oas.annotations.Hidden; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.parameters.RequestBody; @@ -43,18 +46,19 @@ import org.fao.geonet.Constants; import org.fao.geonet.NodeInfo; import org.fao.geonet.api.ApiUtils; -import org.fao.geonet.api.records.MetadataApi; import org.fao.geonet.api.records.MetadataUtils; import org.fao.geonet.api.records.model.related.AssociatedRecord; import org.fao.geonet.api.records.model.related.RelatedItemType; -import org.fao.geonet.api.records.model.related.RelatedResponse; import org.fao.geonet.constants.Edit; import org.fao.geonet.constants.Geonet; import org.fao.geonet.domain.*; import org.fao.geonet.index.es.EsRestClient; import org.fao.geonet.kernel.AccessManager; +import org.fao.geonet.kernel.SchemaManager; import org.fao.geonet.kernel.SelectionManager; import org.fao.geonet.kernel.datamanager.IMetadataUtils; +import org.fao.geonet.kernel.schema.MetadataSchema; +import org.fao.geonet.kernel.schema.MetadataSchemaOperationFilter; import org.fao.geonet.kernel.search.EsFilterBuilder; import org.fao.geonet.repository.SourceRepository; import org.slf4j.Logger; @@ -77,7 +81,6 @@ import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.*; -import java.util.stream.Collectors; import java.util.zip.DeflaterInputStream; import java.util.zip.DeflaterOutputStream; import java.util.zip.GZIPInputStream; @@ -129,6 +132,9 @@ public class EsHTTPProxy { @Autowired private EsRestClient client; + @Autowired + private SchemaManager schemaManager; + public EsHTTPProxy() { } @@ -171,7 +177,7 @@ private static void addRelatedTypes(ObjectNode doc, LOGGER.warn("Failed to load related types for {}. Error is: {}", getSourceString(doc, Geonet.IndexFieldNames.UUID), e.getMessage() - ); + ); } doc.putPOJO("related", related); } @@ -274,20 +280,20 @@ private static boolean hasOperation(ObjectNode doc, ReservedGroup group, Reserve @ResponseBody public void search( @RequestParam(defaultValue = SelectionManager.SELECTION_METADATA) - String bucket, + String bucket, @Parameter(description = "Type of related resource. If none, no associated resource returned.", required = false ) @RequestParam(name = "relatedType", defaultValue = "") - RelatedItemType[] relatedTypes, + RelatedItemType[] relatedTypes, @Parameter(hidden = true) - HttpSession httpSession, + HttpSession httpSession, @Parameter(hidden = true) - HttpServletRequest request, + HttpServletRequest request, @Parameter(hidden = true) - HttpServletResponse response, + HttpServletResponse response, @RequestBody(description = "JSON request based on Elasticsearch API.") - String body, + String body, @Parameter(hidden = true) HttpEntity httpEntity) throws Exception { ServiceContext context = ApiUtils.createServiceContext(request); @@ -306,20 +312,20 @@ public void search( @ResponseBody public void msearch( @RequestParam(defaultValue = SelectionManager.SELECTION_METADATA) - String bucket, + String bucket, @Parameter(description = "Type of related resource. If none, no associated resource returned.", required = false ) @RequestParam(name = "relatedType", defaultValue = "") - RelatedItemType[] relatedTypes, + RelatedItemType[] relatedTypes, @Parameter(hidden = true) - HttpSession httpSession, + HttpSession httpSession, @Parameter(hidden = true) - HttpServletRequest request, + HttpServletRequest request, @Parameter(hidden = true) - HttpServletResponse response, + HttpServletResponse response, @RequestBody(description = "JSON request based on Elasticsearch API.") - String body, + String body, @Parameter(hidden = true) HttpEntity httpEntity) throws Exception { ServiceContext context = ApiUtils.createServiceContext(request); @@ -336,25 +342,25 @@ public void msearch( @RequestMapping(value = "/search/records/{endPoint}", method = { RequestMethod.POST, RequestMethod.GET - }) + }) @ResponseStatus(value = HttpStatus.OK) @PreAuthorize("hasAuthority('Administrator')") @ResponseBody public void call( @RequestParam(defaultValue = SelectionManager.SELECTION_METADATA) - String bucket, + String bucket, @Parameter(description = "'_search' for search service.") @PathVariable String endPoint, @Parameter(hidden = true) - HttpSession httpSession, + HttpSession httpSession, @Parameter(hidden = true) - HttpServletRequest request, + HttpServletRequest request, @Parameter(hidden = true) - HttpServletResponse response, + HttpServletResponse response, @RequestBody(description = "JSON request based on Elasticsearch API.") - String body, + String body, @Parameter(hidden = true) - HttpEntity httpEntity) throws Exception { + HttpEntity httpEntity) throws Exception { ServiceContext context = ApiUtils.createServiceContext(request); call(context, httpSession, request, response, endPoint, httpEntity.getBody(), bucket, null); @@ -419,6 +425,7 @@ public void call(ServiceContext context, HttpSession httpSession, HttpServletReq */ private void addRequiredField(ArrayNode source) { source.add("op*"); + source.add(Geonet.IndexFieldNames.SCHEMA); source.add(Geonet.IndexFieldNames.GROUP_OWNER); source.add(Geonet.IndexFieldNames.OWNER); source.add(Geonet.IndexFieldNames.ID); @@ -430,8 +437,8 @@ private void addFilterToQuery(ServiceContext context, // Build filter node String esFilter = buildQueryFilter(context, - "", - esQuery.toString().contains("\"draft\":")); + "", + esQuery.toString().contains("\"draft\":")); JsonNode nodeFilter = objectMapper.readTree(esFilter); JsonNode queryNode = esQuery.get("query"); @@ -639,13 +646,20 @@ private void processResponse(ServiceContext context, HttpSession httpSession, addRelatedTypes(doc, relatedTypes, context); } - // Remove fields with privileges info if (doc.has("_source")) { ObjectNode sourceNode = (ObjectNode) doc.get("_source"); + String metadataSchema = doc.get("_source").get("documentStandard").asText(); + MetadataSchema mds = schemaManager.getSchema(metadataSchema); + + // Apply metadata schema filters to remove non-allowed fields + processMetadataSchemaFilters(context, mds, doc); + + // Remove fields with privileges info for (ReservedOperation o : ReservedOperation.values()) { sourceNode.remove("op" + o.getId()); } + } }); } else { @@ -778,4 +792,96 @@ protected boolean isContentTypeValid(final String contentType) { return false; } + + /** + * Process the metadata schema filters to filter out from the ElasticSearch response + * the elements defined in the metadata schema filters. + * + * It uses a jsonpath to filter the elements, typically is configured with the following jsonpath, to + * filter the ES object elements with an attribute nilReason = 'withheld'. + * + * $.*[?(@.nilReason == 'withheld')] + * + * The metadata index process, has to define this attribute. Any element that requires to be filtered, should be + * defined as an object in ElasticSearch. + * + * Example for contacts: + * + * + * ... + * + * + * + * + * { + * ... + * "address":"" + * + * ,"nilReason": "withheld" + * + * + * @param mds + * @param doc + * @throws JsonProcessingException + */ + private void processMetadataSchemaFilters(ServiceContext context, MetadataSchema mds, ObjectNode doc) throws JsonProcessingException { + if (!doc.has("_source")) { + return; + } + + ObjectMapper mapper = new ObjectMapper(); + ObjectNode sourceNode = (ObjectNode) doc.get("_source"); + + MetadataSchemaOperationFilter authenticatedFilter = mds.getOperationFilter("authenticated"); + + List jsonpathFilters = new ArrayList<>(); + + if (authenticatedFilter != null && !context.getUserSession().isAuthenticated()) { + jsonpathFilters.add(authenticatedFilter.getJsonpath()); + } + + MetadataSchemaOperationFilter editFilter = mds.getOperationFilter(ReservedOperation.editing); + + if (editFilter != null) { + boolean canEdit = doc.get("edit").asBoolean(); + + if (!canEdit) { + jsonpathFilters.add(editFilter.getJsonpath()); + } + } + + MetadataSchemaOperationFilter downloadFilter = mds.getOperationFilter(ReservedOperation.download); + if (downloadFilter != null) { + boolean canDownload = doc.get("download").asBoolean(); + + if (!canDownload) { + jsonpathFilters.add(downloadFilter.getJsonpath()); + } + } + + MetadataSchemaOperationFilter dynamicFilter = mds.getOperationFilter(ReservedOperation.dynamic); + if (dynamicFilter != null) { + boolean canDynamic = doc.get("dynamic").asBoolean(); + + if (!canDynamic) { + jsonpathFilters.add(dynamicFilter.getJsonpath()); + } + } + + JsonNode actualObj = filterResponseElements(mapper, sourceNode, jsonpathFilters); + if (actualObj != null) { + doc.set("_source", actualObj); + } + } + private JsonNode filterResponseElements(ObjectMapper mapper, ObjectNode sourceNode, List jsonPathFilters) throws JsonProcessingException { + DocumentContext jsonContext = JsonPath.parse(sourceNode.toPrettyString()); + + for(String jsonPath : jsonPathFilters) { + if (StringUtils.isNotBlank(jsonPath)) { + jsonContext = jsonContext.delete(jsonPath); + } + } + + return mapper.readTree(jsonContext.jsonString()); + } } diff --git a/web/src/main/webapp/xml/validation/schemaPlugins/schema-ident.xsd b/web/src/main/webapp/xml/validation/schemaPlugins/schema-ident.xsd index 242e2bdafbd..4748b73e25e 100644 --- a/web/src/main/webapp/xml/validation/schemaPlugins/schema-ident.xsd +++ b/web/src/main/webapp/xml/validation/schemaPlugins/schema-ident.xsd @@ -22,8 +22,9 @@ ~ Rome - Italy. email: geonetwork@osgeo.org --> - + + + + An JSON path to select elements to filter in the ElasticSearch index. + + + @@ -258,6 +266,7 @@ +