diff --git a/src/main/java/org/nrg/xapi/rest/settings/SiteConfigApi.java b/src/main/java/org/nrg/xapi/rest/settings/SiteConfigApi.java index bd4957f10d1ea946e44a18c507cdb647992d46f8..7219758827eff6e99d323da2fecdf3c41b42f820 100644 --- a/src/main/java/org/nrg/xapi/rest/settings/SiteConfigApi.java +++ b/src/main/java/org/nrg/xapi/rest/settings/SiteConfigApi.java @@ -44,6 +44,39 @@ public class SiteConfigApi extends AbstractXnatRestApi { return new ResponseEntity<>(_appInfo.getSystemProperties(), HttpStatus.OK); } + @ApiOperation(value = "Returns a map of extended build attributes.", notes = "The values are dependent on what attributes are set for the build. It is not unexpected that there are no extended build attributes.", response = String.class, responseContainer = "Map") + @ApiResponses({@ApiResponse(code = 200, message = "Extended build attributes successfully retrieved."), @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."), @ApiResponse(code = 500, message = "Unexpected error")}) + @RequestMapping(value = "buildInfo/attributes", produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.GET}) + public ResponseEntity<Map<String, Map<String, String>>> getBuildAttributeInfo() { + if (_log.isDebugEnabled()) { + _log.debug("User " + getSessionUser().getUsername() + " requested the extended application build attributes."); + } + + return new ResponseEntity<>(_appInfo.getSystemAttributes(), HttpStatus.OK); + } + + @ApiOperation(value = "Returns the system uptime.", notes = "This returns the uptime as a map of time units: days, hours, minutes, and seconds.", response = String.class, responseContainer = "Map") + @ApiResponses({@ApiResponse(code = 200, message = "System uptime successfully retrieved."), @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."), @ApiResponse(code = 500, message = "Unexpected error")}) + @RequestMapping(value = "uptime", produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.GET}) + public ResponseEntity<Map<String, String>> getSystemUptime() { + if (_log.isDebugEnabled()) { + _log.debug("User " + getSessionUser().getUsername() + " requested the system uptime map."); + } + + return new ResponseEntity<>(_appInfo.getUptime(), HttpStatus.OK); + } + + @ApiOperation(value = "Returns the system uptime.", notes = "This returns the uptime as a formatted string.", response = String.class) + @ApiResponses({@ApiResponse(code = 200, message = "System uptime successfully retrieved."), @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."), @ApiResponse(code = 500, message = "Unexpected error")}) + @RequestMapping(value = "uptime/display", produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.GET}) + public ResponseEntity<String> getFormattedSystemUptime() { + if (_log.isDebugEnabled()) { + _log.debug("User " + getSessionUser().getUsername() + " requested the formatted system uptime."); + } + + return new ResponseEntity<>(_appInfo.getFormattedUptime(), HttpStatus.OK); + } + @ApiOperation(value = "Returns the full map of site configuration properties.", notes = "Complex objects may be returned as encapsulated JSON strings.", response = String.class, responseContainer = "Map") @ApiResponses({@ApiResponse(code = 200, message = "Site configuration properties successfully retrieved."), @ApiResponse(code = 401, message = "Must be authenticated to access the XNAT REST API."), @ApiResponse(code = 403, message = "Not authorized to set site configuration properties."), @ApiResponse(code = 500, message = "Unexpected error")}) @RequestMapping(produces = {MediaType.APPLICATION_JSON_VALUE}, method = {RequestMethod.GET}) diff --git a/src/main/java/org/nrg/xapi/rest/users/UsersApi.java b/src/main/java/org/nrg/xapi/rest/users/UsersApi.java index 68728939142e621dd9ea0610e53986921f03f694..2f98f6cc408bfb643fa2bccba94291d42fa056c5 100644 --- a/src/main/java/org/nrg/xapi/rest/users/UsersApi.java +++ b/src/main/java/org/nrg/xapi/rest/users/UsersApi.java @@ -6,6 +6,7 @@ import org.nrg.framework.annotations.XapiRestController; import org.nrg.xapi.model.users.User; import org.nrg.xapi.rest.NotFoundException; import org.nrg.xdat.XDAT; +import org.nrg.xdat.preferences.SiteConfigPreferences; import org.nrg.xdat.rest.AbstractXnatRestApi; import org.nrg.xdat.security.UserGroupI; import org.nrg.xdat.security.helpers.Groups; @@ -19,6 +20,8 @@ import org.nrg.xft.event.EventUtils; import org.nrg.xft.security.UserI; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -37,14 +40,26 @@ public class UsersApi extends AbstractXnatRestApi { @RequestMapping(produces = {MediaType.APPLICATION_JSON_VALUE}, method = RequestMethod.GET) @ResponseBody public ResponseEntity<List<String>> usersGet() { + if (_preferences.getRestrictUserListAccessToAdmins()) { + final HttpStatus status = isPermitted(); + if (status != null) { + return new ResponseEntity<>(status); + } + } return new ResponseEntity<List<String>>(new ArrayList<>(Users.getAllLogins()), HttpStatus.OK); } @ApiOperation(value = "Get list of user profiles.", notes = "The users' profiles function returns a list of all users of the XNAT system with brief information about each.", response = User.class, responseContainer = "List") @ApiResponses({@ApiResponse(code = 200, message = "An array of user profiles"), @ApiResponse(code = 500, message = "Unexpected error")}) - @RequestMapping(value = {"/profiles"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = RequestMethod.GET) + @RequestMapping(value = {"profiles"}, produces = {MediaType.APPLICATION_JSON_VALUE}, method = RequestMethod.GET) @ResponseBody public ResponseEntity<List<Map<String, String>>> usersProfilesGet() { + if (_preferences.getRestrictUserListAccessToAdmins()) { + final HttpStatus status = isPermitted(); + if (status != null) { + return new ResponseEntity<>(status); + } + } List<UserI> users = Users.getUsers(); List<Map<String, String>> userMaps = new ArrayList<>(); for (UserI user : users) { @@ -396,7 +411,6 @@ public class UsersApi extends AbstractXnatRestApi { } } - @SuppressWarnings("unused") public static class Event { public static String Added = "Added User"; @@ -410,4 +424,8 @@ public class UsersApi extends AbstractXnatRestApi { public static String ModifiedSettings = "Modified User Settings"; public static String VerifiedEmail = "Verified User Email"; } + + @Autowired + @Lazy + private SiteConfigPreferences _preferences; } diff --git a/src/main/java/org/nrg/xnat/helpers/prearchive/PrearcUtils.java b/src/main/java/org/nrg/xnat/helpers/prearchive/PrearcUtils.java index a260cf1889c6896bbf666cf23b49bf07aa6498af..befae30bde9969f3e01a6f02fec5c1d7a93da501 100644 --- a/src/main/java/org/nrg/xnat/helpers/prearchive/PrearcUtils.java +++ b/src/main/java/org/nrg/xnat/helpers/prearchive/PrearcUtils.java @@ -215,7 +215,7 @@ public class PrearcUtils { String prearcPath; if (project == null || project.equals(COMMON)) { if (allowUnassigned || user == null || Roles.isSiteAdmin(user)) { - prearcPath = ArcSpecManager.GetInstance(false).getGlobalPrearchivePath(); + prearcPath = XDAT.getSiteConfigPreferences().getPrearchivePath(); } else { throw new InvalidPermissionException("user " + user.getUsername() + " does not have permission to access the Unassigned directory "); } diff --git a/src/main/java/org/nrg/xnat/services/XnatAppInfo.java b/src/main/java/org/nrg/xnat/services/XnatAppInfo.java index e6f2fd791dfd6a6074ee8f13e0476fbb1d22f74f..0589dacc9a56305c7879070f59a3e22fbdac544f 100644 --- a/src/main/java/org/nrg/xnat/services/XnatAppInfo.java +++ b/src/main/java/org/nrg/xnat/services/XnatAppInfo.java @@ -10,15 +10,26 @@ import javax.inject.Inject; import javax.servlet.ServletContext; import java.io.IOException; import java.io.InputStream; +import java.text.DecimalFormat; import java.util.*; import java.util.jar.Attributes; import java.util.jar.Manifest; @Component public class XnatAppInfo { + + public static final int MILLISECONDS_IN_A_DAY = (24 * 60 * 60 * 1000); + public static final int MILLISECONDS_IN_AN_HOUR = (60 * 60 * 1000); + public static final int MILLISECONDS_IN_A_MINUTE = (60 * 1000); + public static final DecimalFormat SECONDS_FORMAT = new DecimalFormat("##.000"); + public static final String DAYS = "days"; + public static final String HOURS = "hours"; + public static final String MINUTES = "minutes"; + public static final String SECONDS = "seconds"; + public XnatAppInfo(final ServletContext context) throws IOException { try (final InputStream input = context.getResourceAsStream("/META-INF/MANIFEST.MF")) { - final Manifest manifest = new Manifest(input); + final Manifest manifest = new Manifest(input); final Attributes attributes = manifest.getMainAttributes(); _properties.setProperty("buildNumber", attributes.getValue("Build-Number")); _properties.setProperty("buildDate", attributes.getValue("Build-Date")); @@ -52,6 +63,7 @@ public class XnatAppInfo { /** * Indicates whether the XNAT system has been initialized yet. + * * @return Returns true if the system has been initialized, false otherwise. */ public boolean isInitialized() { @@ -72,10 +84,10 @@ public class XnatAppInfo { * properties are guaranteed to include the following: * * <ul> - * <li>version</li> - * <li>buildNumber</li> - * <li>buildDate</li> - * <li>commit</li> + * <li>version</li> + * <li>buildNumber</li> + * <li>buildDate</li> + * <li>commit</li> * </ul> * * There may be other properties available in the system properties and even more available through the {@link @@ -84,7 +96,7 @@ public class XnatAppInfo { * @return The primary system properties. */ public Properties getSystemProperties() { - return (Properties)_properties.clone(); + return (Properties) _properties.clone(); } /** @@ -96,6 +108,68 @@ public class XnatAppInfo { return new HashMap<>(_attributes); } + /** + * Returns the date indicating the time the system was last started. + * + * @return A date representing the last start time. + */ + public Date getStartTime() { + return new Date(_startTime.getTime()); + } + + /** + * Returns the system uptime as a map of strings indicating the number of days, hours, minutes, and seconds since + * the system was last restarted. The map keys are {@link #DAYS}, {@link #HOURS}, {@link #MINUTES}, and {@link + * #SECONDS}. You can use these values when creating a custom display with the uptime values. If you want a simple + * string with the uptime already formatted, you can use {@link #getFormattedUptime()} instead. + * + * @return A map of values indicating the system uptime. + */ + public Map<String, String> getUptime() { + final long diff = new Date().getTime() - _startTime.getTime(); + final int days = (int) (diff / MILLISECONDS_IN_A_DAY); + final long daysRemainder = diff % MILLISECONDS_IN_A_DAY; + final int hours = (int) (daysRemainder / MILLISECONDS_IN_AN_HOUR); + final long hoursRemainder = daysRemainder % MILLISECONDS_IN_AN_HOUR; + final int minutes = (int) (hoursRemainder / MILLISECONDS_IN_A_MINUTE); + final long minutesRemainder = hoursRemainder % MILLISECONDS_IN_A_MINUTE; + + final Map<String, String> uptime = new HashMap<>(); + if (days > 0) { + uptime.put(DAYS, Integer.toString(days)); + } + if (hours > 0) { + uptime.put(HOURS, Integer.toString(hours)); + } + if (minutes > 0) { + uptime.put(MINUTES, Integer.toString(minutes)); + } + uptime.put(SECONDS, SECONDS_FORMAT.format(minutesRemainder / 1000F)); + + return uptime; + } + + /** + * Returns the system uptime in a formatted display string. + * + * @return The formatted system uptime. + */ + public String getFormattedUptime() { + final Map<String, String> uptime = getUptime(); + final StringBuilder buffer = new StringBuilder(); + if (uptime.containsKey(DAYS)) { + buffer.append(uptime.get(DAYS)).append(" days, "); + } + if (uptime.containsKey(HOURS)) { + buffer.append(uptime.get(HOURS)).append(" hours, "); + } + if (uptime.containsKey(MINUTES)) { + buffer.append(uptime.get(MINUTES)).append(" minutes, "); + } + buffer.append(uptime.get(SECONDS)).append(" seconds"); + return buffer.toString(); + } + private static final Logger _log = LoggerFactory.getLogger(XnatAppInfo.class); private static final List<String> PRIMARY_MANIFEST_ATTRIBUTES = Arrays.asList("Build-Number", "Build-Date", "Implementation-Version", "Implementation-Sha"); @@ -103,6 +177,7 @@ public class XnatAppInfo { @Inject private JdbcTemplate _template; + private final Date _startTime = new Date(); private final Properties _properties = new Properties(); private final Map<String, Map<String, String>> _attributes = new HashMap<>(); private boolean _initialized = false; diff --git a/src/main/webapp/pages/foo.jsp b/src/main/webapp/pages/foo.jsp deleted file mode 100755 index b88053549c9c05f3fc76783e524519bb46ba8a75..0000000000000000000000000000000000000000 --- a/src/main/webapp/pages/foo.jsp +++ /dev/null @@ -1,3 +0,0 @@ -<%@ page contentType="text/html;charset=UTF-8" language="java" %> - -<h1>Foo</h1> diff --git a/src/main/webapp/xnat-templates/screens/topBar/Administer/Default.vm b/src/main/webapp/xnat-templates/screens/topBar/Administer/Default.vm index 0fe8d13b8a55d71a97eef189cdbc05cdfa067e42..5792b19dc6d8782724e08e5819e4a38fe619bb0a 100644 --- a/src/main/webapp/xnat-templates/screens/topBar/Administer/Default.vm +++ b/src/main/webapp/xnat-templates/screens/topBar/Administer/Default.vm @@ -1,9 +1,9 @@ <!-- Sequence: 10 --> +<li><a href="/page/admin/">Site Administration</a></li> <li><a href="$link.setPage("XDATScreen_admin.vm")">Users</a></li> <li><a href="$link.setPage("XDATScreen_groups.vm")">Groups</a></li> <li><a href="$link.setPage("XDATScreen_dataTypes.vm")">Data Types</a></li> <li><a href="$link.setPage("XDATScreen_email.vm")">Email</a></li> <li><a href="$link.setPage("XDATScreen_manage_pipeline.vm")">Pipelines</a></li> -<li><a href="/page/admin/">Configuration</a></li> <li><a href="$link.setPage("Scripts.vm")">Automation</a></li> <li><a href="$link.setPage("XDATScreen_admin_options.vm")">More...</a></li>