diff --git a/dspace-api/src/main/java/org/dspace/alerts/AllowSessionsEnum.java b/dspace-api/src/main/java/org/dspace/alerts/AllowSessionsEnum.java new file mode 100644 index 0000000000..313923bcde --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/alerts/AllowSessionsEnum.java @@ -0,0 +1,46 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.alerts; + +/** + * Enum representing the options for allowing sessions + */ +public enum AllowSessionsEnum { + ALL(0), + CURRENT(1), + ADMIN(2); + + private int allowSessionsType; + + AllowSessionsEnum(int allowSessionsType) { + this.allowSessionsType = allowSessionsType; + } + + public int getValue() { + return allowSessionsType; + } + + public static AllowSessionsEnum fromInt(Integer alertAllowSessionType) { + if (alertAllowSessionType == null) { + return AllowSessionsEnum.ALL; + } + + switch (alertAllowSessionType) { + case 0: + return AllowSessionsEnum.ALL; + case 1: + return AllowSessionsEnum.CURRENT; + case 2: + return AllowSessionsEnum.ADMIN; + default: + throw new IllegalArgumentException("No corresponding enum value for integer"); + } + } + + +} diff --git a/dspace-api/src/main/java/org/dspace/alerts/SystemWideAlert.java b/dspace-api/src/main/java/org/dspace/alerts/SystemWideAlert.java new file mode 100644 index 0000000000..78075511d4 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/alerts/SystemWideAlert.java @@ -0,0 +1,179 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.alerts; + +import java.util.Date; +import javax.persistence.Cacheable; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.SequenceGenerator; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.dspace.core.ReloadableEntity; +import org.hibernate.annotations.CacheConcurrencyStrategy; + +/** + * Database object representing system-wide alerts + */ +@Entity +@Cacheable +@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE, include = "non-lazy") +@Table(name = "systemwidealert") +public class SystemWideAlert implements ReloadableEntity { + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "alert_id_seq") + @SequenceGenerator(name = "alert_id_seq", sequenceName = "alert_id_seq", allocationSize = 1) + @Column(name = "alert_id", unique = true, nullable = false) + private Integer alertId; + + @Column(name = "message", nullable = false) + private String message; + + @Column(name = "allow_sessions") + private int allowSessions; + + @Column(name = "countdown_to") + @Temporal(TemporalType.TIMESTAMP) + private Date countdownTo; + + @Column(name = "active") + private boolean active; + + protected SystemWideAlert() { + } + + /** + * This method returns the ID that the system-wide alert holds within the database + * + * @return The ID that the system-wide alert holds within the database + */ + @Override + public Integer getID() { + return alertId; + } + + /** + * Set the ID for the system-wide alert + * + * @param alertID The ID to set + */ + public void setID(final Integer alertID) { + this.alertId = alertID; + } + + /** + * Retrieve the message of the system-wide alert + * + * @return the message of the system-wide alert + */ + public String getMessage() { + return message; + } + + /** + * Set the message of the system-wide alert + * + * @param message The message to set + */ + public void setMessage(final String message) { + this.message = message; + } + + /** + * Retrieve what kind of sessions are allowed while the system-wide alert is active + * + * @return what kind of sessions are allowed while the system-wide alert is active + */ + public int getAllowSessions() { + return allowSessions; + } + + /** + * Set what kind of sessions are allowed while the system-wide alert is active + * + * @param allowSessions Integer representing what kind of sessions are allowed + */ + public void setAllowSessions(final int allowSessions) { + this.allowSessions = allowSessions; + } + + /** + * Retrieve the date to which will be count down when the system-wide alert is active + * + * @return the date to which will be count down when the system-wide alert is active + */ + public Date getCountdownTo() { + return countdownTo; + } + + /** + * Set the date to which will be count down when the system-wide alert is active + * + * @param countdownTo The date to which will be count down + */ + public void setCountdownTo(final Date countdownTo) { + this.countdownTo = countdownTo; + } + + /** + * Retrieve whether the system-wide alert is active + * + * @return whether the system-wide alert is active + */ + public boolean isActive() { + return active; + } + + /** + * Set whether the system-wide alert is active + * + * @param active Whether the system-wide alert is active + */ + public void setActive(final boolean active) { + this.active = active; + } + + /** + * Return true if other is the same SystemWideAlert + * as this object, false otherwise + * + * @param other object to compare to + * @return true if object passed in represents the same + * system-wide alert as this object + */ + @Override + public boolean equals(Object other) { + return (other instanceof SystemWideAlert && + new EqualsBuilder().append(this.getID(), ((SystemWideAlert) other).getID()) + .append(this.getMessage(), ((SystemWideAlert) other).getMessage()) + .append(this.getAllowSessions(), ((SystemWideAlert) other).getAllowSessions()) + .append(this.getCountdownTo(), ((SystemWideAlert) other).getCountdownTo()) + .append(this.isActive(), ((SystemWideAlert) other).isActive()) + .isEquals()); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37) + .append(this.getID()) + .append(this.getMessage()) + .append(this.getAllowSessions()) + .append(this.getCountdownTo()) + .append(this.isActive()) + .toHashCode(); + } + +} diff --git a/dspace-api/src/main/java/org/dspace/alerts/SystemWideAlertServiceImpl.java b/dspace-api/src/main/java/org/dspace/alerts/SystemWideAlertServiceImpl.java new file mode 100644 index 0000000000..19efc2364d --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/alerts/SystemWideAlertServiceImpl.java @@ -0,0 +1,110 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.alerts; + +import java.io.IOException; +import java.sql.SQLException; +import java.util.Date; +import java.util.List; + +import org.apache.logging.log4j.Logger; +import org.dspace.alerts.dao.SystemWideAlertDAO; +import org.dspace.alerts.service.SystemWideAlertService; +import org.dspace.authorize.AuthorizeException; +import org.dspace.authorize.service.AuthorizeService; +import org.dspace.core.Context; +import org.dspace.core.LogHelper; +import org.dspace.eperson.EPerson; +import org.dspace.eperson.service.EPersonService; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * The implementation for the {@link SystemWideAlertService} class + */ +public class SystemWideAlertServiceImpl implements SystemWideAlertService { + + private static final Logger log = org.apache.logging.log4j.LogManager.getLogger(SystemWideAlertService.class); + + + @Autowired + private SystemWideAlertDAO systemWideAlertDAO; + + @Autowired + private AuthorizeService authorizeService; + + @Autowired + private EPersonService ePersonService; + + public SystemWideAlert create(final Context context, final String message, + final AllowSessionsEnum allowSessionsType, + final Date countdownTo, final boolean active) throws SQLException { + SystemWideAlert systemWideAlert = new SystemWideAlert(); + systemWideAlert.setMessage(message); + systemWideAlert.setAllowSessions(allowSessionsType.getValue()); + systemWideAlert.setCountdownTo(countdownTo); + systemWideAlert.setActive(active); + + SystemWideAlert createdAlert = systemWideAlertDAO.create(context, systemWideAlert); + log.info(LogHelper.getHeader(context, "system_wide_alert_create", + "System Wide Alert has been created with message: '" + message + "' and ID " + + createdAlert.getID() + " and allowSessionsType " + allowSessionsType + + " and active set to " + active)); + + + return createdAlert; + } + + public SystemWideAlert find(final Context context, final int alertId) throws SQLException { + return systemWideAlertDAO.findByID(context, SystemWideAlert.class, alertId); + } + + public List findAll(final Context context) throws SQLException { + return systemWideAlertDAO.findAll(context, SystemWideAlert.class); + } + + public List findAll(final Context context, final int limit, final int offset) throws SQLException { + return systemWideAlertDAO.findAll(context, limit, offset); + } + + public List findAllActive(final Context context, final int limit, final int offset) + throws SQLException { + return systemWideAlertDAO.findAllActive(context, limit, offset); + } + + public void delete(final Context context, final SystemWideAlert systemWideAlert) + throws SQLException, IOException, AuthorizeException { + systemWideAlertDAO.delete(context, systemWideAlert); + log.info(LogHelper.getHeader(context, "system_wide_alert_create", + "System Wide Alert with ID " + systemWideAlert.getID() + " has been deleted")); + + } + + public void update(final Context context, final SystemWideAlert systemWideAlert) throws SQLException { + systemWideAlertDAO.save(context, systemWideAlert); + + } + + public boolean canNonAdminUserLogin(Context context) throws SQLException { + List active = findAllActive(context, 1, 0); + if (active == null || active.isEmpty()) { + return true; + } + return active.get(0).getAllowSessions() == AllowSessionsEnum.ALL.getValue(); + } + + public boolean canUserMaintainSession(Context context, EPerson ePerson) throws SQLException { + if (authorizeService.isAdmin(context, ePerson)) { + return true; + } + List active = findAllActive(context, 1, 0); + if (active == null || active.isEmpty()) { + return true; + } + return active.get(0).getAllowSessions() != AllowSessionsEnum.ADMIN.getValue(); + } +} diff --git a/dspace-api/src/main/java/org/dspace/alerts/dao/SystemWideAlertDAO.java b/dspace-api/src/main/java/org/dspace/alerts/dao/SystemWideAlertDAO.java new file mode 100644 index 0000000000..b26b647583 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/alerts/dao/SystemWideAlertDAO.java @@ -0,0 +1,45 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.alerts.dao; + +import java.sql.SQLException; +import java.util.List; + +import org.dspace.alerts.SystemWideAlert; +import org.dspace.core.Context; +import org.dspace.core.GenericDAO; + +/** + * This is the Data Access Object for the {@link SystemWideAlert} object + */ +public interface SystemWideAlertDAO extends GenericDAO { + + /** + * Returns a list of all SystemWideAlert objects in the database + * + * @param context The relevant DSpace context + * @param limit The limit for the amount of SystemWideAlerts returned + * @param offset The offset for the Processes to be returned + * @return The list of all SystemWideAlert objects in the Database + * @throws SQLException If something goes wrong + */ + List findAll(Context context, int limit, int offset) throws SQLException; + + /** + * Returns a list of all active SystemWideAlert objects in the database + * + * @param context The relevant DSpace context + * @param limit The limit for the amount of SystemWideAlerts returned + * @param offset The offset for the Processes to be returned + * @return The list of all SystemWideAlert objects in the Database + * @throws SQLException If something goes wrong + */ + List findAllActive(Context context, int limit, int offset) throws SQLException; + + +} diff --git a/dspace-api/src/main/java/org/dspace/alerts/dao/impl/SystemWideAlertDAOImpl.java b/dspace-api/src/main/java/org/dspace/alerts/dao/impl/SystemWideAlertDAOImpl.java new file mode 100644 index 0000000000..13a0e0af23 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/alerts/dao/impl/SystemWideAlertDAOImpl.java @@ -0,0 +1,48 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.alerts.dao.impl; + +import java.sql.SQLException; +import java.util.List; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Root; + +import org.dspace.alerts.SystemWideAlert; +import org.dspace.alerts.SystemWideAlert_; +import org.dspace.alerts.dao.SystemWideAlertDAO; +import org.dspace.core.AbstractHibernateDAO; +import org.dspace.core.Context; + +/** + * Implementation class for the {@link SystemWideAlertDAO} + */ +public class SystemWideAlertDAOImpl extends AbstractHibernateDAO implements SystemWideAlertDAO { + + public List findAll(final Context context, final int limit, final int offset) throws SQLException { + CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context); + CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, SystemWideAlert.class); + Root alertRoot = criteriaQuery.from(SystemWideAlert.class); + criteriaQuery.select(alertRoot); + + return list(context, criteriaQuery, false, SystemWideAlert.class, limit, offset); + } + + public List findAllActive(final Context context, final int limit, final int offset) + throws SQLException { + CriteriaBuilder criteriaBuilder = getCriteriaBuilder(context); + CriteriaQuery criteriaQuery = getCriteriaQuery(criteriaBuilder, SystemWideAlert.class); + Root alertRoot = criteriaQuery.from(SystemWideAlert.class); + criteriaQuery.select(alertRoot); + criteriaQuery.where(criteriaBuilder.equal(alertRoot.get(SystemWideAlert_.active), true)); + + return list(context, criteriaQuery, false, SystemWideAlert.class, limit, offset); + } + + +} diff --git a/dspace-api/src/main/java/org/dspace/alerts/service/SystemWideAlertService.java b/dspace-api/src/main/java/org/dspace/alerts/service/SystemWideAlertService.java new file mode 100644 index 0000000000..85b6ad304c --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/alerts/service/SystemWideAlertService.java @@ -0,0 +1,118 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.alerts.service; + +import java.io.IOException; +import java.sql.SQLException; +import java.util.Date; +import java.util.List; + +import org.dspace.alerts.AllowSessionsEnum; +import org.dspace.alerts.SystemWideAlert; +import org.dspace.authorize.AuthorizeException; +import org.dspace.core.Context; +import org.dspace.eperson.EPerson; + +/** + * An interface for the ProcessService with methods regarding the SystemWideAlert workload + */ +public interface SystemWideAlertService { + + /** + * This method will create a SystemWideAlert object in the database + * + * @param context The relevant DSpace context + * @param message The message of the system-wide alert + * @param allowSessionsType Which sessions need to be allowed for the system-wide alert + * @param countdownTo The date to which to count down to when the system-wide alert is active + * @param active Whether the system-wide alert os active + * @return The created SystemWideAlert object + * @throws SQLException If something goes wrong + */ + SystemWideAlert create(Context context, String message, AllowSessionsEnum allowSessionsType, + Date countdownTo, boolean active + ) throws SQLException; + + /** + * This method will retrieve a SystemWideAlert object from the Database with the given ID + * + * @param context The relevant DSpace context + * @param alertId The alert id on which we'll search for in the database + * @return The system-wide alert that holds the given alert id + * @throws SQLException If something goes wrong + */ + SystemWideAlert find(Context context, int alertId) throws SQLException; + + /** + * Returns a list of all SystemWideAlert objects in the database + * + * @param context The relevant DSpace context + * @return The list of all SystemWideAlert objects in the Database + * @throws SQLException If something goes wrong + */ + List findAll(Context context) throws SQLException; + + /** + * Returns a list of all SystemWideAlert objects in the database + * + * @param context The relevant DSpace context + * @param limit The limit for the amount of system-wide alerts returned + * @param offset The offset for the system-wide alerts to be returned + * @return The list of all SystemWideAlert objects in the Database + * @throws SQLException If something goes wrong + */ + List findAll(Context context, int limit, int offset) throws SQLException; + + + /** + * Returns a list of all active SystemWideAlert objects in the database + * + * @param context The relevant DSpace context + * @return The list of all active SystemWideAlert objects in the database + * @throws SQLException If something goes wrong + */ + List findAllActive(Context context, int limit, int offset) throws SQLException; + + /** + * This method will delete the given SystemWideAlert object from the database + * + * @param context The relevant DSpace context + * @param systemWideAlert The SystemWideAlert object to be deleted + * @throws SQLException If something goes wrong + */ + void delete(Context context, SystemWideAlert systemWideAlert) + throws SQLException, IOException, AuthorizeException; + + + /** + * This method will be used to update the given SystemWideAlert object in the database + * + * @param context The relevant DSpace context + * @param systemWideAlert The SystemWideAlert object to be updated + * @throws SQLException If something goes wrong + */ + void update(Context context, SystemWideAlert systemWideAlert) throws SQLException; + + + /** + * Verifies if the user connected to the current context can retain its session + * + * @param context The relevant DSpace context + * @return if the user connected to the current context can retain its session + */ + boolean canUserMaintainSession(Context context, EPerson ePerson) throws SQLException; + + + /** + * Verifies if a non admin user can log in + * + * @param context The relevant DSpace context + * @return if a non admin user can log in + */ + boolean canNonAdminUserLogin(Context context) throws SQLException; +} diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.4_2022.12.15__system_wide_alerts.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.4_2022.12.15__system_wide_alerts.sql new file mode 100644 index 0000000000..148ad5cc3c --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/h2/V7.4_2022.12.15__system_wide_alerts.sql @@ -0,0 +1,22 @@ +-- +-- The contents of this file are subject to the license and copyright +-- detailed in the LICENSE and NOTICE files at the root of the source +-- tree and available online at +-- +-- http://www.dspace.org/license/ +-- + +----------------------------------------------------------------------------------- +-- Create table for System wide alerts +----------------------------------------------------------------------------------- + +CREATE SEQUENCE alert_id_seq; + +CREATE TABLE systemwidealert +( + alert_id INTEGER NOT NULL PRIMARY KEY, + message VARCHAR(512), + allow_sessions INTEGER, + countdown_to TIMESTAMP, + active BOOLEAN +); diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/oracle/V7.4_2022.12.15__system_wide_alerts.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/oracle/V7.4_2022.12.15__system_wide_alerts.sql new file mode 100644 index 0000000000..148ad5cc3c --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/oracle/V7.4_2022.12.15__system_wide_alerts.sql @@ -0,0 +1,22 @@ +-- +-- The contents of this file are subject to the license and copyright +-- detailed in the LICENSE and NOTICE files at the root of the source +-- tree and available online at +-- +-- http://www.dspace.org/license/ +-- + +----------------------------------------------------------------------------------- +-- Create table for System wide alerts +----------------------------------------------------------------------------------- + +CREATE SEQUENCE alert_id_seq; + +CREATE TABLE systemwidealert +( + alert_id INTEGER NOT NULL PRIMARY KEY, + message VARCHAR(512), + allow_sessions INTEGER, + countdown_to TIMESTAMP, + active BOOLEAN +); diff --git a/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.4_2022.12.15__system_wide_alerts.sql b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.4_2022.12.15__system_wide_alerts.sql new file mode 100644 index 0000000000..148ad5cc3c --- /dev/null +++ b/dspace-api/src/main/resources/org/dspace/storage/rdbms/sqlmigration/postgres/V7.4_2022.12.15__system_wide_alerts.sql @@ -0,0 +1,22 @@ +-- +-- The contents of this file are subject to the license and copyright +-- detailed in the LICENSE and NOTICE files at the root of the source +-- tree and available online at +-- +-- http://www.dspace.org/license/ +-- + +----------------------------------------------------------------------------------- +-- Create table for System wide alerts +----------------------------------------------------------------------------------- + +CREATE SEQUENCE alert_id_seq; + +CREATE TABLE systemwidealert +( + alert_id INTEGER NOT NULL PRIMARY KEY, + message VARCHAR(512), + allow_sessions INTEGER, + countdown_to TIMESTAMP, + active BOOLEAN +); diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SystemWideAlertConverter.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SystemWideAlertConverter.java new file mode 100644 index 0000000000..6f02e65008 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/converter/SystemWideAlertConverter.java @@ -0,0 +1,39 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.rest.converter; + +import org.dspace.alerts.SystemWideAlert; +import org.dspace.app.rest.model.SystemWideAlertRest; +import org.dspace.app.rest.projection.Projection; +import org.springframework.stereotype.Component; + +/** + * This converter will convert an object of {@Link SystemWideAlert} to an object of {@link SystemWideAlertRest} + */ +@Component +public class SystemWideAlertConverter implements DSpaceConverter { + + + @Override + public SystemWideAlertRest convert(SystemWideAlert systemWideAlert, Projection projection) { + SystemWideAlertRest systemWideAlertRest = new SystemWideAlertRest(); + systemWideAlertRest.setProjection(projection); + systemWideAlertRest.setId(systemWideAlert.getID()); + systemWideAlertRest.setAlertId(systemWideAlert.getID()); + systemWideAlertRest.setMessage(systemWideAlert.getMessage()); + systemWideAlertRest.setAllowSessions(systemWideAlert.getAllowSessions()); + systemWideAlertRest.setCountdownTo(systemWideAlert.getCountdownTo()); + systemWideAlertRest.setActive(systemWideAlert.isActive()); + return systemWideAlertRest; + } + + @Override + public Class getModelClass() { + return SystemWideAlert.class; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SystemWideAlertRest.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SystemWideAlertRest.java new file mode 100644 index 0000000000..e82dc0b7c8 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/SystemWideAlertRest.java @@ -0,0 +1,88 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.rest.model; + +import java.util.Date; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.dspace.alerts.SystemWideAlert; +import org.dspace.app.rest.RestResourceController; + +/** + * This class serves as a REST representation for the {@link SystemWideAlert} class + */ +public class SystemWideAlertRest extends BaseObjectRest { + public static final String NAME = "systemwidealert"; + public static final String CATEGORY = RestAddressableModel.SYSTEM; + + public String getCategory() { + return CATEGORY; + } + + public Class getController() { + return RestResourceController.class; + } + + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public String getType() { + return NAME; + } + + private Integer alertId; + private String message; + private Integer allowSessions; + private Date countdownTo; + private boolean active; + + public Integer getAlertId() { + return alertId; + } + + public void setAlertId(final Integer alertID) { + this.alertId = alertID; + } + + public String getMessage() { + return message; + } + + public void setMessage(final String message) { + this.message = message; + } + + public Integer getAllowSessions() { + return allowSessions; + } + + public void setAllowSessions(final Integer allowSessions) { + this.allowSessions = allowSessions; + } + + public Date getCountdownTo() { + return countdownTo; + } + + public void setCountdownTo(final Date countdownTo) { + this.countdownTo = countdownTo; + } + + public boolean isActive() { + return active; + } + + public void setActive(final boolean active) { + this.active = active; + } + + @JsonIgnore + @Override + public Integer getId() { + return id; + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/SystemWideAlertResource.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/SystemWideAlertResource.java new file mode 100644 index 0000000000..9089103d2b --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/model/hateoas/SystemWideAlertResource.java @@ -0,0 +1,23 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.rest.model.hateoas; + +import org.dspace.alerts.SystemWideAlert; +import org.dspace.app.rest.model.SystemWideAlertRest; +import org.dspace.app.rest.model.hateoas.annotations.RelNameDSpaceResource; +import org.dspace.app.rest.utils.Utils; + +/** + * The Resource representation of a {@link SystemWideAlert} object + */ +@RelNameDSpaceResource(SystemWideAlertRest.NAME) +public class SystemWideAlertResource extends DSpaceResource { + public SystemWideAlertResource(SystemWideAlertRest content, Utils utils) { + super(content, utils); + } +} diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SystemWideAlertRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SystemWideAlertRestRepository.java new file mode 100644 index 0000000000..7fb451e7fd --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/SystemWideAlertRestRepository.java @@ -0,0 +1,164 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.rest.repository; + +import static org.apache.commons.lang3.StringUtils.isBlank; + +import java.io.IOException; +import java.sql.SQLException; +import java.util.List; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.dspace.alerts.AllowSessionsEnum; +import org.dspace.alerts.SystemWideAlert; +import org.dspace.alerts.service.SystemWideAlertService; +import org.dspace.app.rest.exception.DSpaceBadRequestException; +import org.dspace.app.rest.exception.UnprocessableEntityException; +import org.dspace.app.rest.model.SystemWideAlertRest; +import org.dspace.authorize.AuthorizeException; +import org.dspace.core.Context; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.rest.webmvc.ResourceNotFoundException; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Component; + +/** + * The repository for the SystemWideAlert workload + */ +@Component(SystemWideAlertRest.CATEGORY + "." + SystemWideAlertRest.NAME) +public class SystemWideAlertRestRepository extends DSpaceRestRepository { + + private static final Logger log = LogManager.getLogger(); + + @Autowired + private SystemWideAlertService systemWideAlertService; + + @Override + @PreAuthorize("hasAuthority('ADMIN')") + protected SystemWideAlertRest createAndReturn(Context context) throws SQLException, AuthorizeException { + SystemWideAlert systemWideAlert = createSystemWideAlert(context); + return converter.toRest(systemWideAlert, utils.obtainProjection()); + } + + + @Override + @PreAuthorize("permitAll()") + public SystemWideAlertRest findOne(Context context, Integer id) { + try { + SystemWideAlert systemWideAlert = systemWideAlertService.find(context, id); + if (systemWideAlert == null) { + throw new ResourceNotFoundException( + "systemWideAlert with id " + systemWideAlert.getID() + " was not found"); + } + return converter.toRest(systemWideAlert, utils.obtainProjection()); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + return null; + } + + @Override + @PreAuthorize("permitAll()") + public Page findAll(Context context, Pageable pageable) { + try { + List systemWideAlerts = systemWideAlertService.findAll(context, pageable.getPageSize(), + Math.toIntExact( + pageable.getOffset())); + return converter.toRestPage(systemWideAlerts, pageable, utils.obtainProjection()); + } catch (SQLException e) { + throw new RuntimeException(e.getMessage(), e); + } + } + + @Override + @PreAuthorize("hasAuthority('ADMIN')") + protected SystemWideAlertRest put(Context context, HttpServletRequest request, String apiCategory, String model, + Integer id, JsonNode jsonNode) throws SQLException, AuthorizeException { + + SystemWideAlertRest systemWideAlertRest; + try { + systemWideAlertRest = new ObjectMapper().readValue(jsonNode.toString(), SystemWideAlertRest.class); + } catch (JsonProcessingException e) { + throw new UnprocessableEntityException("Cannot parse JSON in request body", e); + } + + if (systemWideAlertRest == null || isBlank(systemWideAlertRest.getMessage())) { + throw new UnprocessableEntityException("system alert message cannot be blank"); + } + + SystemWideAlert systemWideAlert = systemWideAlertService.find(context, id); + if (systemWideAlert == null) { + throw new ResourceNotFoundException("system wide alert with id: " + id + " not found"); + } + + systemWideAlert.setMessage(systemWideAlertRest.getMessage()); + systemWideAlert.setAllowSessions(systemWideAlertRest.getAllowSessions()); + systemWideAlert.setCountdownTo(systemWideAlertRest.getCountdownTo()); + systemWideAlert.setActive(systemWideAlertRest.isActive()); + + systemWideAlertService.update(context, systemWideAlert); + context.commit(); + + return converter.toRest(systemWideAlert, utils.obtainProjection()); + } + + + /** + * Helper method to create a system-wide alert and deny creation when one already exists + * @param context The database context + * @return the created system-wide alert + * @throws SQLException + */ + private SystemWideAlert createSystemWideAlert(Context context) + throws SQLException { + List all = systemWideAlertService.findAll(context); + if (!all.isEmpty()) { + throw new DSpaceBadRequestException("A system wide alert already exists, no new value can be created. " + + "Try updating the existing one."); + } + + + HttpServletRequest req = getRequestService().getCurrentRequest().getHttpServletRequest(); + ObjectMapper mapper = new ObjectMapper(); + SystemWideAlertRest systemWideAlertRest; + try { + ServletInputStream input = req.getInputStream(); + systemWideAlertRest = mapper.readValue(input, SystemWideAlertRest.class); + } catch (IOException e1) { + throw new UnprocessableEntityException("Error parsing request body.", e1); + } + + SystemWideAlert systemWideAlert; + + try { + systemWideAlert = systemWideAlertService.create(context, systemWideAlertRest.getMessage(), + AllowSessionsEnum.fromInt( + systemWideAlertRest.getAllowSessions()), + systemWideAlertRest.getCountdownTo(), + systemWideAlertRest.isActive()); + systemWideAlertService.update(context, systemWideAlert); + } catch (SQLException e) { + throw new RuntimeException(e.getMessage(), e); + } + return systemWideAlert; + } + + + @Override + public Class getDomainClass() { + return SystemWideAlertRest.class; + } +} diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/SystemWideAlertRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SystemWideAlertRestRepositoryIT.java new file mode 100644 index 0000000000..48a996ee08 --- /dev/null +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/SystemWideAlertRestRepositoryIT.java @@ -0,0 +1,293 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.rest; + +import static com.jayway.jsonpath.JsonPath.read; +import static com.jayway.jsonpath.matchers.JsonPathMatchers.hasJsonPath; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.startsWith; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.io.IOException; +import java.sql.SQLException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.concurrent.atomic.AtomicReference; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.dspace.alerts.AllowSessionsEnum; +import org.dspace.alerts.SystemWideAlert; +import org.dspace.alerts.service.SystemWideAlertService; +import org.dspace.app.rest.model.SystemWideAlertRest; +import org.dspace.app.rest.test.AbstractControllerIntegrationTest; +import org.dspace.authorize.AuthorizeException; +import org.junit.After; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Test class to test the operations in the SystemWideAlertRestRepository + */ +public class SystemWideAlertRestRepositoryIT extends AbstractControllerIntegrationTest { + + private static final Logger log = LogManager.getLogger(SystemWideAlertRestRepositoryIT.class); + + @Autowired + private SystemWideAlertService systemWideAlertService; + + @After + public void destroy() throws Exception { + context.turnOffAuthorisationSystem(); + systemWideAlertService.findAll(context).stream().forEach(systemWideAlert -> { + try { + systemWideAlertService.delete(context, systemWideAlert); + } catch (SQLException | IOException | AuthorizeException e) { + log.error(e); + } + }); + context.restoreAuthSystemState(); + + super.destroy(); + } + + + @Test + public void findAllTest() throws Exception { + Date countdownDate = new Date(); + SystemWideAlert systemWideAlert1 = systemWideAlertService.create(context, "Test alert 1", + AllowSessionsEnum.CURRENT, countdownDate, + true); + SystemWideAlert systemWideAlert2 = systemWideAlertService.create(context, "Test alert 2", + AllowSessionsEnum.ADMIN, null, + false); + DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + getClient().perform(get("/api/system/systemwidealerts/")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$._embedded.systemwidealerts", containsInAnyOrder( + allOf( + hasJsonPath("$.alertId", is(systemWideAlert1.getID())), + hasJsonPath("$.message", is(systemWideAlert1.getMessage())), + hasJsonPath("$.allowSessions", is(systemWideAlert1.getAllowSessions())), + hasJsonPath("$.countdownTo", + startsWith(sdf.format(systemWideAlert1.getCountdownTo()))), + hasJsonPath("$.active", is(systemWideAlert1.isActive())) + ), + allOf( + hasJsonPath("$.alertId", is(systemWideAlert2.getID())), + hasJsonPath("$.message", is(systemWideAlert2.getMessage())), + hasJsonPath("$.allowSessions", is(systemWideAlert2.getAllowSessions())), + hasJsonPath("$.countdownTo", is(systemWideAlert2.getCountdownTo())), + hasJsonPath("$.active", is(systemWideAlert2.isActive())) + ) + ))); + + + } + + @Test + public void findOneTest() throws Exception { + + Date countdownDate = new Date(); + SystemWideAlert systemWideAlert1 = systemWideAlertService.create(context, "Test alert 1", + AllowSessionsEnum.CURRENT, countdownDate, + true); + SystemWideAlert systemWideAlert2 = systemWideAlertService.create(context, "Test alert 2", + AllowSessionsEnum.ADMIN, null, + false); + DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + getClient().perform(get("/api/system/systemwidealerts/" + systemWideAlert1.getID())) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$", allOf( + hasJsonPath("$.alertId", is(systemWideAlert1.getID())), + hasJsonPath("$.message", is(systemWideAlert1.getMessage())), + hasJsonPath("$.allowSessions", is(systemWideAlert1.getAllowSessions())), + hasJsonPath("$.countdownTo", + startsWith(sdf.format(systemWideAlert1.getCountdownTo()))), + hasJsonPath("$.active", is(systemWideAlert1.isActive())) + ) + )); + + } + + @Test + public void createTest() throws Exception { + SystemWideAlertRest systemWideAlertRest = new SystemWideAlertRest(); + systemWideAlertRest.setMessage("Alert test message"); + systemWideAlertRest.setCountdownTo(new Date()); + systemWideAlertRest.setAllowSessions(1); + systemWideAlertRest.setActive(true); + + ObjectMapper mapper = new ObjectMapper(); + + String authToken = getAuthToken(admin.getEmail(), password); + + AtomicReference idRef = new AtomicReference<>(); + + + DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + getClient(authToken).perform(post("/api/system/systemwidealerts/") + .content(mapper.writeValueAsBytes(systemWideAlertRest)) + .contentType(contentType)) + .andExpect(status().isCreated()) + .andExpect( + jsonPath("$", allOf( + hasJsonPath("$.alertId"), + hasJsonPath("$.message", is(systemWideAlertRest.getMessage())), + hasJsonPath("$.allowSessions", + is(systemWideAlertRest.getAllowSessions())), + hasJsonPath("$.countdownTo", + startsWith(sdf.format(systemWideAlertRest.getCountdownTo()))), + hasJsonPath("$.active", is(systemWideAlertRest.isActive())) + ) + )) + .andDo(result -> idRef + .set((read(result.getResponse().getContentAsString(), "$.alertId")))); + + getClient().perform(get("/api/system/systemwidealerts/" + idRef.get())) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$", allOf( + hasJsonPath("$.alertId", is(idRef.get())), + hasJsonPath("$.message", is(systemWideAlertRest.getMessage())), + hasJsonPath("$.allowSessions", is(systemWideAlertRest.getAllowSessions())), + hasJsonPath("$.countdownTo", + startsWith(sdf.format(systemWideAlertRest.getCountdownTo()))), + hasJsonPath("$.active", is(systemWideAlertRest.isActive())) + ) + )); + + } + + @Test + public void createForbiddenTest() throws Exception { + + SystemWideAlertRest systemWideAlertRest = new SystemWideAlertRest(); + systemWideAlertRest.setMessage("Alert test message"); + systemWideAlertRest.setCountdownTo(new Date()); + systemWideAlertRest.setAllowSessions(1); + systemWideAlertRest.setActive(true); + + ObjectMapper mapper = new ObjectMapper(); + + String authToken = getAuthToken(eperson.getEmail(), password); + + + getClient(authToken).perform(post("/api/system/systemwidealerts/") + .content(mapper.writeValueAsBytes(systemWideAlertRest)) + .contentType(contentType)) + .andExpect(status().isForbidden()); + } + + @Test + public void createUnAuthorizedTest() throws Exception { + + SystemWideAlertRest systemWideAlertRest = new SystemWideAlertRest(); + systemWideAlertRest.setMessage("Alert test message"); + systemWideAlertRest.setCountdownTo(new Date()); + systemWideAlertRest.setAllowSessions(1); + systemWideAlertRest.setActive(true); + + ObjectMapper mapper = new ObjectMapper(); + + getClient().perform(post("/api/system/systemwidealerts/") + .content(mapper.writeValueAsBytes(systemWideAlertRest)) + .contentType(contentType)) + .andExpect(status().isUnauthorized()); + + } + + + @Test + public void createWhenAlreadyExistsTest() throws Exception { + + SystemWideAlert systemWideAlert = systemWideAlertService.create(context, "Test alert", + AllowSessionsEnum.ADMIN, null, + false); + + SystemWideAlertRest systemWideAlertRest = new SystemWideAlertRest(); + systemWideAlertRest.setMessage("Alert test message"); + systemWideAlertRest.setCountdownTo(new Date()); + systemWideAlertRest.setAllowSessions(1); + systemWideAlertRest.setActive(true); + + ObjectMapper mapper = new ObjectMapper(); + + String authToken = getAuthToken(admin.getEmail(), password); + + getClient(authToken).perform(post("/api/system/systemwidealerts/") + .content(mapper.writeValueAsBytes(systemWideAlertRest)) + .contentType(contentType)) + .andExpect(status().isBadRequest()); + + } + + @Test + public void putTest() throws Exception { + + SystemWideAlert systemWideAlert = systemWideAlertService.create(context, "Alert test message", + AllowSessionsEnum.ADMIN, null, + false); + + SystemWideAlertRest systemWideAlertRest = new SystemWideAlertRest(); + systemWideAlertRest.setAlertId(systemWideAlert.getID()); + systemWideAlertRest.setMessage("Updated alert test message"); + systemWideAlertRest.setCountdownTo(new Date()); + systemWideAlertRest.setAllowSessions(1); + systemWideAlertRest.setActive(true); + + ObjectMapper mapper = new ObjectMapper(); + + String authToken = getAuthToken(admin.getEmail(), password); + + + DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + getClient(authToken).perform(put("/api/system/systemwidealerts/" + systemWideAlert.getID()) + .content(mapper.writeValueAsBytes(systemWideAlertRest)) + .contentType(contentType)) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$", allOf( + hasJsonPath("$.alertId"), + hasJsonPath("$.message", is(systemWideAlertRest.getMessage())), + hasJsonPath("$.allowSessions", + is(systemWideAlertRest.getAllowSessions())), + hasJsonPath("$.countdownTo", + startsWith(sdf.format(systemWideAlertRest.getCountdownTo()))), + hasJsonPath("$.active", is(systemWideAlertRest.isActive())) + ) + )); + + getClient().perform(get("/api/system/systemwidealerts/" + systemWideAlert.getID())) + .andExpect(status().isOk()) + .andExpect( + jsonPath("$", allOf( + hasJsonPath("$.alertId", is(systemWideAlert.getID())), + hasJsonPath("$.message", is(systemWideAlertRest.getMessage())), + hasJsonPath("$.allowSessions", is(systemWideAlertRest.getAllowSessions())), + hasJsonPath("$.countdownTo", + startsWith(sdf.format(systemWideAlertRest.getCountdownTo()))), + hasJsonPath("$.active", is(systemWideAlertRest.isActive())) + ) + )); + + + } + + +} + diff --git a/dspace/config/hibernate.cfg.xml b/dspace/config/hibernate.cfg.xml index 085ed0bd6e..d04cb75155 100644 --- a/dspace/config/hibernate.cfg.xml +++ b/dspace/config/hibernate.cfg.xml @@ -54,6 +54,7 @@ + diff --git a/dspace/config/spring/api/core-dao-services.xml b/dspace/config/spring/api/core-dao-services.xml index ae4b5e6e3b..fa19cb28d9 100644 --- a/dspace/config/spring/api/core-dao-services.xml +++ b/dspace/config/spring/api/core-dao-services.xml @@ -34,6 +34,8 @@ + + diff --git a/dspace/config/spring/api/core-services.xml b/dspace/config/spring/api/core-services.xml index a124ec830f..1854d8eee3 100644 --- a/dspace/config/spring/api/core-services.xml +++ b/dspace/config/spring/api/core-services.xml @@ -60,6 +60,8 @@ + +