From dcc651945dd9c4567a0a25028f6f36b39b2d8745 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 27 Jul 2020 11:09:26 -0500 Subject: [PATCH 01/10] Throw errors when possible "zip slip" detected. --- .../app/itemimport/ItemImportServiceImpl.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java index 12fcd84d04..13aa236f54 100644 --- a/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/app/itemimport/ItemImportServiceImpl.java @@ -1519,6 +1519,12 @@ public class ItemImportServiceImpl implements ItemImportService, InitializingBea if (!dir.exists() && !dir.mkdirs()) { log.error("Unable to create directory: " + dir.getAbsolutePath()); } + // Verify that the directory the entry is using is a subpath of zipDir (and not somewhere else!) + if (!dir.toPath().normalize().startsWith(zipDir)) { + throw new IOException("Bad zip entry: '" + entry.getName() + + "' in file '" + zipfile.getAbsolutePath() + "'!" + + " Cannot process this file."); + } //Entries could have too many directories, and we need to adjust the sourcedir // file1.zip (SimpleArchiveFormat / item1 / contents|dublin_core|... @@ -1539,9 +1545,16 @@ public class ItemImportServiceImpl implements ItemImportService, InitializingBea } byte[] buffer = new byte[1024]; int len; + File outFile = new File(zipDir + entry.getName()); + // Verify that this file will be created in our zipDir (and not somewhere else!) + if (!outFile.toPath().normalize().startsWith(zipDir)) { + throw new IOException("Bad zip entry: '" + entry.getName() + + "' in file '" + zipfile.getAbsolutePath() + "'!" + + " Cannot process this file."); + } InputStream in = zf.getInputStream(entry); BufferedOutputStream out = new BufferedOutputStream( - new FileOutputStream(zipDir + entry.getName())); + new FileOutputStream(outFile)); while ((len = in.read(buffer)) >= 0) { out.write(buffer, 0, len); } From cf7daef43125e902ba6eb47506d4a8b5868b41fb Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 27 Jul 2020 11:12:18 -0500 Subject: [PATCH 02/10] Set invalidation cookie to "secure" just to avoid LGTM warning --- .../rest/security/jwt/JWTTokenRestAuthenticationServiceImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/jwt/JWTTokenRestAuthenticationServiceImpl.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/jwt/JWTTokenRestAuthenticationServiceImpl.java index 1b5ee6a0c5..8aff9cc884 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/jwt/JWTTokenRestAuthenticationServiceImpl.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/security/jwt/JWTTokenRestAuthenticationServiceImpl.java @@ -154,6 +154,7 @@ public class JWTTokenRestAuthenticationServiceImpl implements RestAuthentication Cookie cookie = new Cookie(AUTHORIZATION_COOKIE, ""); cookie.setHttpOnly(true); cookie.setMaxAge(0); + cookie.setSecure(true); response.addCookie(cookie); } From 294bb02193b74b15f9193e6ef6af3958ca406f31 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 27 Jul 2020 12:00:06 -0500 Subject: [PATCH 03/10] Fix HTTP Response splitting by validating URL --- .../dspace/rdf/negotiation/Negotiator.java | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/rdf/negotiation/Negotiator.java b/dspace-api/src/main/java/org/dspace/rdf/negotiation/Negotiator.java index c28b9ec1e6..d011d305b1 100644 --- a/dspace-api/src/main/java/org/dspace/rdf/negotiation/Negotiator.java +++ b/dspace-api/src/main/java/org/dspace/rdf/negotiation/Negotiator.java @@ -15,6 +15,7 @@ import java.util.Iterator; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.validator.routines.UrlValidator; import org.apache.logging.log4j.Logger; import org.dspace.rdf.RDFUtil; import org.dspace.services.factory.DSpaceServicesFactory; @@ -197,6 +198,7 @@ public class Negotiator { if (extraPathInfo == null) { extraPathInfo = ""; } + UrlValidator urlValidator = new UrlValidator(UrlValidator.ALLOW_LOCAL_URLS); StringBuilder urlBuilder = new StringBuilder(); String lang = null; @@ -256,12 +258,15 @@ public class Negotiator { urlBuilder.append(handle).append("/").append(extraPathInfo); } String url = urlBuilder.toString(); - - log.debug("Will forward to '" + url + "'."); - response.setStatus(HttpServletResponse.SC_SEE_OTHER); - response.setHeader("Location", url); - response.flushBuffer(); - return true; + if (urlValidator.isValid(url)) { + log.debug("Will forward to '" + url + "'."); + response.setStatus(HttpServletResponse.SC_SEE_OTHER); + response.setHeader("Location", url); + response.flushBuffer(); + return true; + } else { + throw new IOException("Invalid URL '" + url + "', cannot redirect."); + } } // currently we cannot serve statistics as rdf @@ -287,10 +292,14 @@ public class Negotiator { urlBuilder.append("/handle/").append(handle); urlBuilder.append("/").append(lang); String url = urlBuilder.toString(); - log.debug("Will forward to '" + url + "'."); - response.setStatus(HttpServletResponse.SC_SEE_OTHER); - response.setHeader("Location", url); - response.flushBuffer(); - return true; + if (urlValidator.isValid(url)) { + log.debug("Will forward to '" + url + "'."); + response.setStatus(HttpServletResponse.SC_SEE_OTHER); + response.setHeader("Location", url); + response.flushBuffer(); + return true; + } else { + throw new IOException("Invalid URL '" + url + "', cannot redirect."); + } } } From b006b84039850be461611687c75e7e25267d5236 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 27 Jul 2020 12:08:47 -0500 Subject: [PATCH 04/10] Use object's reported handle instead of user provided handle --- .../org/dspace/rdf/providing/LocalURIRedirectionServlet.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dspace-rdf/src/main/java/org/dspace/rdf/providing/LocalURIRedirectionServlet.java b/dspace-rdf/src/main/java/org/dspace/rdf/providing/LocalURIRedirectionServlet.java index b6a6854938..7224bb9bfb 100644 --- a/dspace-rdf/src/main/java/org/dspace/rdf/providing/LocalURIRedirectionServlet.java +++ b/dspace-rdf/src/main/java/org/dspace/rdf/providing/LocalURIRedirectionServlet.java @@ -86,7 +86,8 @@ public class LocalURIRedirectionServlet extends HttpServlet { response.sendError(HttpServletResponse.SC_NOT_FOUND); return; } - + // use object's reported handle for redirect (just in case user provided handle had odd characters) + handle = dso.getHandle(); // close the context and send forward. context.abort(); Negotiator.sendRedirect(response, handle, "", requestedMimeType, true); From 53df0814221a7bc36ad91299c48db65dce1621e0 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 27 Jul 2020 13:13:25 -0500 Subject: [PATCH 05/10] Fix possible XXE attacks by disabling DTD parsing for external service requests. --- .../java/org/dspace/app/sherpa/SHERPAResponse.java | 3 +++ .../org/dspace/content/packager/METSManifest.java | 14 +++++++++----- .../dspace/ctask/general/MetadataWebService.java | 3 +++ .../license/CCLicenseConnectorServiceImpl.java | 4 ++++ .../org/dspace/submit/lookup/ArXivService.java | 3 +++ .../org/dspace/submit/lookup/CiNiiService.java | 6 ++++++ .../org/dspace/submit/lookup/CrossRefService.java | 3 +++ .../org/dspace/submit/lookup/PubmedService.java | 9 +++++++++ 8 files changed, 40 insertions(+), 5 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/sherpa/SHERPAResponse.java b/dspace-api/src/main/java/org/dspace/app/sherpa/SHERPAResponse.java index c5b8bbebf3..bd2909c0c1 100644 --- a/dspace-api/src/main/java/org/dspace/app/sherpa/SHERPAResponse.java +++ b/dspace-api/src/main/java/org/dspace/app/sherpa/SHERPAResponse.java @@ -48,6 +48,9 @@ public class SHERPAResponse { factory.setValidating(false); factory.setIgnoringComments(true); factory.setIgnoringElementContentWhitespace(true); + // disallow DTD parsing to ensure no XXE attacks can occur. + // See https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); DocumentBuilder db = factory.newDocumentBuilder(); Document inDoc = db.parse(xmlData); diff --git a/dspace-api/src/main/java/org/dspace/content/packager/METSManifest.java b/dspace-api/src/main/java/org/dspace/content/packager/METSManifest.java index 53a8678df2..ed15037c11 100644 --- a/dspace-api/src/main/java/org/dspace/content/packager/METSManifest.java +++ b/dspace-api/src/main/java/org/dspace/content/packager/METSManifest.java @@ -272,12 +272,16 @@ public class METSManifest { // Set validation feature if (validate) { builder.setFeature("http://apache.org/xml/features/validation/schema", true); - } - // Tell the parser where local copies of schemas are, to speed up - // validation. Local XSDs are identified in the configuration file. - if (localSchemas.length() > 0) { - builder.setProperty("http://apache.org/xml/properties/schema/external-schemaLocation", localSchemas); + // Tell the parser where local copies of schemas are, to speed up + // validation & avoid XXE attacks from remote schemas. Local XSDs are identified in the configuration file. + if (localSchemas.length() > 0) { + builder.setProperty("http://apache.org/xml/properties/schema/external-schemaLocation", localSchemas); + } + } else { + // disallow DTD parsing to ensure no XXE attacks can occur. + // See https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html + builder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); } // Parse the METS file diff --git a/dspace-api/src/main/java/org/dspace/ctask/general/MetadataWebService.java b/dspace-api/src/main/java/org/dspace/ctask/general/MetadataWebService.java index 2b6c52d0d6..754f3b4ab3 100644 --- a/dspace-api/src/main/java/org/dspace/ctask/general/MetadataWebService.java +++ b/dspace-api/src/main/java/org/dspace/ctask/general/MetadataWebService.java @@ -199,6 +199,9 @@ public class MetadataWebService extends AbstractCurationTask implements Namespac DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); try { + // disallow DTD parsing to ensure no XXE attacks can occur. + // See https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); docBuilder = factory.newDocumentBuilder(); } catch (ParserConfigurationException pcE) { log.error("caught exception: " + pcE); diff --git a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java index a237a91984..792c25d629 100644 --- a/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/license/CCLicenseConnectorServiceImpl.java @@ -75,6 +75,10 @@ public class CCLicenseConnectorServiceImpl implements CCLicenseConnectorService, .disableAutomaticRetries() .setMaxConnTotal(5) .build(); + + // disallow DTD parsing to ensure no XXE attacks can occur. + // See https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html + parser.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); } /** diff --git a/dspace-api/src/main/java/org/dspace/submit/lookup/ArXivService.java b/dspace-api/src/main/java/org/dspace/submit/lookup/ArXivService.java index 0a32871758..337fb4175a 100644 --- a/dspace-api/src/main/java/org/dspace/submit/lookup/ArXivService.java +++ b/dspace-api/src/main/java/org/dspace/submit/lookup/ArXivService.java @@ -113,6 +113,9 @@ public class ArXivService { factory.setValidating(false); factory.setIgnoringComments(true); factory.setIgnoringElementContentWhitespace(true); + // disallow DTD parsing to ensure no XXE attacks can occur. + // See https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); DocumentBuilder db = factory.newDocumentBuilder(); Document inDoc = db.parse(response.getEntity().getContent()); diff --git a/dspace-api/src/main/java/org/dspace/submit/lookup/CiNiiService.java b/dspace-api/src/main/java/org/dspace/submit/lookup/CiNiiService.java index 23026353fd..bb59043e52 100644 --- a/dspace-api/src/main/java/org/dspace/submit/lookup/CiNiiService.java +++ b/dspace-api/src/main/java/org/dspace/submit/lookup/CiNiiService.java @@ -102,6 +102,9 @@ public class CiNiiService { factory.setValidating(false); factory.setIgnoringComments(true); factory.setIgnoringElementContentWhitespace(true); + // disallow DTD parsing to ensure no XXE attacks can occur. + // See https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); DocumentBuilder db = factory.newDocumentBuilder(); Document inDoc = db.parse(response.getEntity().getContent()); @@ -178,6 +181,9 @@ public class CiNiiService { factory.setValidating(false); factory.setIgnoringComments(true); factory.setIgnoringElementContentWhitespace(true); + // disallow DTD parsing to ensure no XXE attacks can occur. + // See https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); DocumentBuilder db = factory.newDocumentBuilder(); Document inDoc = db.parse(response.getEntity().getContent()); diff --git a/dspace-api/src/main/java/org/dspace/submit/lookup/CrossRefService.java b/dspace-api/src/main/java/org/dspace/submit/lookup/CrossRefService.java index f73e9c0352..4b99cf1f8b 100644 --- a/dspace-api/src/main/java/org/dspace/submit/lookup/CrossRefService.java +++ b/dspace-api/src/main/java/org/dspace/submit/lookup/CrossRefService.java @@ -99,6 +99,9 @@ public class CrossRefService { factory.setValidating(false); factory.setIgnoringComments(true); factory.setIgnoringElementContentWhitespace(true); + // disallow DTD parsing to ensure no XXE attacks can occur. + // See https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); DocumentBuilder db = factory .newDocumentBuilder(); diff --git a/dspace-api/src/main/java/org/dspace/submit/lookup/PubmedService.java b/dspace-api/src/main/java/org/dspace/submit/lookup/PubmedService.java index fa30ee8ea5..a5e74322f5 100644 --- a/dspace-api/src/main/java/org/dspace/submit/lookup/PubmedService.java +++ b/dspace-api/src/main/java/org/dspace/submit/lookup/PubmedService.java @@ -119,6 +119,9 @@ public class PubmedService { factory.setValidating(false); factory.setIgnoringComments(true); factory.setIgnoringElementContentWhitespace(true); + // disallow DTD parsing to ensure no XXE attacks can occur. + // See https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); DocumentBuilder builder; try { @@ -156,6 +159,9 @@ public class PubmedService { factory.setValidating(false); factory.setIgnoringComments(true); factory.setIgnoringElementContentWhitespace(true); + // disallow DTD parsing to ensure no XXE attacks can occur. + // See https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); DocumentBuilder builder = factory.newDocumentBuilder(); Document inDoc = builder.parse(stream); @@ -216,6 +222,9 @@ public class PubmedService { factory.setValidating(false); factory.setIgnoringComments(true); factory.setIgnoringElementContentWhitespace(true); + // disallow DTD parsing to ensure no XXE attacks can occur. + // See https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); DocumentBuilder builder = factory.newDocumentBuilder(); Document inDoc = builder From c323b989d2b3751fbb4a0b10aaac97423da47917 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 27 Jul 2020 14:42:56 -0500 Subject: [PATCH 06/10] Escape any HTML in user provided param. --- .../main/java/org/dspace/app/rest/OpenSearchController.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/OpenSearchController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/OpenSearchController.java index 42ad173f2e..62c6a9c573 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/OpenSearchController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/OpenSearchController.java @@ -34,6 +34,7 @@ import org.dspace.content.service.CollectionService; import org.dspace.content.service.CommunityService; import org.dspace.core.Context; import org.dspace.core.LogManager; +import org.dspace.core.Utils; import org.dspace.discovery.DiscoverQuery; import org.dspace.discovery.DiscoverResult; import org.dspace.discovery.IndexableObject; @@ -103,7 +104,8 @@ public class OpenSearchController { // do some sanity checking if (!openSearchService.getFormats().contains(format)) { - String err = "Format " + format + " is not supported."; + // Since we are returning error response as HTML, escape any HTML in "format" param + String err = "Format " + Utils.addEntities(format) + " is not supported."; response.setContentType("text/html"); response.setContentLength(err.length()); response.getWriter().write(err); From 79e4e3b497374e889161c15343faf461b250a454 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 27 Jul 2020 15:08:05 -0500 Subject: [PATCH 07/10] Escape special chars in filename which includes user input --- dspace-sword/src/main/java/org/dspace/sword/DepositManager.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dspace-sword/src/main/java/org/dspace/sword/DepositManager.java b/dspace-sword/src/main/java/org/dspace/sword/DepositManager.java index 7535302139..4491f876cc 100644 --- a/dspace-sword/src/main/java/org/dspace/sword/DepositManager.java +++ b/dspace-sword/src/main/java/org/dspace/sword/DepositManager.java @@ -245,6 +245,8 @@ public class DepositManager { String filenameBase = "sword-" + deposit.getUsername() + "-" + (new Date()).getTime(); + // No dots or slashes allowed in filename + filenameBase = filenameBase.replaceAll("\\.", "").replaceAll("/", ""). replaceAll("\\\\", ""); File packageFile = new File(path, filenameBase); File headersFile = new File(path, filenameBase + "-headers"); From 93903de1c599ffc54c5327cb1f202e44737053aa Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 27 Jul 2020 15:23:43 -0500 Subject: [PATCH 08/10] Fix possible int overflow by converting to long before multiplication --- .../src/main/java/org/dspace/harvest/HarvestScheduler.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/harvest/HarvestScheduler.java b/dspace-api/src/main/java/org/dspace/harvest/HarvestScheduler.java index d668b09bc4..5d0545845c 100644 --- a/dspace-api/src/main/java/org/dspace/harvest/HarvestScheduler.java +++ b/dspace-api/src/main/java/org/dspace/harvest/HarvestScheduler.java @@ -134,11 +134,13 @@ public class HarvestScheduler implements Runnable { if (maxActiveThreads == 0) { maxActiveThreads = 3; } - minHeartbeat = ConfigurationManager.getIntProperty("oai", "harvester.minHeartbeat") * 1000; + minHeartbeat = ConfigurationManager.getIntProperty("oai", "harvester.minHeartbeat"); + minHeartbeat = minHeartbeat * 1000; // multiple by 1000 to turn seconds to ms if (minHeartbeat == 0) { minHeartbeat = 30000; } - maxHeartbeat = ConfigurationManager.getIntProperty("oai", "harvester.maxHeartbeat") * 1000; + maxHeartbeat = ConfigurationManager.getIntProperty("oai", "harvester.maxHeartbeat"); + maxHeartbeat = maxHeartbeat * 1000; // multiple by 1000 to turn seconds to ms if (maxHeartbeat == 0) { maxHeartbeat = 3600000; } From 645a1800bb0f54af01269dd8456114efff142f83 Mon Sep 17 00:00:00 2001 From: Tim Donohue Date: Mon, 27 Jul 2020 15:27:51 -0500 Subject: [PATCH 09/10] Fix potentially unsafe external link --- .../main/webapp/static/reports/restReport.js | 153 +++++++++--------- 1 file changed, 77 insertions(+), 76 deletions(-) diff --git a/dspace-rest/src/main/webapp/static/reports/restReport.js b/dspace-rest/src/main/webapp/static/reports/restReport.js index 3efc67e85e..8abe17be06 100644 --- a/dspace-rest/src/main/webapp/static/reports/restReport.js +++ b/dspace-rest/src/main/webapp/static/reports/restReport.js @@ -15,7 +15,7 @@ var Report = function() { this.ROOTPATH = "/xmlui/handle/" //this.ROOTPATH = "/jspui/handle/" //this.ROOTPATH = "/handle/" - + //Indicate if Password Authentication is supported this.makeAuthLink = function(){return false;}; @@ -27,34 +27,34 @@ var Report = function() { this.getId = function(obj) { return obj.uuid; } - + //Override this method is sortable.js has been included this.hasSorttable = function() { return false; } - + this.getDefaultParameters = function(){ return {}; } this.getCurrentParameters = function(){ return {}; } - + this.saveUrl = function() { this.myReportParameters.saveAsUrl(this.getCurrentParameters()); } - + this.getLoginPayload = function() { //Placeholder to allow a customized report to prompt for email/password //If not enabled, the authenticaton callback will be called immediately var email = $("#restemail").val(); var pass = $("#restpass").val(); if (email == "" || pass == "") { - return undefined; + return undefined; } else if (email == null || pass == null) { - return undefined; + return undefined; } else { - return {email: email, password: pass}; + return {email: email, password: pass}; } } this.getLangSuffix = function(){ @@ -82,15 +82,15 @@ var Report = function() { className: 'spinner', // The CSS class to assign to the spinner zIndex: 2e9, // The z-index (defaults to 2000000000) top: '400px', // Top position relative to parent - left: '600px' // Left position relative to parent + left: '600px' // Left position relative to parent }); - + this.displayItems = function(itemsTitle, offset, limit, total, funcdec, funcinc) { var count = $("#itemtable tr.data").length; - + var last = offset + limit; var suff = ""; - + if (total == null) { last = offset + count; suff = (count == limit) ? " of " + last + "+ " : " of " + last; @@ -102,7 +102,7 @@ var Report = function() { suff = " of " + total; } suff += " unfiltered; displaying " + count + " filtered" ; - + itemsTitle += " (" + (offset+1) + " - " + last + suff + ")"; $("#prev,#next").attr("disabled",true); $("#itemdiv h3").text(itemsTitle); @@ -110,34 +110,34 @@ var Report = function() { if (offset > 0) $("#prev").attr("disabled", false); $("#prev").off("click").on("click", funcdec); //in case of filters, always allow next - + if (total == null) { - $("#next").attr("disabled", false); + $("#next").attr("disabled", false); } else if (offset + limit < total) { - $("#next").attr("disabled", false); + $("#next").attr("disabled", false); $("#exlimit").addClass("red"); } else if (limit == total) { //total may only be accurate to one page - $("#next").attr("disabled", false); + $("#next").attr("disabled", false); $("#exlimit").addClass("red"); } $("#next").off("click").on("click", funcinc); } - + this.myReportParameters = undefined; this.myFilters = undefined; this.myMetadataFields = undefined; - + this.initMetadataFields = function() { this.myMetadataFields = new MetadataFields(self); - this.myMetadataFields.load(); + this.myMetadataFields.load(); } - + this.initBitstreamFields = function() { this.myBitstreamFields = new BitstreamFields(self); - this.myBitstreamFields.load(); + this.myBitstreamFields.load(); } - + this.baseInit = function() { this.myReportParameters = new ReportParameters( this.getDefaultParameters(), @@ -173,13 +173,13 @@ var Report = function() { }); return itemdata; } - + this.export = function(rows) { var itemdata = "data:text/csv;charset=utf-8," + this.makeCsv(rows); var encodedUri = encodeURI(itemdata); - window.open(encodedUri); + window.open(encodedUri); } - + //this is meant to be overridden for each report this.exportCol = function(colnum, col) { var data = ""; @@ -187,7 +187,7 @@ var Report = function() { data += self.exportCell(col); return data; } - + this.exportCell = function(col) { data = "\""; $(col).contents().each(function(i, node){ @@ -198,16 +198,16 @@ var Report = function() { if ($(node).is("div:not(:last-child)")) { data += "||"; } - } + } }); data += "\""; return data; } - + this.init = function() { - this.baseInit(); + this.baseInit(); } - + } var Auth = function(report) { @@ -242,17 +242,17 @@ var Auth = function(report) { self.authStat(); self.callback(); } - }); + }); } this.verifyShibLogin = function() { var self = this; $.ajax({ - url: "/rest/shibboleth-login", + url: "/rest/shibboleth-login", success: self.authStat }); } - + this.authStat = function() { var self = this; $.ajax({ @@ -264,7 +264,7 @@ var Auth = function(report) { success: function(data) { var user = ""; if (data.email != undefined) { - user = data.email; + user = data.email; } else { user = "You are not logged in. Some items may be excluded from reports."; } @@ -279,10 +279,10 @@ var Auth = function(report) { if (data.email == undefined && self.report.makeShibLink()) { self.verifyShibLogin(); } - } - }); + } + }); } - + this.logout = function() { var self = this; $.ajax({ @@ -293,7 +293,7 @@ var Auth = function(report) { complete: function(xhr, status) { self.authStat(); } - }); + }); } this.getHeaders = function() { var HEADERS = {}; @@ -314,14 +314,14 @@ var ReportParameters = function(defaultParams, prmstr) { var field = tmparr[0]; var val = decodeURIComponent(tmparr[1]); var pval = this.params[field]; - + if ($.isArray(pval)) { - pval[pval.length] = val; + pval[pval.length] = val; } else { this.params[field] = val; } } - $("#limit").val(this.params.limit); + $("#limit").val(this.params.limit); $("#offset").val(this.params.offset); this.limit = this.params.limit; this.offset = this.params.offset; @@ -350,11 +350,11 @@ var ReportParameters = function(defaultParams, prmstr) { var lim = $("#limit").val(); if ($.isNumeric(val) && $.isNumeric(lim)) { if (increment) { - $("#offset").val(this.getNextOffset()); + $("#offset").val(this.getNextOffset()); } else { - $("#offset").val(this.getPrevOffset()); + $("#offset").val(this.getPrevOffset()); } - } + } } this.saveAsUrl = function(params) { @@ -381,7 +381,7 @@ var Filters = function() { $("#filter-reload").attr("disabled", false); } ); - + $.getJSON( "/rest/filters", function(data){ @@ -444,13 +444,13 @@ var Filters = function() { list = "none"; } return list; - } + } } var MetadataFields = function(report) { this.metadataSchemas = undefined; var self = this; - + this.load = function(){ $.ajax({ url: "/rest/registries/schema", @@ -463,15 +463,15 @@ var MetadataFields = function(report) { }, complete: function(xhr, status) { } - }); + }); } - + this.initFields = function(data, report) { var params = report.myReportParameters.params; self.metadataSchemas = data; self.drawShowFields(params["show_fields[]"]); } - + this.getShowFields = function(){ var val = $("#show-fields select").val(); return val == null ? Array() : val; @@ -497,7 +497,7 @@ var MetadataFields = function(report) { }); }); } - + this.initQueries = function(){}; } @@ -508,15 +508,15 @@ var BitstreamFields = function(report) { } this.map = [ { - key: "original-file-names", - name: "Original File Names", + key: "original-file-names", + name: "Original File Names", ftest: self.isOriginal, fval: function(bit) { return bit.name; } }, { - key: "mime-type", + key: "mime-type", name: "Mime Type", ftest: self.isOriginal, fval: function(bit) { @@ -524,7 +524,7 @@ var BitstreamFields = function(report) { } }, { - key: "bitstream-format", + key: "bitstream-format", name: "Bitstream Format", ftest: self.isOriginal, fval: function(bit) { @@ -532,7 +532,7 @@ var BitstreamFields = function(report) { } }, { - key: "bitstream-description", + key: "bitstream-description", name: "Bitstream Description", ftest: self.isOriginal, fval: function(bit) { @@ -540,7 +540,7 @@ var BitstreamFields = function(report) { } }, { - key: "bitstream-size", + key: "bitstream-size", name: "Bitstream Size", ftest: self.isOriginal, fval: function(bit) { @@ -548,18 +548,18 @@ var BitstreamFields = function(report) { } }, { - key: "bitstream-checksum", + key: "bitstream-checksum", name: "MD5 Checksum", ftest: self.isOriginal, fval: function(bit) { if (bit.checkSum.checkSumAlgorithm === "MD5") { - return bit.checkSum.value; + return bit.checkSum.value; } return ""; } }, ]; - + this.load = function(){ self.initFields(report); } @@ -568,7 +568,7 @@ var BitstreamFields = function(report) { var params = report.myReportParameters.params; self.drawShowFieldsBits(params["show_fields_bits[]"]); }; - + this.hasBitstreamFields = function() { return self.getShowFieldsBits() != null; } @@ -576,20 +576,20 @@ var BitstreamFields = function(report) { var val = $("#show-fields-bits select").val(); return val == null ? Array() : val; } - + this.drawShowFieldsBits = function(pfieldsBits) { var sel = $("