package edu.harvard.iq.dataverse;
import edu.harvard.iq.dataverse.authorization.AuthenticationServiceBean;
import edu.harvard.iq.dataverse.authorization.DataverseRole;
import edu.harvard.iq.dataverse.authorization.DataverseRolePermissionHelper;
import edu.harvard.iq.dataverse.authorization.Permission;
import edu.harvard.iq.dataverse.authorization.RoleAssignee;
import edu.harvard.iq.dataverse.authorization.RoleAssigneeDisplayInfo;
import edu.harvard.iq.dataverse.authorization.groups.GroupServiceBean;
import edu.harvard.iq.dataverse.authorization.groups.impl.builtin.AuthenticatedUsers;
import edu.harvard.iq.dataverse.authorization.groups.impl.explicit.ExplicitGroup;
import edu.harvard.iq.dataverse.authorization.groups.impl.explicit.ExplicitGroupServiceBean;
import edu.harvard.iq.dataverse.authorization.users.AuthenticatedUser;
import edu.harvard.iq.dataverse.engine.command.exception.CommandException;
import edu.harvard.iq.dataverse.engine.command.exception.PermissionException;
import edu.harvard.iq.dataverse.engine.command.impl.AssignRoleCommand;
import edu.harvard.iq.dataverse.engine.command.impl.CreateRoleCommand;
import edu.harvard.iq.dataverse.engine.command.impl.RevokeRoleCommand;
import edu.harvard.iq.dataverse.engine.command.impl.UpdateDataverseDefaultContributorRoleCommand;
import edu.harvard.iq.dataverse.util.JsfHelper;
import static edu.harvard.iq.dataverse.util.JsfHelper.JH;
import edu.harvard.iq.dataverse.util.StringUtil;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.EJB;
import javax.faces.application.FacesMessage;
import javax.faces.event.ActionEvent;
import javax.faces.view.ViewScoped;
import javax.inject.Inject;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.apache.commons.lang.StringEscapeUtils;
/**
*
* @author gdurand
*/
@ViewScoped
@Named
public class ManagePermissionsPage implements java.io.Serializable {
private static final Logger logger = Logger.getLogger(ManagePermissionsPage.class.getCanonicalName());
@EJB
DvObjectServiceBean dvObjectService;
@EJB
DataverseRoleServiceBean roleService;
@EJB
RoleAssigneeServiceBean roleAssigneeService;
@EJB
PermissionServiceBean permissionService;
@EJB
AuthenticationServiceBean authenticationService;
@EJB
ExplicitGroupServiceBean explicitGroupService;
@EJB
GroupServiceBean groupService;
@EJB
EjbDataverseEngine commandEngine;
@EJB
UserNotificationServiceBean userNotificationService;
@Inject
DataverseRequestServiceBean dvRequestService;
@Inject
PermissionsWrapper permissionsWrapper;
@PersistenceContext(unitName = "VDCNet-ejbPU")
EntityManager em;
@Inject
DataverseSession session;
private DataverseRolePermissionHelper dataverseRolePermissionHelper;
private List<DataverseRole> roleList;
DvObject dvObject = new Dataverse(); // by default we use a Dataverse, but this will be overridden in init by the findById
public DvObject getDvObject() {
return dvObject;
}
public void setDvObject(DvObject dvObject) {
this.dvObject = dvObject;
/*
SEK 09/15/2016 - may need to do something here if permissions are transmitted/inherited from dataverse to dataverse
*/
/*if (dvObject instanceof DvObjectContainer) {
inheritAssignments = !((DvObjectContainer) dvObject).isPermissionRoot();
}*/
}
public String init() {
//@todo deal with any kind of dvObject
if (dvObject.getId() != null) {
dvObject = dvObjectService.findDvObject(dvObject.getId());
}
// check if dvObject exists and user has permission
if (dvObject == null) {
return permissionsWrapper.notFound();
}
// for dataFiles, check the perms on its owning dataset
DvObject checkPermissionsdvObject = dvObject instanceof DataFile ? dvObject.getOwner() : dvObject;
if (!permissionService.on(checkPermissionsdvObject).has(checkPermissionsdvObject instanceof Dataverse ? Permission.ManageDataversePermissions : Permission.ManageDatasetPermissions)) {
return permissionsWrapper.notAuthorized();
}
// initialize the configure settings
if (dvObject instanceof Dataverse) {
initAccessSettings();
}
roleList = roleService.findAll();
roleAssignments = initRoleAssignments();
dataverseRolePermissionHelper = new DataverseRolePermissionHelper(roleList);
return "";
}
/*
main page - role assignment table
*/
// used by remove Role Assignment
private RoleAssignment selectedRoleAssignment;
public RoleAssignment getSelectedRoleAssignment() {
return selectedRoleAssignment;
}
public void setSelectedRoleAssignment(RoleAssignment selectedRoleAssignment) {
this.selectedRoleAssignment = selectedRoleAssignment;
}
private List<RoleAssignmentRow> roleAssignments;
public List<RoleAssignmentRow> getRoleAssignments() {
return roleAssignments;
}
public void setRoleAssignments(List<RoleAssignmentRow> roleAssignments) {
this.roleAssignments = roleAssignments;
}
public List<RoleAssignmentRow> initRoleAssignments() {
List<RoleAssignmentRow> raList = null;
if (dvObject != null && dvObject.getId() != null) {
Set<RoleAssignment> ras = roleService.rolesAssignments(dvObject);
raList = new ArrayList<>(ras.size());
for (RoleAssignment roleAssignment : ras) {
// for files, only show role assignments which can download
if (!(dvObject instanceof DataFile) || roleAssignment.getRole().permissions().contains(Permission.DownloadFile)) {
RoleAssignee roleAssignee = roleAssigneeService.getRoleAssignee(roleAssignment.getAssigneeIdentifier());
if (roleAssignee != null) {
raList.add(new RoleAssignmentRow(roleAssignment, roleAssignee.getDisplayInfo()));
} else {
logger.info("Could not find role assignee based on role assignment id " + roleAssignment.getId());
}
}
}
}
return raList;
}
public void removeRoleAssignment() {
revokeRole(selectedRoleAssignment);
if (dvObject instanceof Dataverse) {
initAccessSettings(); // in case the revoke was for the AuthenticatedUsers group
}
roleAssignments = initRoleAssignments();
showAssignmentMessages();
}
// internal method used by removeRoleAssignment and saveConfiguration
private void revokeRole(RoleAssignment ra) {
try {
commandEngine.submit(new RevokeRoleCommand(ra, dvRequestService.getDataverseRequest()));
JsfHelper.addSuccessMessage(ra.getRole().getName() + " role for " + roleAssigneeService.getRoleAssignee(ra.getAssigneeIdentifier()).getDisplayInfo().getTitle() + " was removed.");
RoleAssignee assignee = roleAssigneeService.getRoleAssignee(ra.getAssigneeIdentifier());
notifyRoleChange(assignee, UserNotification.Type.REVOKEROLE);
} catch (PermissionException ex) {
JH.addMessage(FacesMessage.SEVERITY_ERROR, "The role assignment was not able to be removed.", "Permissions " + ex.getRequiredPermissions().toString() + " missing.");
} catch (CommandException ex) {
JH.addMessage(FacesMessage.SEVERITY_FATAL, "The role assignment could not be removed.");
logger.log(Level.SEVERE, "Error removing role assignment: " + ex.getMessage(), ex);
}
}
/*
main page - roles table
*/
public List<DataverseRole> getRoles() {
if (dvObject != null && dvObject.getId() != null) {
return roleService.findByOwnerId(dvObject.getId());
}
return new ArrayList<>();
}
public void createNewRole(ActionEvent e) {
setRole(new DataverseRole());
role.setOwner(dvObject);
}
public void cloneRole(String roleId) {
DataverseRole clonedRole = new DataverseRole();
clonedRole.setOwner(dvObject);
DataverseRole originalRole = roleService.find(Long.parseLong(roleId));
clonedRole.addPermissions(originalRole.permissions());
setRole(clonedRole);
}
public void editRole(String roleId) {
setRole(roleService.find(Long.parseLong(roleId)));
}
/*
============================================================================
edit configuration dialog // only for dataverse version of page
============================================================================
*/
private String authenticatedUsersContributorRoleAlias = null;
private String defaultContributorRoleAlias = DataverseRole.EDITOR;
public String getAuthenticatedUsersContributorRoleAlias() {
return authenticatedUsersContributorRoleAlias;
}
public void setAuthenticatedUsersContributorRoleAlias(String authenticatedUsersContributorRoleAlias) {
this.authenticatedUsersContributorRoleAlias = authenticatedUsersContributorRoleAlias;
}
public String getDefaultContributorRoleAlias() {
return defaultContributorRoleAlias;
}
public void setDefaultContributorRoleAlias(String defaultContributorRoleAlias) {
this.defaultContributorRoleAlias = defaultContributorRoleAlias;
}
public void initAccessSettings() {
if (dvObject instanceof Dataverse) {
authenticatedUsersContributorRoleAlias = "";
List<RoleAssignment> aUsersRoleAssignments = roleService.directRoleAssignments(AuthenticatedUsers.get(), dvObject);
for (RoleAssignment roleAssignment : aUsersRoleAssignments) {
String roleAlias = roleAssignment.getRole().getAlias();
authenticatedUsersContributorRoleAlias = roleAlias;
break;
// @todo handle case where more than one role has been assigned to the AutenticatedUsers group!
}
defaultContributorRoleAlias = ((Dataverse) dvObject).getDefaultContributorRole().getAlias();
}
}
public void saveConfiguration(ActionEvent e) {
// Set role (if any) for authenticatedUsers
DataverseRole roleToAssign = null;
List<String> contributorRoles = Arrays.asList(DataverseRole.FULL_CONTRIBUTOR, DataverseRole.DV_CONTRIBUTOR, DataverseRole.DS_CONTRIBUTOR);
if (!StringUtil.isEmpty(authenticatedUsersContributorRoleAlias)) {
roleToAssign = roleService.findBuiltinRoleByAlias(authenticatedUsersContributorRoleAlias);
}
// then, check current contributor role
List<RoleAssignment> aUsersRoleAssignments = roleService.directRoleAssignments(AuthenticatedUsers.get(), dvObject);
for (RoleAssignment roleAssignment : aUsersRoleAssignments) {
DataverseRole currentRole = roleAssignment.getRole();
if (contributorRoles.contains(currentRole.getAlias())) {
if (currentRole.equals(roleToAssign)) {
roleToAssign = null; // found the role, so no need to assign
} else {
revokeRole(roleAssignment);
}
}
}
// finally, assign role, if new
if (roleToAssign != null) {
assignRole(AuthenticatedUsers.get(), roleToAssign);
}
// set dataverse default contributor role
if (dvObject instanceof Dataverse) {
Dataverse dv = (Dataverse) dvObject;
DataverseRole defaultRole = roleService.findBuiltinRoleByAlias(defaultContributorRoleAlias);
if (!defaultRole.equals(dv.getDefaultContributorRole())) {
try {
commandEngine.submit(new UpdateDataverseDefaultContributorRoleCommand(defaultRole, dvRequestService.getDataverseRequest(), dv));
JsfHelper.addSuccessMessage("The default permissions for this dataverse have been updated.");
} catch (PermissionException ex) {
JH.addMessage(FacesMessage.SEVERITY_ERROR, "Cannot assign default permissions.", "Permissions " + ex.getRequiredPermissions().toString() + " missing.");
} catch (CommandException ex) {
JH.addMessage(FacesMessage.SEVERITY_FATAL, "Cannot assign default permissions.");
logger.log(Level.SEVERE, "Error assigning default permissions: " + ex.getMessage(), ex);
}
}
}
roleAssignments = initRoleAssignments();
showConfigureMessages();
}
/*
============================================================================
assign roles dialog
============================================================================
*/
private List<RoleAssignee> roleAssignSelectedRoleAssignees;
private Long selectedRoleId;
public List<RoleAssignee> getRoleAssignSelectedRoleAssignees() {
return roleAssignSelectedRoleAssignees;
}
public void setRoleAssignSelectedRoleAssignees(List<RoleAssignee> selectedRoleAssignees) {
this.roleAssignSelectedRoleAssignees = selectedRoleAssignees;
}
public Long getSelectedRoleId() {
return selectedRoleId;
}
public void setSelectedRoleId(Long selectedRoleId) {
this.selectedRoleId = selectedRoleId;
}
public void initAssigneeDialog(ActionEvent ae) {
roleAssignSelectedRoleAssignees = new LinkedList<>();
selectedRoleId = null;
showNoMessages();
}
public List<RoleAssignee> completeRoleAssignee( String query ) {
return roleAssigneeService.filterRoleAssignees(query, dvObject, roleAssignSelectedRoleAssignees);
}
public List<DataverseRole> getAvailableRoles() {
List<DataverseRole> roles = new LinkedList<>();
if (dvObject != null && dvObject.getId() != null) {
if (dvObject instanceof Dataverse) {
roles.addAll(roleService.availableRoles(dvObject.getId()));
} else if (dvObject instanceof Dataset) {
// don't show roles that only have Dataverse level permissions
// current the available roles for a dataset are gotten from its parent
for (DataverseRole role : roleService.availableRoles(dvObject.getOwner().getId())) {
for (Permission permission : role.permissions()) {
if (permission.appliesTo(Dataset.class) || permission.appliesTo(DataFile.class)) {
roles.add(role);
break;
}
}
}
} else if (dvObject instanceof DataFile) {
roles.add(roleService.findBuiltinRoleByAlias(DataverseRole.FILE_DOWNLOADER));
}
Collections.sort(roles, DataverseRole.CMP_BY_NAME);
}
return roles;
}
public DataverseRole getAssignedRole() {
if (selectedRoleId != null) {
return roleService.find(selectedRoleId);
}
return null;
}
public String getAssignedRoleObjectTypes(){
String retString = "";
if (selectedRoleId != null) {
/* SEK 09/15/2016 SEK commenting out for now
because permissions are not inherited
if (dataverseRolePermissionHelper.hasDataversePermissions(selectedRoleId) && dvObject instanceof Dataverse){
String dvLabel = ResourceBundle.getBundle("Bundle").getString("dataverses");
retString = dvLabel;
}
*/
if (dataverseRolePermissionHelper.hasDatasetPermissions(selectedRoleId) && dvObject instanceof Dataverse){
String dsLabel = ResourceBundle.getBundle("Bundle").getString("datasets");
if(!retString.isEmpty()) {
retString +=", " + dsLabel;
} else {
retString = dsLabel;
}
}
if (dataverseRolePermissionHelper.hasFilePermissions(selectedRoleId)){
String filesLabel = ResourceBundle.getBundle("Bundle").getString("files");
if(!retString.isEmpty()) {
retString +=", " + filesLabel;
} else {
retString = filesLabel;
}
}
return retString;
}
return null;
}
public String getDefinitionLevelString(){
if (dvObject != null){
if (dvObject instanceof Dataverse) return ResourceBundle.getBundle("Bundle").getString("dataverse");
if (dvObject instanceof Dataset) return ResourceBundle.getBundle("Bundle").getString("dataset");
}
return null;
}
public void assignRole(ActionEvent evt) {
logger.info("Got to assignRole");
List<RoleAssignee> selectedRoleAssigneesList = getRoleAssignSelectedRoleAssignees();
if ( selectedRoleAssigneesList == null ) {
logger.info("** SELECTED role asignees is null");
selectedRoleAssigneesList = new LinkedList<>();
}
for (RoleAssignee roleAssignee : selectedRoleAssigneesList) {
assignRole(roleAssignee, roleService.find(selectedRoleId));
}
roleAssignments = initRoleAssignments();
}
/**
* Notify a {@code RoleAssignee} that a role was either assigned or revoked.
* Will notify all members of a group.
* @param ra The {@code RoleAssignee} to be notified.
* @param type The type of notification.
*/
private void notifyRoleChange(RoleAssignee ra, UserNotification.Type type) {
if (ra instanceof AuthenticatedUser) {
userNotificationService.sendNotification((AuthenticatedUser) ra, new Timestamp(new Date().getTime()), type, dvObject.getId());
} else if (ra instanceof ExplicitGroup) {
ExplicitGroup eg = (ExplicitGroup) ra;
Set<String> explicitGroupMembers = eg.getContainedRoleAssgineeIdentifiers();
for (String id : explicitGroupMembers) {
RoleAssignee explicitGroupMember = roleAssigneeService.getRoleAssignee(id);
if (explicitGroupMember instanceof AuthenticatedUser) {
userNotificationService.sendNotification((AuthenticatedUser) explicitGroupMember, new Timestamp(new Date().getTime()), type, dvObject.getId());
}
}
}
}
private void assignRole(RoleAssignee ra, DataverseRole r) {
try {
String privateUrlToken = null;
commandEngine.submit(new AssignRoleCommand(ra, r, dvObject, dvRequestService.getDataverseRequest(), privateUrlToken));
JsfHelper.addSuccessMessage(r.getName() + " role assigned to " + ra.getDisplayInfo().getTitle() + " for " + StringEscapeUtils.escapeHtml(dvObject.getDisplayName()) + ".");
// don't notify if role = file downloader and object is not released
if (!(r.getAlias().equals(DataverseRole.FILE_DOWNLOADER) && !dvObject.isReleased()) ){
notifyRoleChange(ra, UserNotification.Type.ASSIGNROLE);
}
} catch (PermissionException ex) {
JH.addMessage(FacesMessage.SEVERITY_ERROR, "The role was not able to be assigned.", "Permissions " + ex.getRequiredPermissions().toString() + " missing.");
} catch (CommandException ex) {
String message = r.getName() + " role could NOT be assigned to " + ra.getDisplayInfo().getTitle() + " for " + StringEscapeUtils.escapeHtml(dvObject.getDisplayName()) + ".";
JsfHelper.addErrorMessage(message);
//JH.addMessage(FacesMessage.SEVERITY_FATAL, "The role was not able to be assigned.");
logger.log(Level.SEVERE, "Error assiging role: " + ex.getMessage(), ex);
}
showAssignmentMessages();
}
/*
============================================================================
edit role dialog
============================================================================
*/
private DataverseRole role = new DataverseRole();
private List<String> selectedPermissions;
public DataverseRole getRole() {
return role;
}
public void setRole(DataverseRole role) {
this.role = role;
selectedPermissions = new LinkedList<>();
if (role != null) {
for (Permission p : role.permissions()) {
selectedPermissions.add(p.name());
}
}
}
public List<String> getSelectedPermissions() {
return selectedPermissions;
}
public void setSelectedPermissions(List<String> selectedPermissions) {
this.selectedPermissions = selectedPermissions;
}
public List<Permission> getPermissions() {
return Arrays.asList(Permission.values());
}
public void updateRole(ActionEvent e) {
// @todo currently only works for Dataverse since CreateRoleCommand only takes a dataverse
// we need to decide if we want roles at the dataset level or not
if (dvObject instanceof Dataverse) {
role.clearPermissions();
for (String pmsnStr : getSelectedPermissions()) {
role.addPermission(Permission.valueOf(pmsnStr));
}
try {
String roleState = role.getId() != null ? "updated" : "created";
setRole(commandEngine.submit(new CreateRoleCommand(role, dvRequestService.getDataverseRequest(), (Dataverse) role.getOwner())));
JsfHelper.addSuccessMessage("The role was " + roleState + ". To assign it to a user and/or group, click on the Assign Roles to Users/Groups button in the Users/Groups section of this page.");
} catch (PermissionException ex) {
JH.addMessage(FacesMessage.SEVERITY_ERROR, "The role was not able to be saved.", "Permissions " + ex.getRequiredPermissions().toString() + " missing.");
} catch (CommandException ex) {
JH.addMessage(FacesMessage.SEVERITY_FATAL, "The role was not able to be saved.");
logger.log(Level.SEVERE, "Error saving role: " + ex.getMessage(), ex);
}
}
showRoleMessages();
}
public DataverseRolePermissionHelper getDataverseRolePermissionHelper() {
return dataverseRolePermissionHelper;
}
public void setDataverseRolePermissionHelper(DataverseRolePermissionHelper dataverseRolePermissionHelper) {
this.dataverseRolePermissionHelper = dataverseRolePermissionHelper;
}
/*
============================================================================
Internal methods
============================================================================
*/
boolean renderConfigureMessages = false;
boolean renderAssignmentMessages = false;
boolean renderRoleMessages = false;
private void showNoMessages() {
renderConfigureMessages = false;
renderAssignmentMessages = false;
renderRoleMessages = false;
}
private void showConfigureMessages() {
renderConfigureMessages = true;
renderAssignmentMessages = false;
renderRoleMessages = false;
}
private void showAssignmentMessages() {
renderConfigureMessages = false;
renderAssignmentMessages = true;
renderRoleMessages = false;
}
private void showRoleMessages() {
renderConfigureMessages = false;
renderAssignmentMessages = false;
renderRoleMessages = true;
}
public Boolean getRenderConfigureMessages() {
return renderConfigureMessages;
}
public void setRenderConfigureMessages(Boolean renderConfigureMessages) {
this.renderConfigureMessages = renderConfigureMessages;
}
public Boolean getRenderAssignmentMessages() {
return renderAssignmentMessages;
}
public void setRenderAssignmentMessages(Boolean renderAssignmentMessages) {
this.renderAssignmentMessages = renderAssignmentMessages;
}
public Boolean getRenderRoleMessages() {
return renderRoleMessages;
}
public void setRenderRoleMessages(Boolean renderRoleMessages) {
this.renderRoleMessages = renderRoleMessages;
}
// inner class used for display of role assignments
public static class RoleAssignmentRow {
private final RoleAssigneeDisplayInfo assigneeDisplayInfo;
private final RoleAssignment ra;
public RoleAssignmentRow(RoleAssignment anRa, RoleAssigneeDisplayInfo disInf) {
ra = anRa;
assigneeDisplayInfo = disInf;
}
public RoleAssignment getRoleAssignment() {
return ra;
}
public RoleAssigneeDisplayInfo getAssigneeDisplayInfo() {
return assigneeDisplayInfo;
}
public DataverseRole getRole() {
return ra.getRole();
}
public String getRoleName() {
return getRole().getName();
}
public DvObject getDefinitionPoint() {
return ra.getDefinitionPoint();
}
public String getAssignedDvName() {
return ra.getDefinitionPoint().getDisplayName();
}
public Long getId() {
return ra.getId();
}
}
}