/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package edu.harvard.iq.dataverse.datasetutility;
import edu.harvard.iq.dataverse.DataFile;
import edu.harvard.iq.dataverse.Dataset;
import edu.harvard.iq.dataverse.DataverseSession;
import edu.harvard.iq.dataverse.DvObject;
import edu.harvard.iq.dataverse.FileMetadata;
import edu.harvard.iq.dataverse.MapLayerMetadata;
import edu.harvard.iq.dataverse.MapLayerMetadataServiceBean;
import edu.harvard.iq.dataverse.PermissionServiceBean;
import edu.harvard.iq.dataverse.authorization.Permission;
import edu.harvard.iq.dataverse.authorization.users.GuestUser;
import edu.harvard.iq.dataverse.authorization.users.User;
import edu.harvard.iq.dataverse.settings.SettingsServiceBean;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.faces.view.ViewScoped;
import javax.inject.Inject;
import javax.inject.Named;
/**
* This class originally encapsulated display logic for the DatasetPage
*
* It allows the following checks without redundantly querying the db to
* check permissions or if MapLayerMetadata exists
*
* - canUserSeeMapDataButton (private)
* - canUserSeeMapDataButtonFromPage (public)
* - canUserSeeMapDataButtonFromAPI (public)
*
* - canSeeMapButtonReminderToPublish (private)
* - canSeeMapButtonReminderToPublishFromPage (public)
* - canSeeMapButtonReminderToPublishFromAPI (public)
*
* - canUserSeeExploreWorldMapButton (private)
* - canUserSeeExploreWorldMapButtonFromPage (public)
* - canUserSeeExploreWorldMapButtonFromAPI (public)
*
* @author rmp553
*/
@ViewScoped
@Named
public class WorldMapPermissionHelper implements java.io.Serializable {
@Inject SettingsServiceBean settingsService;
@Inject MapLayerMetadataServiceBean mapLayerMetadataService;
@Inject PermissionServiceBean permissionService;
@Inject DataverseSession session;
private final Map<Long, Boolean> fileMetadataWorldMapExplore = new HashMap<>(); // { FileMetadata.id : Boolean }
private Map<Long, MapLayerMetadata> mapLayerMetadataLookup = null;
private final Map<String, Boolean> datasetPermissionMap = new HashMap<>(); // { Permission human_name : Boolean }
public WorldMapPermissionHelper( ){
}
/**
* Using a DataFile id, retrieve an associated MapLayerMetadata object
*
* The MapLayerMetadata objects have been fetched at page inception by
* "loadMapLayerMetadataLookup()"
*/
public MapLayerMetadata getMapLayerMetadata(DataFile df) {
if (df == null) {
return null;
}
if (mapLayerMetadataLookup == null){
loadMapLayerMetadataLookup(df.getOwner());
}
return this.mapLayerMetadataLookup.get(df.getId());
}
/*
* Call this when using the API
* - calls private method canUserSeeExploreWorldMapButton
*/
public boolean canUserSeeExploreWorldMapButtonFromAPI(FileMetadata fm, User user){
if (fm == null){
return false;
}
if (user==null){
return false;
}
if (!this.permissionService.userOn(user, fm.getDataFile()).has(Permission.DownloadFile)){
return false;
}
return this.canUserSeeExploreWorldMapButton(fm, true);
}
/**
* Call this from a Dataset or File page
* - calls private method canUserSeeExploreWorldMapButton
*
* WARNING: Before calling this, make sure the user has download
* permission for the file!! (See DatasetPage.canDownloadFile())
*
* @param FileMetadata fm
* @return boolean
*/
public boolean canUserSeeExploreWorldMapButtonFromPage(FileMetadata fm){
if (fm==null){
return false;
}
return this.canUserSeeExploreWorldMapButton(fm, true);
}
/**
* WARNING: Before calling this, make sure the user has download
* permission for the file!! (See DatasetPage.canDownloadFile())
*
* Should there be a Explore WorldMap Button for this file?
* See table in: https://github.com/IQSS/dataverse/issues/1618
*
* (1) Does the file have MapLayerMetadata?
* (2) Are the proper settings in place
*
* @param fm FileMetadata
* @return boolean
*/
private boolean canUserSeeExploreWorldMapButton(FileMetadata fm, boolean permissionsChecked){
if (fm==null){
return false;
}
// This is only here to make the public method users think...
if (!permissionsChecked){
return false;
}
if (this.fileMetadataWorldMapExplore.containsKey(fm.getId())){
// Yes, return previous answer
//logger.info("using cached result for candownloadfile on filemetadata "+fid);
return this.fileMetadataWorldMapExplore.get(fm.getId());
}
/* -----------------------------------------------------
Does a Map Exist?
----------------------------------------------------- */
if (!(this.hasMapLayerMetadata(fm))) {
//See if it does
MapLayerMetadata layer_metadata = mapLayerMetadataService.findMetadataByDatafile(fm.getDataFile());
if (layer_metadata != null) {
if (mapLayerMetadataLookup == null) {
loadMapLayerMetadataLookup(fm.getDataFile().getOwner());
}
// yes: keep going...
mapLayerMetadataLookup.put(layer_metadata.getDataFile().getId(), layer_metadata);
} else {
// Nope: no button
this.fileMetadataWorldMapExplore.put(fm.getId(), false);
return false;
}
}
/*
Is setting for GeoconnectViewMaps true?
Nope? no button
*/
if (!settingsService.isTrueForKey(SettingsServiceBean.Key.GeoconnectViewMaps, false)){
this.fileMetadataWorldMapExplore.put(fm.getId(), false);
return false;
}
//----------------------------------------------------------------------
//(0) Before we give it to you - if version is deaccessioned and user
// does not have edit dataset permission then may download
//----------------------------------------------------------------------
if (fm.getDatasetVersion().isDeaccessioned()) {
if (this.doesSessionUserHavePermission( Permission.EditDataset, fm)) {
// Yes, save answer and return true
this.fileMetadataWorldMapExplore.put(fm.getId(), true);
return true;
} else {
this.fileMetadataWorldMapExplore.put(fm.getId(), false);
return false;
}
}
//Check for restrictions
boolean isRestrictedFile = fm.isRestricted();
// --------------------------------------------------------------------
// Is the file Unrestricted ?
// --------------------------------------------------------------------
if (!isRestrictedFile){
// Yes, save answer and return true
this.fileMetadataWorldMapExplore.put(fm.getId(), true);
return true;
}
// --------------------------------------------------------------------
// Conditions (2) through (4) are for Restricted files
// --------------------------------------------------------------------
if (session.getUser() instanceof GuestUser){
this.fileMetadataWorldMapExplore.put(fm.getId(), false);
return false;
}
// --------------------------------------------------------------------
// (3) Does the User have DownloadFile Permission at the **Dataset** level
// --------------------------------------------------------------------
if (!this.doesSessionUserHavePermission(Permission.DownloadFile, fm)){
// Yes, save answer and return true
this.fileMetadataWorldMapExplore.put(fm.getId(), false);
return false;
}
/* -----------------------------------------------------
Yes: User can view button!
----------------------------------------------------- */
this.fileMetadataWorldMapExplore.put(fm.getId(), true);
return true;
}
/*
Check if the FileMetadata.dataFile has an associated MapLayerMetadata object
The MapLayerMetadata objects have been fetched at page inception by "loadMapLayerMetadataLookup()"
*/
public boolean hasMapLayerMetadata(FileMetadata fm) {
if (fm == null) {
return false;
}
if (fm.getDataFile() == null) {
return false;
}
if (mapLayerMetadataLookup == null) {
loadMapLayerMetadataLookup(fm.getDataFile().getOwner());
}
return doesDataFileHaveMapLayerMetadata(fm.getDataFile());
}
/**
* Check if a DataFile has an associated MapLayerMetadata object
*
* The MapLayerMetadata objects have been fetched at page inception by
* "loadMapLayerMetadataLookup()"
*/
private boolean doesDataFileHaveMapLayerMetadata(DataFile df) {
if (df == null) {
return false;
}
if (df.getId() == null) {
return false;
}
return this.mapLayerMetadataLookup.containsKey(df.getId());
}
/**
* Create a hashmap consisting of { DataFile.id : MapLayerMetadata object}
*
* Very few DataFiles will have associated MapLayerMetadata objects so only
* use 1 query to get them
*/
private void loadMapLayerMetadataLookup(Dataset dataset) {
mapLayerMetadataLookup = new HashMap<>();
if (dataset == null) {
}
if (dataset.getId() == null) {
return;
}
List<MapLayerMetadata> mapLayerMetadataList = mapLayerMetadataService.getMapLayerMetadataForDataset(dataset);
if (mapLayerMetadataList == null) {
return;
}
for (MapLayerMetadata layer_metadata : mapLayerMetadataList) {
mapLayerMetadataLookup.put(layer_metadata.getDataFile().getId(), layer_metadata);
}
}// A DataFile may have a related MapLayerMetadata object
/**
* Check if this is a mappable file type.
*
* Currently (2/2016)
* - Shapefile (zipped shapefile)
* - Tabular file with Geospatial Data tag
*
* @param fm
* @return
*/
private boolean isPotentiallyMappableFileType(FileMetadata fm){
if (fm==null){
return false;
}
// Yes, it's a shapefile
//
if (this.isShapefileType(fm)){
return true;
}
// Yes, it's tabular with a geospatial tag
//
if (fm.getDataFile().isTabularData()){
if (fm.getDataFile().hasGeospatialTag()){
return true;
}
}
return false;
}
public boolean isShapefileType(FileMetadata fm) {
if (fm == null) {
return false;
}
if (fm.getDataFile() == null) {
return false;
}
return fm.getDataFile().isShapefileType();
}
/**
* Call this from a Dataset or File page
* - calls private method canSeeMapButtonReminderToPublish
*
* WARNING: Assumes user isAuthenicated AND has Permission.EditDataset
* - These checks should be made on the DatasetPage or FilePage which calls this method
*
*
* @param FileMetadata fm
* @return boolean
*/
public boolean canSeeMapButtonReminderToPublishFromPage(FileMetadata fm){
if (fm == null){
return false;
}
if (mapLayerMetadataLookup == null){
loadMapLayerMetadataLookup(fm.getDatasetVersion().getDataset());
}
return this.canSeeMapButtonReminderToPublish(fm, true);
}
/**
* Call this when using the API
* - calls private method canSeeMapButtonReminderToPublish
*
* @param fm
* @param user
* @return
*/
public boolean canSeeMapButtonReminderToPublishFromAPI(FileMetadata fm, User user){
if (fm == null){
return false;
}
if (user==null){
return false;
}
if (!this.permissionService.userOn(user, fm.getDataFile().getOwner()).has(Permission.EditDataset)){
return false;
}
return this.canSeeMapButtonReminderToPublish(fm, true);
}
/**
* Assumes permissions have been checked!!
*
* See table in: https://github.com/IQSS/dataverse/issues/1618
*
* Can the user see a reminder to publish button?
* (1) Is the view GeoconnectViewMaps
* (2) Is this file a Shapefile or a Tabular file tagged as Geospatial?
* (3) Is this DataFile released? Yes, don't need reminder
* (4) Does a map already exist? Yes, don't need reminder
*/
private boolean canSeeMapButtonReminderToPublish(FileMetadata fm, boolean permissionsChecked){
if (fm==null){
return false;
}
// Is this user authenticated with EditDataset permission?
//
if (!(isUserAuthenticatedWithEditDatasetPermission(fm))){
return false;
}
// This is only here as a reminder to the public method users
if (!permissionsChecked){
return false;
}
// (1) Is the view GeoconnectViewMaps
if (!settingsService.isTrueForKey(SettingsServiceBean.Key.GeoconnectCreateEditMaps, false)){
return false;
}
// (2) Is this file a Shapefile or a Tabular file tagged as Geospatial?
//
if (!(this.isPotentiallyMappableFileType(fm))){
return false;
}
// (3) Is this DataFile released? Yes, don't need reminder
//
if (fm.getDataFile().isReleased()){
return false;
}
// (4) Does a map already exist? Yes, don't need reminder
//
if (this.hasMapLayerMetadata(fm)){
return false;
}
// Looks good
//
return true;
}
/**
*
* WARNING: Assumes user isAuthenicated AND has Permission.EditDataset
* - These checks are made on the DatasetPage which calls this method
*
*/
public boolean canUserSeeMapDataButtonFromPage(FileMetadata fm){
if (fm==null){
return false;
}
// Is this user authenticated with EditDataset permission?
//
if (!(isUserAuthenticatedWithEditDatasetPermission(fm))){
return false;
}
if (mapLayerMetadataLookup == null){
loadMapLayerMetadataLookup(fm.getDatasetVersion().getDataset());
}
if (this.hasMapLayerMetadata(fm)){
return false;
}
return this.canUserSeeMapDataButton(fm, true);
}
/**
* Call this when using the API
* - calls private method canUserSeeMapDataButton
*
* @param fm
* @param user
* @return
*/
public boolean canUserSeeMapDataButtonFromAPI(FileMetadata fm, User user){
if (fm == null){
return false;
}
if (user==null){
return false;
}
if (!this.permissionService.userOn(user, fm.getDataFile().getOwner()).has(Permission.EditDataset)){
return false;
}
return this.canUserSeeMapDataButton(fm, true);
}
/**
*
* WARNING: Assumes user isAuthenicated AND has Permission.EditDataset
* - These checks are made on the DatasetPage which calls this method
*
* Should there be a Map Data Button for this file?
* see table in: https://github.com/IQSS/dataverse/issues/1618
* (1) Is the user logged in?
* (2) Is this file a Shapefile or a Tabular file tagged as Geospatial?
* (3) Does the logged in user have permission to edit the Dataset to which this FileMetadata belongs?
* (4) Is the create Edit Maps flag set to true?
* (5) Any of these conditions:
* 9a) File Published
* (b) Draft: File Previously published
* @param fm FileMetadata
* @return boolean
*/
private boolean canUserSeeMapDataButton(FileMetadata fm, boolean permissionsChecked){
if (fm==null){
return false;
}
// This is only here as a reminder to the public method users
if (!permissionsChecked){
return false;
}
// (1) Is this file a Shapefile or a Tabular file tagged as Geospatial?
// TO DO: EXPAND FOR TABULAR FILES TAGGED AS GEOSPATIAL!
//
if (!(this.isPotentiallyMappableFileType(fm))){
return false;
}
// (2) Is the view GeoconnectViewMaps
if (!settingsService.isTrueForKey(SettingsServiceBean.Key.GeoconnectCreateEditMaps, false)){
return false;
}
// (3) Is File released?
//
if (fm.getDataFile().isReleased()){
return true;
}
// Nope
return false;
}
private boolean isUserAuthenticatedWithEditDatasetPermission( FileMetadata fm){
// Is the user authenticated?
//
if (!(isSessionUserAuthenticated())){
return false;
}
// If so, can the logged in user edit the Dataset to which this FileMetadata belongs?
//
if (!this.doesSessionUserHavePermission(Permission.EditDataset, fm)){
return false;
}
return true;
}
public boolean isSessionUserAuthenticated() {
if (session == null) {
return false;
}
if (session.getUser() == null) {
return false;
}
return session.getUser().isAuthenticated();
}
private boolean doesSessionUserHavePermission(Permission permissionToCheck, FileMetadata fileMetadata){
if (permissionToCheck == null){
return false;
}
DvObject objectToCheck = null;
if (permissionToCheck.equals(Permission.EditDataset)){
objectToCheck = fileMetadata.getDatasetVersion().getDataset();
} else if (permissionToCheck.equals(Permission.DownloadFile)){
objectToCheck = fileMetadata.getDataFile();
}
if (objectToCheck == null){
return false;
}
if (this.session.getUser() == null){
return false;
}
if (this.permissionService == null){
return false;
}
String permName = permissionToCheck.getHumanName();
// Has this check already been done?
//
if (this.datasetPermissionMap.containsKey(permName)){
// Yes, return previous answer
return this.datasetPermissionMap.get(permName);
}
// Check the permission
//
boolean hasPermission = this.permissionService.userOn(this.session.getUser(), objectToCheck).has(permissionToCheck);
// Save the permission
this.datasetPermissionMap.put(permName, hasPermission);
// return true/false
return hasPermission;
}
}