diff options
author | mconway <michael_conway@unc.edu> | 2013-06-11 15:42:52 (GMT) |
---|---|---|
committer | mconway <michael_conway@unc.edu> | 2013-06-11 15:42:52 (GMT) |
commit | e003f9e72181705f8eb66dd53b80526f112799da (patch) | |
tree | f02d7d17585c7db0ffbe8d22f321f42caa79a8c1 | |
parent | 9ef17f1f03246616584a981cd6dfa4ee0b3c3412 (diff) | |
download | QCG-Data-e003f9e72181705f8eb66dd53b80526f112799da.zip QCG-Data-e003f9e72181705f8eb66dd53b80526f112799da.tar.gz QCG-Data-e003f9e72181705f8eb66dd53b80526f112799da.tar.bz2 |
[#715] check access before download
-rwxr-xr-x | idrop-web/grails-app/conf/Config.groovy | 2 | ||||
-rwxr-xr-x | idrop-web/grails-app/controllers/org/irods/mydrop/controller/FileController.groovy | 64 | ||||
-rwxr-xr-x | idrop-web/grails-app/i18n/messages.properties | 1 | ||||
-rwxr-xr-x | idrop-web/grails-app/views/browse/browseDetails.gsp | 289 | ||||
-rw-r--r-- | idrop-web/release_notes.txt | 3 | ||||
-rw-r--r-- | idrop-web/web-app/js/mydrop/home.js | 34 |
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');
}
|