Skip to content

Commit 30c24d2

Browse files
committed
feat: added allowed files extensions configuration, applied to file upload in admin and data access pages #4449
1 parent 906ea60 commit 30c24d2

File tree

7 files changed

+51
-14
lines changed

7 files changed

+51
-14
lines changed

mica-core/src/main/resources/config/application.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ metrics:
2727
cache:
2828
timeToLiveSeconds: 3600
2929

30-
portal.draftResource.urlPattern: "{portalUrl}/mica/{resourceType}/{resourceId}/draft/{shareKey}"
30+
portal:
31+
draftResource:
32+
urlPattern: "{portalUrl}/mica/{resourceType}/{resourceId}/draft/{shareKey}"
33+
files:
34+
extensions: .xls,.xlsx,.ods,.doc,.docx,.odt,.ppt,.pptx,.odp,.xml,.yaml,.yml,.jpg,.jpeg,.png,.pdf,.txt,.csv,.tsv,.sav,.sas7bdat,.sas7bcat,.por,.gz,.bz2,.xz,.zip
3135

3236
#
3337
# Plugins

mica-rest/src/main/java/org/obiba/mica/file/rest/TempFilesResource.java

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,38 +12,47 @@
1212

1313
import java.io.IOException;
1414
import java.net.URI;
15+
import java.util.List;
16+
import java.util.Spliterators;
17+
import java.util.stream.Collectors;
1518

1619
import javax.inject.Inject;
1720
import javax.servlet.http.HttpServletRequest;
18-
import javax.ws.rs.Consumes;
19-
import javax.ws.rs.POST;
20-
import javax.ws.rs.Path;
21-
import javax.ws.rs.PathParam;
21+
import javax.ws.rs.*;
2222
import javax.ws.rs.core.Context;
2323
import javax.ws.rs.core.MediaType;
2424
import javax.ws.rs.core.Response;
2525
import javax.ws.rs.core.UriInfo;
2626

27+
import com.google.common.base.Splitter;
28+
import org.apache.commons.compress.utils.Lists;
2729
import org.apache.commons.fileupload.FileItem;
2830
import org.apache.commons.fileupload.FileItemFactory;
2931
import org.apache.commons.fileupload.FileUploadException;
3032
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
3133
import org.apache.commons.fileupload.servlet.ServletFileUpload;
34+
import org.apache.commons.io.StreamIterator;
3235
import org.apache.shiro.authz.annotation.RequiresPermissions;
3336
import org.obiba.mica.file.TempFile;
3437
import org.obiba.mica.file.service.TempFileService;
3538
import org.slf4j.Logger;
3639
import org.slf4j.LoggerFactory;
40+
import org.springframework.beans.factory.annotation.Value;
3741
import org.springframework.context.ApplicationContext;
3842

3943
import com.codahale.metrics.annotation.Timed;
44+
import org.springframework.stereotype.Component;
4045

