summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormconway <michael_conway@unc.edu>2013-06-11 15:42:52 (GMT)
committer mconway <michael_conway@unc.edu>2013-06-11 15:42:52 (GMT)
commite003f9e72181705f8eb66dd53b80526f112799da (patch)
treef02d7d17585c7db0ffbe8d22f321f42caa79a8c1
parent9ef17f1f03246616584a981cd6dfa4ee0b3c3412 (diff)
downloadQCG-Data-e003f9e72181705f8eb66dd53b80526f112799da.zip
QCG-Data-e003f9e72181705f8eb66dd53b80526f112799da.tar.gz
QCG-Data-e003f9e72181705f8eb66dd53b80526f112799da.tar.bz2
[#715] check access before download
-rwxr-xr-xidrop-web/grails-app/conf/Config.groovy2
-rwxr-xr-xidrop-web/grails-app/controllers/org/irods/mydrop/controller/FileController.groovy64
-rwxr-xr-xidrop-web/grails-app/i18n/messages.properties1
-rwxr-xr-xidrop-web/grails-app/views/browse/browseDetails.gsp289
-rw-r--r--idrop-web/release_notes.txt3
-rw-r--r--idrop-web/web-app/js/mydrop/home.js34
6 files changed, 243 insertions, 150 deletions
diff --git a/idrop-web/grails-app/conf/Config.groovy b/idrop-web/grails-app/conf/Config.groovy
index 21b141f..9b743cc 100755
--- a/idrop-web/grails-app/conf/Config.groovy
+++ b/idrop-web/grails-app/conf/Config.groovy
@@ -186,7 +186,7 @@ log4j = {
'net.sf.ehcache.hibernate'
//info 'org.irods.mydrop'
- debug 'org.irods.jargon'
+ info 'org.irods.jargon'
warn 'org.irods.jargon.spring.security'
warn 'org.springframework'
diff --git a/idrop-web/grails-app/controllers/org/irods/mydrop/controller/FileController.groovy b/idrop-web/grails-app/controllers/org/irods/mydrop/controller/FileController.groovy
index 137b225..aca5fb5 100755
--- a/idrop-web/grails-app/controllers/org/irods/mydrop/controller/FileController.groovy
+++ b/idrop-web/grails-app/controllers/org/irods/mydrop/controller/FileController.groovy
@@ -79,11 +79,11 @@ class FileController {
def length = irodsFile.length()
log.info("file length = ${length}")
log.info("opened input stream")
-
+
response.setContentType("application/octet-stream")
response.setContentLength((int) length)
response.setHeader("Content-disposition", "attachment;filename=\"${irodsFile.name}\"")
-
+
response.outputStream << irodsFileInputStream // Performing a binary stream copy
} catch (CatNoAccessException e) {
log.error("no access error", e)
@@ -127,14 +127,14 @@ class FileController {
render(view:"uploadDialog", model:[irodsTargetCollection:irodsTargetCollection])
}
-
+
/**
* Prepare a quick upload dialog to upload a file to the default location using the quick upload service
*
*/
def prepareQuickUploadDialog = {
log.info("prepareQuickUploadDialog")
-
+
log.info("checking if uploads default directory needs to be created")
/* here we could do any processing on irods, such as provisioning of metadata fields based on target
@@ -313,20 +313,20 @@ class FileController {
log.info("name for create folder:${newFolderName}")
IRODSFileFactory irodsFileFactory = irodsAccessObjectFactory.getIRODSFileFactory(irodsAccount)
IRODSFile targetFile = irodsFileFactory.instanceIRODSFile(parent + "/" + newFolderName)
-
+
if (targetFile.exists()) {
log.error "no name in request"
def message = message(code:"error.duplicate.file")
response.sendError(500,message)
}
-
+
targetFile.mkdirs()
log.info("file created:${targetFile.absolutePath}")
render targetFile.getAbsolutePath()
} catch (CatNoAccessException e) {
- log.error("no access error", e)
- response.sendError(500, message(code:"message.no.access"))
- }
+ log.error("no access error", e)
+ response.sendError(500, message(code:"message.no.access"))
+ }
}
@@ -368,12 +368,12 @@ class FileController {
}
try {
prevFile.renameTo(newFile)
-
+
// return the parent, which will be reloaded
render newFile.parent
} catch (CatNoAccessException e) {
- log.error("no access error", e)
- response.sendError(500, message(code:"message.no.access"))
+ log.error("no access error", e)
+ response.sendError(500, message(code:"message.no.access"))
}
}
@@ -414,6 +414,44 @@ class FileController {
render targetAbsPath
}
+ /**
+ * Do a check for access rights to a file at a given path, if it exists, is a data object, and the user has read access, return "OK", otherwise
+ * throw an appropriate exception
+ */
+ def screenForDownloadRights = {
+ log.info("screenForDownloadRights")
+
+ String sourceAbsPath = params['absPath']
+ if (!sourceAbsPath) {
+ log.error "no source path in request"
+ def message = message(code:"error.no.path.provided")
+ response.sendError(500,message)
+ }
+
+ log.info("getting file for path:${sourceAbsPath}")
+ IRODSFile irodsFile = irodsAccessObjectFactory.getIRODSFileFactory(irodsAccount).instanceIRODSFile(sourceAbsPath)
+ if (!irodsFile.exists()) {
+ log.error "file does not exist"
+ def message = message(code:"error.file.not.found")
+ response.sendError(500,message)
+ }
+
+ if (!irodsFile.isFile()) {
+ log.error "not a file"
+ def message = message(code:"error.file.not.found")
+ response.sendError(500,message)
+ }
+
+ if (!irodsFile.canRead()) {
+ log.error "no access to file"
+ def message = message(code:"error.no.access.permission")
+ response.sendError(500,message)
+ }
+
+ render "OK"
+
+ }
+
/**
* Copy a file in iRODS
*/
@@ -433,7 +471,7 @@ class FileController {
def message = message(code:"error.no.path.provided")
response.sendError(500,message)
}
-
+
String defaultResource = irodsAccount.defaultStorageResource
log.info("defaultResource:${defaultResource}")
diff --git a/idrop-web/grails-app/i18n/messages.properties b/idrop-web/grails-app/i18n/messages.properties
index b82e74a..6d60c26 100755
--- a/idrop-web/grails-app/i18n/messages.properties
+++ b/idrop-web/grails-app/i18n/messages.properties
@@ -179,6 +179,7 @@ browse.page.prompt=Select a directory or file to see info and actions
# messages
error.confirm.password.missing=Confirmation password is null or blank
error.nothing.selected=Nothing was selected for the action
+error.no.access.permission=No access permission
error.no.action=No action found
error.no.audit.access=This user does not have permission to view audit data
error.no.data.found=No data found
diff --git a/idrop-web/grails-app/views/browse/browseDetails.gsp b/idrop-web/grails-app/views/browse/browseDetails.gsp
index 24a7a1e..87d21cc 100755
--- a/idrop-web/grails-app/views/browse/browseDetails.gsp
+++ b/idrop-web/grails-app/views/browse/browseDetails.gsp
@@ -1,149 +1,166 @@
<div id="browseDetailsMessageArea"></div>
<div id=browseDetailsDialogArea" style-"height:0px;></div>
<g:hiddenField id="browseDetailsAbsPath" name="absolutePath"
- value="${parent.collectionName}" />
-
-<div style="overflow:visible; position:relative;">
- <div id="idropLiteArea">
- <!-- area to show idrop lite applet -->
- </div>
- <div id="toggleHtmlArea">
- <div id="infoDialogArea">
- <!-- no empty divs -->
- </div>
- <!-- render the paging controls if needed -->
- <g:if test="${pagingActions.paging}">
- <g:render template="/browse/browseDetailsToolbar" />
- </g:if>
-
- <div id="detailsTopSection" >
- <form id="browseDetailsForm" name="browseDetailsForm">
- <table class="table table-striped table-hover" cellspacing="0" cellpadding="0" border="0"
- id="browseDataDetailsTable">
- <thead>
- <tr>
- <th></th>
- <th>
-
- <div class="btn-group">
- <a class="btn dropdown-toggle" data-toggle="dropdown" href="#">Action<span class="caret"></span></a>
- <ul class="dropdown-menu">
- <li id="menuAddToCartDetails"><a href="#addAllToCartDetails" onclick="addSelectedToCart()"><g:message code="text.add.all.to.cart" /></a></li>
- <li id="menuDeleteDetails"><a href="#deleteAllDetails" onclick="deleteSelected()"><g:message code="text.delete.all" /></a></li>
- <!-- dropdown menu links -->
- </ul>
- </div>
-
- </th>
- <th><g:message code="text.name" /></th>
- <th><g:message code="text.type" /></th>
- <th><g:message code="text.modified" /></th>
- <th><g:message code="text.length" /></th>
- </tr>
- </thead>
- <tbody>
- <g:each in="${collection}" var="entry">
- <tr id="${entry.formattedAbsolutePath}" class="draggableFile">
-
- <td><span
- class="ui-icon-circle-plus browse_detail_icon ui-icon"></span>
- </td>
- <td><g:checkBox name="selectDetail"
- value="${entry.formattedAbsolutePath}" checked="false" />
- <g:if test="${entry.objectType.toString() == 'COLLECTION'}"><img class="icon-info-sign" onclick="infoHere('${entry.formattedAbsolutePath}')"/></g:if> <g:else><img class="icon-info-sign" onclick="infoHere('${entry.formattedAbsolutePath}')"/></g:else>
- </td>
- <td><g:if
- test="${entry.objectType.toString() == 'COLLECTION'}">
- <a href="#" id="${entry.formattedAbsolutePath}" onclick="clickOnPathInBrowseDetails(this.id)">${entry.nodeLabelDisplayValue}</a>
-
- </g:if> <g:else>
- ${entry.nodeLabelDisplayValue}
-
- <!-- <g:link url="${'file/download' + entry.formattedAbsolutePath}">
-${entry.nodeLabelDisplayValue} -->
- </g:link>
- </g:else></td>
- <td>
-${entry.objectType}
- </td>
- <td>
-${entry.modifiedAt}
- </td>
- <td>
-${entry.displayDataSize}
- </td>
- </tr>
- </g:each>
-
- </tbody>
-
- <tfoot>
- <tr>
- <td></td>
- <td></td>
- <td></td>
- <td></td>
- <td></td>
- <td></td>
- </tr>
- </tfoot>
- </table>
- </form>
- </div>
- </div>
+ value="${parent.collectionName}" />
+
+<div style="overflow: visible; position: relative;">
+ <div id="idropLiteArea">
+ <!-- area to show idrop lite applet -->
+ </div>
+ <div id="toggleHtmlArea">
+ <div id="infoDialogArea">
+ <!-- no empty divs -->
+ </div>
+ <!-- render the paging controls if needed -->
+ <g:if test="${pagingActions.paging}">
+ <g:render template="/browse/browseDetailsToolbar" />
+ </g:if>
+
+ <div id="detailsTopSection">
+ <form id="browseDetailsForm" name="browseDetailsForm">
+ <table class="table table-striped table-hover" cellspacing="0"
+ cellpadding="0" border="0" id="browseDataDetailsTable">
+ <thead>
+ <tr>
+ <th></th>
+ <th>
+
+ <div class="btn-group">
+ <a class="btn dropdown-toggle" data-toggle="dropdown" href="#">Action<span
+ class="caret"></span></a>
+ <ul class="dropdown-menu">
+ <li id="menuAddToCartDetails"><a
+ href="#addAllToCartDetails" onclick="addSelectedToCart()"><g:message
+ code="text.add.all.to.cart" /></a></li>
+ <li id="menuDeleteDetails"><a href="#deleteAllDetails"
+ onclick="deleteSelected()"><g:message
+ code="text.delete.all" /></a></li>
+ <!-- dropdown menu links -->
+ </ul>
+ </div>
+
+ </th>
+ <th><g:message code="text.name" /></th>
+ <th><g:message code="text.type" /></th>
+ <th><g:message code="text.modified" /></th>
+ <th><g:message code="text.length" /></th>
+ </tr>
+ </thead>
+ <tbody>
+ <g:each in="${collection}" var="entry">
+ <tr id="${entry.formattedAbsolutePath}" class="draggableFile">
+
+ <td><span
+ class="ui-icon-circle-plus browse_detail_icon ui-icon"></span>
+ </td>
+ <td><g:checkBox name="selectDetail"
+ value="${entry.formattedAbsolutePath}" checked="false" /> <g:if
+ test="${entry.objectType.toString() == 'COLLECTION'}">
+ <i class="icon-info-sign"
+ onclick="infoHere('${entry.formattedAbsolutePath}')" ></i>
+ </g:if> <g:else>
+ <i class="icon-info-sign"
+ onclick="infoHere('${entry.formattedAbsolutePath}')" ></i>
+ </g:else></td>
+ <td><g:if
+ test="${entry.objectType.toString() == 'COLLECTION'}">
+ <a href="#" id="${entry.formattedAbsolutePath}"
+ onclick="clickOnPathInBrowseDetails(this.id)">
+ ${entry.nodeLabelDisplayValue}
+ </a>
+
+ </g:if> <g:else>
+ ${entry.nodeLabelDisplayValue}
+
+
+ <i class="icon-download"
+ onclick="downloadViaToolbarGivenPath('${entry.formattedAbsolutePath}')" ></i>
+
+ </g:else></td>
+ <td>
+ ${entry.objectType}
+ </td>
+ <td>
+ ${entry.modifiedAt}
+ </td>
+ <td>
+ ${entry.displayDataSize}
+ </td>
+ </tr>
+ </g:each>
+
+ </tbody>
+
+ <tfoot>
+ <tr>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+ </tfoot>
+ </table>
+ </form>
+ </div>
+ </div>
</div>
<script>
+ var dataTable;
- var dataTable;
-
- tableParams = {"bJQueryUI" : true,
- "bLengthChange": false,
- "bFilter": false,
- "iDisplayLength" : 500,
- "sDom": "<'row'<'span10'l><'span8'f>r>t<'row'<'span10'i><'span10'p>>",
- "aoColumns" : [
- {'sWidth': '20px', 'bSortable':false},
- {'sWidth': '20px', 'bSortable':false},
- { 'sWidth': '120px' },
- { 'sWidth': '30px' },
- { 'sWidth': '40px' },
- { 'sWidth': '40px' }
-
- ],
-
- "fnInitComplete": function() {
- this.fnAdjustColumnSizing(true);
- }
-
- }
-
- $(function() {
- //alert("building table ");
- dataTable = lcBuildTableInPlace("#browseDataDetailsTable", browseDetailsClick, ".browse_detail_icon", tableParams);
- $("#infoDiv").resize();
- $.extend( $.fn.dataTableExt.oStdClasses, {
- "sSortAsc": "header headerSortDown",
- "sSortDesc": "header headerSortUp",
- "sSortable": "header"
- } );
- });
-
- function showLegend() {
- $("#legend").show("slow");
- }
-
- function hideLegend() {
- $("#legend").hide("slow");
- }
+ tableParams = {
+ "bJQueryUI" : true,
+ "bLengthChange" : false,
+ "bFilter" : false,
+ "iDisplayLength" : 500,
+ "sDom" : "<'row'<'span10'l><'span8'f>r>t<'row'<'span10'i><'span10'p>>",
+ "aoColumns" : [ {
+ 'sWidth' : '20px',
+ 'bSortable' : false
+ }, {
+ 'sWidth' : '20px',
+ 'bSortable' : false
+ }, {
+ 'sWidth' : '120px'
+ }, {
+ 'sWidth' : '30px'
+ }, {
+ 'sWidth' : '40px'
+ }, {
+ 'sWidth' : '40px'
+ }
+ ],
+
+ "fnInitComplete" : function() {
+ this.fnAdjustColumnSizing(true);
+ }
+
+ }
+
+ $(function() {
+ //alert("building table ");
+ dataTable = lcBuildTableInPlace("#browseDataDetailsTable",
+ browseDetailsClick, ".browse_detail_icon", tableParams);
+ $("#infoDiv").resize();
+ $.extend($.fn.dataTableExt.oStdClasses, {
+ "sSortAsc" : "header headerSortDown",
+ "sSortDesc" : "header headerSortUp",
+ "sSortable" : "header"
+ });
+ });
+
+ function showLegend() {
+ $("#legend").show("slow");
+ }
+
+ function hideLegend() {
+ $("#legend").hide("slow");
+ }
function infoHere(path) {
setDefaultView("info");
selectTreePathFromIrodsPath(path);
}
-
-
-
-
-
</script> \ No newline at end of file
diff --git a/idrop-web/release_notes.txt b/idrop-web/release_notes.txt
index e34921d..da2ee4d 100644
--- a/idrop-web/release_notes.txt
+++ b/idrop-web/release_notes.txt
@@ -58,6 +58,9 @@ Note that the following bug and feature requests are logged in GForge with relat
*[#1312] fix public link access to not override account if alread logged in
+*[#715] check access before download
+**Check file access and verify is a data object before doing a download, displaying a nicer error message
+
==Features==
*[#984] iDrop web '2.0' redesign effort
diff --git a/idrop-web/web-app/js/mydrop/home.js b/idrop-web/web-app/js/mydrop/home.js
index 77e581c..cafadea 100644
--- a/idrop-web/web-app/js/mydrop/home.js
+++ b/idrop-web/web-app/js/mydrop/home.js
@@ -1652,7 +1652,41 @@ function downloadViaToolbarGivenPath(path) {
showErrorMessage(jQuery.i18n.prop('msg.path.missing'));
return false;
}
+
+ showBlockingPanel();
+
+ var params = {
+ absPath : path
+ }
+ var jqxhr = $
+ .post(context + "/file/screenForDownloadRights", params, null, "html")
+ .success(
+ function(returnedData, status, xhr) {
+ var continueReq = checkForSessionTimeout(
+ returnedData, xhr);
+
+ if (!continueReq) {
+ return false;
+ }
+
+ unblockPanel();
+ doActualDownload(path);
+
+
+ }).error(function(xhr, status, error) {
+ setErrorMessage(xhr.responseText);
+ unblockPanel();
+ });
+}
+
+function doActualDownload(path) {
+
+ if (path == null) {
+ setErrorMessage(jQuery.i18n.prop('msg.path.missing'));
+ return false;
+ }
+
window.open(context + '/file/download' + escape(path), '_self');
}