Fixed security issues with Dataset Description access

This commit is contained in:
George Kalampokis 2020-02-26 13:36:42 +02:00
parent 03619a65fc
commit 2b30d4e2d2
11 changed files with 102 additions and 43 deletions

View File

@ -11,6 +11,7 @@ import eu.eudat.data.query.items.table.dmp.DataManagementPlanTableRequest;
import eu.eudat.data.query.items.table.dmp.DataManagmentPlanPublicTableRequest;
import eu.eudat.exceptions.datamanagementplan.DMPNewVersionException;
import eu.eudat.exceptions.datamanagementplan.DMPWithDatasetsDeleteException;
import eu.eudat.exceptions.security.UnauthorisedException;
import eu.eudat.logic.managers.DataManagementPlanManager;
import eu.eudat.logic.managers.DatasetManager;
import eu.eudat.logic.proxy.config.configloaders.ConfigLoader;
@ -123,7 +124,11 @@ public class DMPs extends BaseController {
DataManagementPlanOverviewModel dataManagementPlan = this.dataManagementPlanManager.getOverviewSingle(id, principal);
return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<DataManagementPlanOverviewModel>().status(ApiMessageCode.NO_MESSAGE).payload(dataManagementPlan));
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new ResponseItem<DataManagementPlanOverviewModel>().status(ApiMessageCode.ERROR_MESSAGE));
if (e instanceof UnauthorisedException) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(new ResponseItem<DataManagementPlanOverviewModel>().status(ApiMessageCode.ERROR_MESSAGE));
} else {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new ResponseItem<DataManagementPlanOverviewModel>().status(ApiMessageCode.ERROR_MESSAGE));
}
}
}

View File

