diff --git a/src/main/java/com/researchspace/api/v1/UserDetailsSyadminApi.java b/src/main/java/com/researchspace/api/v1/UserDetailsSysAdminApi.java similarity index 63% rename from src/main/java/com/researchspace/api/v1/UserDetailsSyadminApi.java rename to src/main/java/com/researchspace/api/v1/UserDetailsSysAdminApi.java index 5a65a7b90..1c557cabc 100644 --- a/src/main/java/com/researchspace/api/v1/UserDetailsSyadminApi.java +++ b/src/main/java/com/researchspace/api/v1/UserDetailsSysAdminApi.java @@ -11,15 +11,18 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; -/** returns user details available to sysadmins - only sysadmins will be allowed to use this API */ -@RequestMapping("/api/v1/syadmin/userDetails") -public interface UserDetailsSyadminApi { +/** + * returns user details available to sysadmins - only sysadmins will be allowed to use this API, and + * only if sysadmin.apikey.access deployment property is set to true + */ +@RequestMapping("/api/v1/sysadmin/userDetails") +public interface UserDetailsSysAdminApi { - @GetMapping("/apiKeyInfo/all") - @ResponseBody /** - * returns a map of all users usernames to api keys. Request user must be a sysadmin or request - * will not be authorised + * returns a map of all users usernames to api keys. Request user must be a sysadmin, and + * sysadmin.apikey.access deployment property must be set to 'true', or request will be rejected */ + @GetMapping("/apiKeyInfo/all") + @ResponseBody Map getAllApiKeyInfo(@RequestAttribute(name = "user") User user); } diff --git a/src/main/java/com/researchspace/api/v1/controller/UserDetailsSysAdminApiController.java b/src/main/java/com/researchspace/api/v1/controller/UserDetailsSysAdminApiController.java index 223f0fc5e..7f4663a93 100644 --- a/src/main/java/com/researchspace/api/v1/controller/UserDetailsSysAdminApiController.java +++ b/src/main/java/com/researchspace/api/v1/controller/UserDetailsSysAdminApiController.java @@ -1,6 +1,6 @@ package com.researchspace.api.v1.controller; -import com.researchspace.api.v1.UserDetailsSyadminApi; +import com.researchspace.api.v1.UserDetailsSysAdminApi; import com.researchspace.model.SignupSource; import com.researchspace.model.User; import com.researchspace.model.UserApiKey; @@ -11,19 +11,25 @@ import java.util.Optional; import org.apache.shiro.authz.AuthorizationException; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.RequestAttribute; @ApiController -public class UserDetailsSysAdminApiController implements UserDetailsSyadminApi { +public class UserDetailsSysAdminApiController implements UserDetailsSysAdminApi { @Autowired private UserManager userManager; @Autowired private UserApiKeyManager apiKeyMgr; + @Value("${sysadmin.apikey.access}") + private boolean sysadminApiKeyAccess; + @Override public Map getAllApiKeyInfo(@RequestAttribute(name = "user") User user) { - boolean isSysadmin = user.hasSysadminRole(); - if (!isSysadmin) { + if (!user.hasSysadminRole()) { throw new AuthorizationException("Only sysadmin can use this API"); } + if (!sysadminApiKeyAccess) { + throw new AuthorizationException("Reading apiKeys by sysadmin is not enabled on this server"); + } Map allUserInfo = new HashMap<>(); for (User aUser : userManager.getUsers()) { Optional optKey = apiKeyMgr.getKeyForUser(aUser); diff --git a/src/main/java/com/researchspace/webapp/controller/UserProfileController.java b/src/main/java/com/researchspace/webapp/controller/UserProfileController.java index 01fa8690a..e50319b1c 100644 --- a/src/main/java/com/researchspace/webapp/controller/UserProfileController.java +++ b/src/main/java/com/researchspace/webapp/controller/UserProfileController.java @@ -105,6 +105,7 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; +import org.apache.shiro.SecurityUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.dao.DataIntegrityViolationException; @@ -697,7 +698,7 @@ public AjaxReturnObject updatePreferenceValue( @PostMapping("/ajax/apiKey") @IgnoreInLoggingInterceptor(ignoreRequestParams = "password") - public @ResponseBody AjaxReturnObject generateApiKey( + public @ResponseBody AjaxReturnObject generateApiKey( @RequestParam("password") String pwd) { if (isEmpty(pwd)) { return new AjaxReturnObject<>( @@ -712,7 +713,8 @@ public AjaxReturnObject updatePreferenceValue( UserApiKey apiKey = apiKeyMgr.createKeyForUser(user); SECURITY_LOG.info("User {} created new API key", user.getUsername()); - return new AjaxReturnObject<>(new ApiInfo(apiKey.getApiKey(), true, true, true, "", 0L), null); + return new AjaxReturnObject<>( + new ApiKeyInfo(apiKey.getApiKey(), true, true, true, "", 0L), null); } @DeleteMapping("/ajax/apiKey") @@ -726,7 +728,7 @@ public AjaxReturnObject updatePreferenceValue( @Data @AllArgsConstructor @NoArgsConstructor - public static class ApiInfo { + public static class ApiKeyInfo { /** The actual API key */ private String key = null; @@ -746,13 +748,12 @@ public static class ApiInfo { private long age; } - @GetMapping("/ajax/apiKeyInfo") - public @ResponseBody AjaxReturnObject getApiKeyInfo() { + @GetMapping("/ajax/apiKeyDisplayInfo") + public @ResponseBody AjaxReturnObject getApiKeyDisplayInfo() { User user = userManager.getAuthenticatedUserInSession(); Optional optKey = apiKeyMgr.getKeyForUser(user); - ApiInfo rc = new ApiInfo(); + ApiKeyInfo rc = new ApiKeyInfo(); if (optKey.isPresent()) { - rc.setKey(optKey.get().getApiKey()); rc.setRevokable(true); rc.setAge(calculateAge(optKey.get())); } @@ -764,6 +765,22 @@ public static class ApiInfo { return new AjaxReturnObject<>(rc, null); } + @GetMapping("/ajax/apiKeyValue") + public @ResponseBody AjaxReturnObject getApiKeyValue() { + if (SecurityUtils.getSubject().isRunAs()) { + return new AjaxReturnObject<>( + null, ErrorList.of("API key value cannot be accessed when 'operating as' another user")); + } + User user = userManager.getAuthenticatedUserInSession(); + SECURITY_LOG.info("User [{}] asked to see their API key", user.getUsername()); + + Optional optKey = apiKeyMgr.getKeyForUser(user); + if (!optKey.isPresent()) { + return new AjaxReturnObject<>(null, ErrorList.of("API key is not set")); + } + return new AjaxReturnObject<>(optKey.get().getApiKey(), null); + } + /** Shows a list of created OAuth apps on the user's profile page */ @GetMapping("/ajax/oAuthApps") @ResponseBody diff --git a/src/main/resources/deployments/defaultDeployment.properties b/src/main/resources/deployments/defaultDeployment.properties index 4d24b0708..9573de3b0 100644 --- a/src/main/resources/deployments/defaultDeployment.properties +++ b/src/main/resources/deployments/defaultDeployment.properties @@ -121,6 +121,8 @@ sysadmin.delete.user=false sysadmin.delete.user.resourceList.folder=archive/deletedUserResourceListings # whether successful user deletion from DB should be immediately followed by filestore resources deletion sysadmin.delete.user.deleteResourcesImmediately=true +# whether sysadmin should be able to see users' API keys; this shouldn't be changed unless in very specific scenarios +sysadmin.apikey.access=false #Path to error log file sysadmin.errorfile.path=src/test/resources/TestResources/sampleLogs/RSLogs.txt diff --git a/src/main/webapp/WEB-INF/pages/userform.jsp b/src/main/webapp/WEB-INF/pages/userform.jsp index bc73d583c..dca1d8ef0 100644 --- a/src/main/webapp/WEB-INF/pages/userform.jsp +++ b/src/main/webapp/WEB-INF/pages/userform.jsp @@ -14,12 +14,6 @@ " /> -<%-- commenting as seems unused (mk - 22/09/16) --%> -<%-- --%> -<%-- --%> -<%-- --%> -<%-- --%> - @@ -567,14 +561,14 @@