46+
@Component
4147
@Path("/files/temp")
4248
@RequiresPermissions({ "/files:UPLOAD" })
4349
public class TempFilesResource {
4450

4551
private static final Logger log = LoggerFactory.getLogger(TempFilesResource.class);
4652

53+
@Value("${portal.files.extensions}")
54+
private String filesExtensions;
55+
4756
@Inject
4857
private ApplicationContext applicationContext;
4958

@@ -55,17 +64,22 @@ public class TempFilesResource {
5564
@Timed
5665
public Response upload(@Context HttpServletRequest request, @Context UriInfo uriInfo)
5766
throws IOException, FileUploadException {
58-
5967
FileItem fileItem = getUploadedFile(request);
60-
6168
if (fileItem == null) throw new FileUploadException("Failed to extract file item from request");
69+
validateFileExtension(fileItem.getName());
6270
TempFile tempFile = tempFileService.addTempFile(fileItem.getName(), fileItem.getInputStream());
6371
URI location = uriInfo.getBaseUriBuilder().path(TempFilesResource.class).path(TempFilesResource.class, "file")
6472
.build(tempFile.getId());
65-
6673
return Response.created(location).build();
6774
}
6875

76+
@GET
77+
@Path("/_extensions")
78+
@Produces(MediaType.TEXT_PLAIN)
79+
public Response getFilesExtensions() {
80+
return Response.ok().type(MediaType.TEXT_PLAIN).entity(filesExtensions).build();
81+
}
82+
6983
@Path("/{id}")
7084
public TempFileResource file(@PathParam("id") String id) {
7185
TempFileResource tempFileResource = applicationContext.getBean(TempFileResource.class);
@@ -84,4 +98,11 @@ FileItem getUploadedFile(HttpServletRequest request) throws FileUploadException
8498

8599
return null;
86100
}
101+
102+
private void validateFileExtension(String filename) {
103+
List<String> extensions = Splitter.on(",").splitToList(filesExtensions).stream().map(ext -> ext.trim().toLowerCase()).collect(Collectors.toList());
104+
if (extensions.isEmpty() || extensions.contains("*")) return;
105+
boolean valid = extensions.stream().anyMatch(ext -> filename.toLowerCase().endsWith(ext));
106+
if (!valid) throw new BadRequestException("Not a valid file format");
107+
}
87108
}

mica-webapp/src/main/java/org/obiba/mica/web/controller/DataAccessController.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.obiba.mica.security.Roles;
2525
import org.obiba.mica.web.controller.domain.DataAccessCollaboratorBundle;
2626
import org.obiba.mica.web.controller.domain.TimelineItem;
27+
import org.springframework.beans.factory.annotation.Value;
2728
import org.springframework.stereotype.Controller;
2829
import org.springframework.web.bind.annotation.GetMapping;
2930
import org.springframework.web.bind.annotation.PathVariable;
@@ -42,6 +43,9 @@
4243
@Controller
4344
public class DataAccessController extends BaseDataAccessController {
4445

46+
@Value("${portal.files.extensions}")
47+
private String filesExtensions;
48+
4549
@Inject
4650
private DataAccessCollaboratorService dataAccessCollaboratorService;
4751

@@ -141,6 +145,8 @@ public ModelAndView getDocuments(@PathVariable String id) {
141145

142146
params.put("permissions", permissions);
143147

148+
params.put("filesExtensions", filesExtensions);
149+
144150
return new ModelAndView("data-access-documents", params);
145151
} else {
146152
return new ModelAndView("redirect:../signin?redirect=" + micaConfigService.getContextPath() + "/data-access-documents%2F" + id);

mica-webapp/src/main/resources/_templates/data-access-documents.ftl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@
135135
<form>
136136
<div class="form-group">
137137
<label for="file-field"><@message "select-file-to-upload"/></label>
138-
<input type="file" id="file-field" class="form-control-file" onchange="handleFiles(this.files)">
138+
<input type="file" id="file-field" class="form-control-file" accept="${filesExtensions}" onchange="handleFiles(this.files)">
139139
<input type="hidden" id="file-id">
140140
</div>
141141
<div>

mica-webapp/src/main/webapp/app/file-system/file-system-controller.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,7 @@ mica.fileSystem
566566
{name: 'REVIEWER', label: $filter('translate')('permission.reviewer')}
567567
];
568568

569+
$scope.filesExtensions = FileSystemService.filesExtensions.get();
569570
$scope.screen = $rootScope.screen;
570571
$scope.hasRole = $rootScope.hasRole;
571572
$scope.getDocumentTypeTitle = FileSystemService.getDocumentTypeTitle;

mica-webapp/src/main/webapp/app/file-system/file-system-service.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ mica.fileSystem
7575
});
7676
}])
7777

78-
.service('FileSystemService', ['TempFileResource', 'Upload',
79-
function (TempFileResource, Upload) {
78+
.service('FileSystemService', ['TempFileResource', 'Upload','$resource',
79+
function (TempFileResource, Upload, $resource) {
8080

8181
this.isFile = function (document) {
8282
return document && document.type === 'FILE';
@@ -86,6 +86,10 @@ mica.fileSystem
8686
return document && document.path === '/';
8787
};
8888

89+
this.filesExtensions = $resource(contextPath + '/ws/files/temp/_extensions', {}, {
90+
'get': {method: 'GET', accept: 'text/plain'}
91+
});
92+
8993
this.getDocumentTypeTitle = function (type) {
9094
switch (type) {
9195
case 'study':
@@ -121,7 +125,7 @@ mica.fileSystem
121125
switch (ext[1].toLowerCase()) {
122126
case 'doc':
123127
case 'docx':
124-
case 'odm':
128+
case 'odt':
125129
case 'gdoc':
126130
return 'fa-file-word-o';
127131

@@ -133,7 +137,8 @@ mica.fileSystem
133137
return 'fa-file-pdf-o';
134138

135139
case 'ppt':
136-
case 'odt':
140+
case 'pptx':
141+
case 'odp':
137142
return 'fa-file-powerpoint-o';
138143

139144
case 'xt':

mica-webapp/src/main/webapp/app/file-system/views/file-system-template.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141

4242
<div class="voffset2">
4343
<a ng-if="!data.isFile" ng-disabled="!data.document.permissions.edit || data.document.revisionStatus !== 'DRAFT'"
44-
class="btn btn-info btn-responsive" ngf-multiple="true" ngf-select ngf-change="onFileSelect($files)">
44+
class="btn btn-info btn-responsive" ngf-multiple="true" ngf-select ngf-accept="filesExtensions" ngf-change="onFileSelect($files)">
4545
<i class="fa fa-upload"></i> {{'upload' | translate}}
4646
</a>
4747
<a ng-if="data.document.permissions.view && data.isFile" target="_self" class="btn btn-info btn-responsive" ng-href="ws/draft/file-dl/{{data.document.path}} ">

0 commit comments

Comments
 (0)