@ -4,6 +4,7 @@ import eu.eudat.data.entities.Dataset;
import eu.eudat.data.query.items.item.dataset.DatasetWizardAutocompleteRequest;
import eu.eudat.data.query.items.item.datasetprofile.DatasetProfileWizardAutocompleteRequest;
import eu.eudat.exceptions.datasetwizard.DatasetWizardCannotUnlockException;
import eu.eudat.exceptions.security.UnauthorisedException;
import eu.eudat.logic.managers.DatasetManager;
import eu.eudat.logic.managers.DatasetWizardManager;
import eu.eudat.logic.managers.UserManager;
@ -15,6 +16,7 @@ import eu.eudat.models.data.datasetwizard.DataManagentPlanListingModel;
import eu.eudat.models.data.datasetwizard.DatasetWizardModel;
import eu.eudat.models.data.dmp.AssociatedProfile;
import eu.eudat.models.data.helpers.responses.ResponseItem;
import eu.eudat.models.data.listingmodels.DataManagementPlanOverviewModel;
import eu.eudat.models.data.security.Principal;
import eu.eudat.models.data.user.composite.PagedDatasetProfile;
import eu.eudat.types.ApiMessageCode;
@ -30,6 +32,7 @@ import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.persistence.NoResultException;
import javax.transaction.Transactional;
import java.io.File;
import java.io.FileInputStream;
@ -79,32 +82,43 @@ public class DatasetWizardController extends BaseController {
@Transactional
@RequestMapping(method = RequestMethod.GET, value = {"{id}"}, produces = "application/json")
public @ResponseBody
ResponseEntity getSingle(@PathVariable String id, @RequestHeader("Content-Type") String contentType, @ClaimedAuthorities(claims = {ANONYMOUS}) Principal principal) throws IllegalAccessException, IOException, InstantiationException {
if(contentType.equals("application/xml")) {
VisibilityRuleService visibilityRuleService = this.getApiContext().getUtilitiesService().getVisibilityRuleService();
return this.datasetManager.getDocument(id, visibilityRuleService, contentType);
}
else if (contentType.equals("application/msword")) {
File file = datasetManager.getWordDocument(this.configLoader, id, this.getApiContext().getUtilitiesService().getVisibilityRuleService());
InputStream resource = new FileInputStream(file);
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.setContentLength(file.length());
responseHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
responseHeaders.set("Content-Disposition", "attachment;filename=" + file.getName());
responseHeaders.set("Access-Control-Expose-Headers", "Content-Disposition");
responseHeaders.get("Access-Control-Expose-Headers").add("Content-Type");
ResponseEntity getSingle(@PathVariable String id, @RequestHeader("Content-Type") String contentType, Principal principal) throws IllegalAccessException, IOException, InstantiationException {
try {
if (contentType.equals("application/xml")) {
VisibilityRuleService visibilityRuleService = this.getApiContext().getUtilitiesService().getVisibilityRuleService();
return this.datasetManager.getDocument(id, visibilityRuleService, contentType, principal);
} else if (contentType.equals("application/msword")) {
File file = datasetManager.getWordDocument(this.configLoader, id, this.getApiContext().getUtilitiesService().getVisibilityRuleService(), principal);
InputStream resource = new FileInputStream(file);
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.setContentLength(file.length());
responseHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
responseHeaders.set("Content-Disposition", "attachment;filename=" + file.getName());
responseHeaders.set("Access-Control-Expose-Headers", "Content-Disposition");
responseHeaders.get("Access-Control-Expose-Headers").add("Content-Type");
byte[] content = IOUtils.toByteArray(resource);
resource.close();
Files.deleteIfExists(file.toPath());
return new ResponseEntity<>(content,
responseHeaders,
HttpStatus.OK);
}
else {
DatasetWizardModel dataset = this.datasetManager.getSingle(id);
return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<DatasetWizardModel>().status(ApiMessageCode.NO_MESSAGE).payload(dataset));
byte[] content = IOUtils.toByteArray(resource);
resource.close();
Files.deleteIfExists(file.toPath());
return new ResponseEntity<>(content,
responseHeaders,
HttpStatus.OK);
} else {
DatasetWizardModel dataset = this.datasetManager.getSingle(id, principal);
return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem<DatasetWizardModel>().status(ApiMessageCode.NO_MESSAGE).payload(dataset));
}
} catch (Exception e) {
if (e instanceof UnauthorisedException) {
if (e instanceof UnauthorisedException) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(new ResponseItem<DataManagementPlanOverviewModel>().status(ApiMessageCode.ERROR_MESSAGE));
} else if (e instanceof NoResultException) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new ResponseItem<DataManagementPlanOverviewModel>().status(ApiMessageCode.ERROR_MESSAGE));
} else {
throw e;
}
}
}
return null; // ????
}
@RequestMapping(method = RequestMethod.GET, value = {"/public/{id}"}, produces = "application/json")
@ -137,8 +151,8 @@ public class DatasetWizardController extends BaseController {
@RequestMapping(method = RequestMethod.GET, value = {"/getPDF/{id}"})
public @ResponseBody
ResponseEntity<byte[]> getPDFDocument(@PathVariable String id) throws IllegalAccessException, IOException, InstantiationException, InterruptedException {
File file = datasetManager.getWordDocument(this.configLoader, id, this.getApiContext().getUtilitiesService().getVisibilityRuleService());
ResponseEntity<byte[]> getPDFDocument(@PathVariable String id, Principal principal) throws IllegalAccessException, IOException, InstantiationException, InterruptedException {
File file = datasetManager.getWordDocument(this.configLoader, id, this.getApiContext().getUtilitiesService().getVisibilityRuleService(), principal);
String fileName = file.getName();
if (fileName.endsWith(".docx")){
fileName = fileName.substring(0, fileName.length() - 5);

View File

@ -1035,7 +1035,7 @@ public class DataManagementPlanManager {
eu.eudat.data.entities.DMP dmp = databaseRepository.getDmpDao().find(UUID.fromString(id));
if (!dmp.isPublic() && dmp.getUsers().stream().noneMatch(userInfo -> userInfo.getUser().getId() == principal.getId()))
throw new UnauthorisedException();
RDAExportModel rdaExportModel = new RDAExportModel().fromDataModel(dmp, datasetManager);
RDAExportModel rdaExportModel = new RDAExportModel().fromDataModel(dmp, datasetManager, principal);
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

View File

@ -14,6 +14,7 @@ import eu.eudat.data.query.items.table.dataset.DatasetTableRequest;
import eu.eudat.data.query.items.table.datasetprofile.DatasetProfileTableRequestItem;
import eu.eudat.elastic.criteria.DatasetCriteria;
import eu.eudat.elastic.repository.DatasetRepository;
import eu.eudat.exceptions.security.UnauthorisedException;
import eu.eudat.logic.builders.BuilderFactory;
import eu.eudat.logic.builders.entity.UserInfoBuilder;
import eu.eudat.logic.proxy.config.configloaders.ConfigLoader;
@ -186,9 +187,13 @@ public class DatasetManager {
return dataTable;
}
public DatasetWizardModel getSingle(String id) {
public DatasetWizardModel getSingle(String id, Principal principal) {
DatasetWizardModel dataset = new DatasetWizardModel();
eu.eudat.data.entities.Dataset datasetEntity = databaseRepository.getDatasetDao().find(UUID.fromString(id), HintedModelFactory.getHint(DatasetWizardModel.class));
if (datasetEntity.getDmp().getUsers()
.stream().filter(userInfo -> userInfo.getUser().getId() == principal.getId())
.collect(Collectors.toList()).size() == 0)
throw new UnauthorisedException();
eu.eudat.elastic.entities.Dataset datasetElastic;
try {
datasetElastic = datasetRepository.exists() ?
@ -261,11 +266,15 @@ public class DatasetManager {
return pagedDatasetProfile;
}
public File getWordDocument(ConfigLoader configLoader, String id, VisibilityRuleService visibilityRuleService) throws IOException {
public File getWordDocument(ConfigLoader configLoader, String id, VisibilityRuleService visibilityRuleService, Principal principal) throws IOException {
WordBuilder wordBuilder = new WordBuilder();
DatasetWizardModel dataset = new DatasetWizardModel();
XWPFDocument document = configLoader.getDocument();
eu.eudat.data.entities.Dataset datasetEntity = databaseRepository.getDatasetDao().find(UUID.fromString(id), HintedModelFactory.getHint(DatasetWizardModel.class));
if (!datasetEntity.getDmp().isPublic() && datasetEntity.getDmp().getUsers()
.stream().filter(userInfo -> userInfo.getUser().getId() == principal.getId())
.collect(Collectors.toList()).size() == 0)
throw new UnauthorisedException();
wordBuilder.addParagraphContent(datasetEntity.getLabel(), document, ParagraphStyle.HEADER1, BigInteger.ZERO);
// Space below Dataset title.
@ -342,10 +351,14 @@ public class DatasetManager {
return exportFile;
}
public FileEnvelope getXmlDocument(String id, VisibilityRuleService visibilityRuleService) throws InstantiationException, IllegalAccessException, IOException {
public FileEnvelope getXmlDocument(String id, VisibilityRuleService visibilityRuleService, Principal principal) throws InstantiationException, IllegalAccessException, IOException {
ExportXmlBuilder xmlBuilder = new ExportXmlBuilder();
DatasetWizardModel dataset = new DatasetWizardModel();
eu.eudat.data.entities.Dataset datasetEntity = databaseRepository.getDatasetDao().find(UUID.fromString(id), HintedModelFactory.getHint(DatasetWizardModel.class));
if (!datasetEntity.getDmp().isPublic() && datasetEntity.getDmp().getUsers()
.stream().filter(userInfo -> userInfo.getUser().getId() == principal.getId())
.collect(Collectors.toList()).size() == 0)
throw new UnauthorisedException();
Map<String, Object> properties = new HashMap<>();
if (datasetEntity.getProperties() != null) {
JSONObject jobject = new JSONObject(datasetEntity.getProperties());
@ -595,8 +608,8 @@ public class DatasetManager {
datasetDao.createOrUpdate(dataset);
}
public ResponseEntity<byte[]> getDocument(String id, VisibilityRuleService visibilityRuleService, String contentType) throws IllegalAccessException, IOException, InstantiationException {
FileEnvelope envelope = getXmlDocument(id, visibilityRuleService);
public ResponseEntity<byte[]> getDocument(String id, VisibilityRuleService visibilityRuleService, String contentType, Principal principal) throws IllegalAccessException, IOException, InstantiationException {
FileEnvelope envelope = getXmlDocument(id, visibilityRuleService, principal);
InputStream resource = new FileInputStream(envelope.getFile());
logger.info("Mime Type of " + envelope.getFilename() + " is " +
new MimetypesFileTypeMap().getContentType(envelope.getFile()));

View File

@ -7,6 +7,7 @@ import com.jayway.jsonpath.JsonPath;
import eu.eudat.data.entities.Dataset;
import eu.eudat.logic.managers.DatasetManager;
import eu.eudat.logic.utilities.builders.XmlBuilder;
import eu.eudat.models.data.security.Principal;
import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.Logger;
@ -149,7 +150,7 @@ public class DatasetRDAExportModel {
}
public DatasetRDAExportModel fromDataModel(Dataset dataset, DatasetManager datasetManager) {
public DatasetRDAExportModel fromDataModel(Dataset dataset, DatasetManager datasetManager, Principal principal) {
// Map of template Ids to rda values.
JSONObject jObject = new JSONObject(dataset.getProperties());
Map<String, Object> templateIdsToValues = jObject.toMap();
@ -165,7 +166,7 @@ public class DatasetRDAExportModel {
// Transform the answered dataset description to json so we can parse it and fill the rda model.
JSONObject datasetDescriptionJson = null;
try {
String jsonResult = mapper.writeValueAsString(datasetManager.getSingle(dataset.getId().toString()).getDatasetProfileDefinition());
String jsonResult = mapper.writeValueAsString(datasetManager.getSingle(dataset.getId().toString(), principal).getDatasetProfileDefinition());
datasetDescriptionJson = new JSONObject(jsonResult);
} catch (JsonProcessingException e) {
logger.error(e.getMessage(), e);

View File

@ -5,6 +5,7 @@ import eu.eudat.data.entities.Dataset;
import eu.eudat.data.entities.UserDMP;
import eu.eudat.data.entities.UserInfo;
import eu.eudat.logic.managers.DatasetManager;
import eu.eudat.models.data.security.Principal;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
@ -127,7 +128,7 @@ public class DmpRDAExportModel {
this.title = title;
}
public DmpRDAExportModel fromDataModel(DMP entity, DatasetManager datasetManager) {
public DmpRDAExportModel fromDataModel(DMP entity, DatasetManager datasetManager, Principal principal) {
DmpRDAExportModel dmpRda = new DmpRDAExportModel();
dmpRda.contact = new ContactRDAExportModel().fromDataModel(entity.getUsers().stream().filter(x -> x.getRole().equals(UserDMP.UserDMPRoles.OWNER.getValue())).findFirst().get().getUser());
if (entity.getUsers().stream().anyMatch(x -> x.getRole().equals(UserDMP.UserDMPRoles.USER.getValue()))) {
@ -142,7 +143,7 @@ public class DmpRDAExportModel {
dmpRda.dataset = new LinkedList<>();
for (Dataset dataset : entity.getDataset()) {
if (dataset.getStatus() != Dataset.Status.DELETED.getValue() && dataset.getStatus() != Dataset.Status.CANCELED.getValue())
dmpRda.dataset.add(new DatasetRDAExportModel().fromDataModel(dataset, datasetManager));
dmpRda.dataset.add(new DatasetRDAExportModel().fromDataModel(dataset, datasetManager, principal));
}
dmpRda.description = entity.getDescription().replace("\n", " ");
if (entity.getDoi() != null) {

View File

@ -3,6 +3,7 @@ package eu.eudat.models.data.rda;
import eu.eudat.data.entities.DMP;
import eu.eudat.data.entities.Dataset;
import eu.eudat.logic.managers.DatasetManager;
import eu.eudat.models.data.security.Principal;
import java.util.LinkedList;
import java.util.List;
@ -17,8 +18,8 @@ public class RDAExportModel {
this.dmp = dmp;
}
public RDAExportModel fromDataModel(DMP dmp, DatasetManager datasetManager) {
this.dmp = new DmpRDAExportModel().fromDataModel(dmp, datasetManager);
public RDAExportModel fromDataModel(DMP dmp, DatasetManager datasetManager, Principal principal) {
this.dmp = new DmpRDAExportModel().fromDataModel(dmp, datasetManager, principal);
return this;
}
}

View File

@ -162,8 +162,17 @@ export class DatasetWizardComponent extends BaseComponent implements OnInit, IBr
})
},
error => {
this.uiNotificationService.snackBarNotification(this.language.instant('DATASET-WIZARD.MESSAGES.DATASET-NOT-FOUND'), SnackBarNotificationLevel.Error);
this.router.navigate(['/datasets/edit/' + this.itemId]);
switch (error.status) {
case 403:
this.uiNotificationService.snackBarNotification(this.language.instant('DATASET-WIZARD.MESSAGES.DATASET-NOT-ALLOWED'), SnackBarNotificationLevel.Error);
break;
case 404:
this.uiNotificationService.snackBarNotification(this.language.instant('DATASET-WIZARD.MESSAGES.DATASET-NOT-FOUND'), SnackBarNotificationLevel.Error);
break;
default:
this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.ERRORS.HTTP-REQUEST-ERROR'), SnackBarNotificationLevel.Error);
}
this.router.navigate(['/datasets/']);
return observableOf(null);
});
} else if (dmpId != null) {

View File

@ -74,6 +74,9 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit {
if (error.status === 404) {
return this.onFetchingDeletedCallbackError('/plans/');
}
if (error.status === 403) {
return this.onFetchingForbiddenCallbackError('/plans/');
}
});
}
else if (publicId != null) {
@ -93,6 +96,9 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit {
if (error.status === 404) {
return this.onFetchingDeletedCallbackError('/plans/');
}
if (error.status === 403) {
return this.onFetchingForbiddenCallbackError('/plans/');
}
});
}
});
@ -103,6 +109,11 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit {
this.router.navigate([redirectRoot]);
}
onFetchingForbiddenCallbackError(redirectRoot: string) {
this.uiNotificationService.snackBarNotification(this.language.instant('DMP-OVERVIEW.ERROR.FORBIDEN-DMP'), SnackBarNotificationLevel.Error);
this.router.navigate([redirectRoot]);
}
setIsUserOwner() {
if (this.dmp) {
const principal: Principal = this.authentication.current();

View File

@ -491,6 +491,7 @@
},
"MESSAGES": {
"DATASET-NOT-FOUND": "Dataset Description does not exist",
"DATASET-NOT-ALLOWED": "You have no access to this Dataset Description",
"SUCCESS-UPDATE-DATASET-PROFILE": "Dataset Description Template updated successfully"
},
"UPLOAD": {
@ -519,7 +520,8 @@
"TEMPLATES-INVOLVED": "Dataset Description Templates Involved"
},
"ERROR": {
"DELETED-DMP": "The requested DMP is deleted"
"DELETED-DMP": "The requested DMP is deleted",
"FORBIDEN-DMP": "You are not allowed to access this DMP"
}
},
"DATASET-LISTING": {

View File

@ -489,6 +489,7 @@
},
"MESSAGES": {
"DATASET-NOT-FOUND": "No existe la descripción del Dataset",
"DATASET-NOT-ALLOWED": "You have no access to this Dataset Description",
"SUCCESS-UPDATE-DATASET-PROFILE": "Plantilla de descripción del Dataset actualizada correctamente"
},
"UPLOAD": {
@ -517,7 +518,8 @@
"TEMPLATES-INVOLVED": "Plantillas de descripción del Dataset implicadas"
},
"ERROR": {
"DELETED-DMP": "El PGD solicitado está borrado"
"DELETED-DMP": "El PGD solicitado está borrado",
"FORBIDEN-DMP": "You are not allowed to access this DMP"
}
},
"DATASET-LISTING": {