From ab35efb06b926e8a3aee5cfc8d1fa908aa4a4707 Mon Sep 17 00:00:00 2001 From: sowgro Date: Wed, 26 Mar 2025 18:14:47 -0400 Subject: Fix cupboard access checking and logging --- .../ufundapi/controller/CupboardController.java | 33 ++++++++++++++++------ .../api/ufundapi/controller/UserController.java | 12 ++++---- .../ufund/api/ufundapi/service/AuthService.java | 30 ++++++++++++++++++-- .../api/ufundapi/service/CupboardService.java | 3 +- 4 files changed, 62 insertions(+), 16 deletions(-) (limited to 'ufund-api/src/main/java/com') diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/CupboardController.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/CupboardController.java index e62d5ab..55ee457 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/CupboardController.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/CupboardController.java @@ -5,6 +5,7 @@ import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; +import com.ufund.api.ufundapi.service.AuthService; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; @@ -23,21 +24,21 @@ import com.ufund.api.ufundapi.model.Need; import com.ufund.api.ufundapi.model.Need.GoalType; import com.ufund.api.ufundapi.service.CupboardService; -import static java.util.List.of; - @RestController @RequestMapping("cupboard") public class CupboardController { private static final Logger LOG = Logger.getLogger(CupboardController.class.getName()); private final CupboardService cupboardService; + private final AuthService authService; /** * Create a cupboard controller to receive REST signals * * @param cupboardService The Data Access Object */ - public CupboardController(CupboardService cupboardService) { + public CupboardController(CupboardService cupboardService, AuthService authService) { this.cupboardService = cupboardService; + this.authService = authService; } /** @@ -50,14 +51,15 @@ public class CupboardController { * INTERNAL_SERVER_ERROR otherwise */ @PostMapping("") - public ResponseEntity createNeed(@RequestBody Map params) { + public ResponseEntity createNeed(@RequestBody Map params, @RequestHeader("jelly-api-key") String key) { LOG.log(Level.INFO, "POST /cupboard body={0}", params); String name = (String) params.get("name"); - double maxGoal = (double) params.get("maxGoal"); + double maxGoal = ((Number) params.get("maxGoal")).doubleValue(); Need.GoalType goalType = GoalType.valueOf((String) params.get("type")); try { + authService.keyHasAccessToCupboard(key); Need need = cupboardService.createNeed(name, maxGoal, goalType); return new ResponseEntity<>(need, HttpStatus.OK); } catch (DuplicateKeyException ex) { @@ -66,6 +68,9 @@ public class CupboardController { } catch (IllegalArgumentException ex) { LOG.log(Level.WARNING, ex.getLocalizedMessage()); return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } catch (IllegalAccessException ex) { + LOG.log(Level.WARNING, ex.getLocalizedMessage()); + return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); } catch (IOException ex) { LOG.log(Level.SEVERE, ex.getLocalizedMessage()); return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); @@ -152,9 +157,10 @@ public class CupboardController { * @return OK response and the need if it was successful, or INTERNAL_SERVER_ERROR if there was an issue */ @PutMapping("/{id}") - public ResponseEntity updateNeed(@RequestBody Need need, @PathVariable int id) { + public ResponseEntity updateNeed(@RequestBody Need need, @PathVariable int id, @RequestHeader("jelly-api-key") String key) { LOG.log(Level.INFO, "PUT /cupboard/{0} body={1}", of(id, need)); try { + authService.keyHasAccessToCupboard(key); Need updatedNeed = cupboardService.updateNeed(need, id); if (updatedNeed != null) { return new ResponseEntity<>(need, HttpStatus.OK); @@ -164,6 +170,9 @@ public class CupboardController { } catch (IllegalArgumentException ex) { LOG.log(Level.WARNING, ex.getLocalizedMessage()); return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } catch (IllegalAccessException ex) { + LOG.log(Level.WARNING, ex.getLocalizedMessage()); + return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); } catch (IOException ex) { LOG.log(Level.SEVERE, ex.getLocalizedMessage()); return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); @@ -204,19 +213,27 @@ public class CupboardController { * @return OK if the need was deleted, NOT_FOUND if the need was not found, or INTERNAL_SERVER_ERROR if an error occurred */ @DeleteMapping("/{id}") - public ResponseEntity deleteNeed(@PathVariable int id) { + public ResponseEntity deleteNeed(@PathVariable int id, @RequestHeader("jelly-api-key") String key) { LOG.log(Level.INFO, "DELETE /cupboard/{0}", id); try { + authService.keyHasAccessToCupboard(key); Need need = cupboardService.getNeed(id); if (cupboardService.deleteNeed(id)) { return new ResponseEntity<>(need, HttpStatus.OK); } else { return new ResponseEntity<>(HttpStatus.NOT_FOUND); - } + } + } catch (IllegalAccessException ex) { + LOG.log(Level.WARNING, ex.getLocalizedMessage()); + return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); } catch (IOException ex) { LOG.log(Level.SEVERE, ex.getLocalizedMessage()); return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } } + private Object[] of(Object ...params) { + return params; + } + } diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java index c2d9e06..33d2e4f 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/UserController.java @@ -22,8 +22,6 @@ import com.ufund.api.ufundapi.model.User; import com.ufund.api.ufundapi.service.AuthService; import com.ufund.api.ufundapi.service.UserService; -import static java.util.List.of; - @RestController @RequestMapping("users") public class UserController { @@ -79,7 +77,7 @@ public class UserController { LOG.log(Level.INFO, "GET /user/{0} key={1}", of(username, key)); try { - authService.authenticate(username, key); + authService.keyHasAccessToUser(username, key); User user = userService.getUser(username); if (user != null) { return new ResponseEntity<>(user.withoutPasswordHash(), HttpStatus.OK); @@ -109,7 +107,7 @@ public class UserController { public ResponseEntity updateUser(@RequestBody User user, @PathVariable String username, @RequestHeader("jelly-api-key") String key) { LOG.log(Level.INFO,"PUT /users/{0} body={1} key={2}", of(username, user, key)); try { - authService.authenticate(username, key); + authService.keyHasAccessToUser(username, key); user = userService.updateUser(user, username); if (user != null) { return new ResponseEntity<>(user, HttpStatus.OK); @@ -141,7 +139,7 @@ public class UserController { LOG.log(Level.INFO, "DELETE /users/{0} id={1}", of(username, key)); try { - authService.authenticate(username, key); + authService.keyHasAccessToUser(username, key); if (userService.deleteUser(username)) { return new ResponseEntity<>(HttpStatus.OK); } else { @@ -156,4 +154,8 @@ public class UserController { } } + private Object[] of(Object ...params) { + return params; + } + } diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/AuthService.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/AuthService.java index 4e5ebce..cdce80d 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/AuthService.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/AuthService.java @@ -25,8 +25,9 @@ public class AuthService { * @param targetUsername The targetUsername of the user trying to be accessed. * @param key The api key obtained by the client from logging in. * @throws IllegalAccessException Thrown if access was denied to the user. + * @throws IOException Thrown on a file writing issue */ - public void authenticate(String targetUsername, String key) throws IllegalAccessException, IOException { + public void keyHasAccessToUser(String targetUsername, String key) throws IllegalAccessException, IOException { var userAuth = userAuthDAO.getUserAuth(key); if (userAuth == null) { throw new IllegalAccessException("Invalid authentication key"); @@ -39,11 +40,36 @@ public class AuthService { } } - public void authenticate(String key) throws IOException, IllegalAccessException { + /** + * Check if the provided key is valid + * @param key The api key obtained by the client from logging in. + * @throws IllegalAccessException Thrown if access was denied to the user. + * @throws IOException Thrown on a file writing issue + */ + public void keyIsValid(String key) throws IOException, IllegalAccessException { + var userAuth = userAuthDAO.getUserAuth(key); + if (userAuth == null) { + throw new IllegalAccessException("Invalid authentication key"); + } + } + + /** + * Check if the provided key has access to edit the cupboard + * @param key The api key obtained by the client from logging in. + * @throws IllegalAccessException Thrown if access was denied to the user. + * @throws IOException Thrown on a file writing issue + */ + public void keyHasAccessToCupboard(String key) throws IOException, IllegalAccessException { var userAuth = userAuthDAO.getUserAuth(key); if (userAuth == null) { throw new IllegalAccessException("Invalid authentication key"); } + + var username = userAuth.getUsername(); + var userType = userService.getUser(username).getType(); + if (userType != User.UserType.MANAGER) { + throw new IllegalAccessException("Provided key does not grant access to perform the requested operation"); + } } /** diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java index 91e3ba5..aaa8cb8 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/CupboardService.java @@ -111,7 +111,7 @@ public class CupboardService { if (checkoutAmount <= 0) { throw new IllegalArgumentException("Amount must be greater than 0"); } - authService.authenticate(key); + authService.keyIsValid(key); Need need = cupboardDAO.getNeed(id); need.incrementCurrent(checkoutAmount); } @@ -124,6 +124,7 @@ public class CupboardService { * @throws IOException Thrown on any problem removing the need */ public boolean deleteNeed(int id) throws IOException { + return cupboardDAO.deleteNeed(id); } } -- cgit v1.2.3