diff options
author | benal01 <bja4245@rit.edu> | 2025-04-01 09:34:36 -0400 |
---|---|---|
committer | benal01 <bja4245@rit.edu> | 2025-04-01 09:34:36 -0400 |
commit | 7ed26c5ee7171a502f6f8527fc55de2bb77eab3b (patch) | |
tree | 2046e58c146097aac21c9e352771420c31df6589 /ufund-api/src/main/java/com | |
parent | ef46ddd082bb91d0262363536d46fe3eb4da47be (diff) | |
parent | d8330f1ac85b26d08ca4df5ce3875078d7b4f47f (diff) | |
download | JellySolutions-7ed26c5ee7171a502f6f8527fc55de2bb77eab3b.tar.gz JellySolutions-7ed26c5ee7171a502f6f8527fc55de2bb77eab3b.tar.bz2 JellySolutions-7ed26c5ee7171a502f6f8527fc55de2bb77eab3b.zip |
Merge branch 'main' of https://github.com/RIT-SWEN-261-02/team-project-2245-swen-261-02-2b-jellysolutions
Diffstat (limited to 'ufund-api/src/main/java/com')
12 files changed, 334 insertions, 155 deletions
diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/AuthController.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/AuthController.java index b46d4ee..82b2c67 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/AuthController.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/AuthController.java @@ -2,6 +2,8 @@ package com.ufund.api.ufundapi.controller; import java.io.IOException; import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -17,6 +19,7 @@ import com.ufund.api.ufundapi.service.AuthService; @RestController @RequestMapping("auth") public class AuthController { + private static final Logger LOG = Logger.getLogger(AuthController.class.getName()); private final AuthService authService; public AuthController(AuthService authService) { @@ -32,15 +35,18 @@ public class AuthController { */ @PostMapping("") public ResponseEntity<String> login(@RequestBody Map<String, String> params) { + LOG.log(Level.INFO, "POST /auth body={0}", params); String username = params.get("username"); String password = params.get("password"); try { String key = authService.login(username, password); return new ResponseEntity<>(key, HttpStatus.OK); - } catch (IllegalAccessException e) { - return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); + } catch (IllegalAccessException ex) { + LOG.log(Level.WARNING, ex.getLocalizedMessage()); + return new ResponseEntity<>(ex.getMessage(), HttpStatus.UNAUTHORIZED); } catch (IOException ex) { - return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + LOG.log(Level.SEVERE, ex.getLocalizedMessage()); + return new ResponseEntity<>(ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); } } @@ -52,11 +58,13 @@ public class AuthController { */ @DeleteMapping("") public ResponseEntity<Object> logout(@RequestHeader("jelly-api-key") String key) { + LOG.log(Level.INFO, "DELETE /auth key={0}", key); try { authService.logout(key); return new ResponseEntity<>(HttpStatus.OK); - } catch (IOException e) { - return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } catch (IOException ex) { + LOG.log(Level.WARNING, ex.getLocalizedMessage()); + return new ResponseEntity<>(ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); } } } 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 36ae341..12fb0a9 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 @@ -13,6 +13,7 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @@ -20,6 +21,7 @@ import org.springframework.web.bind.annotation.RestController; import com.ufund.api.ufundapi.DuplicateKeyException; import com.ufund.api.ufundapi.model.Need; import com.ufund.api.ufundapi.model.Need.GoalType; +import com.ufund.api.ufundapi.service.AuthService; import com.ufund.api.ufundapi.service.CupboardService; @RestController @@ -27,14 +29,16 @@ import com.ufund.api.ufundapi.service.CupboardService; 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; } /** @@ -47,21 +51,33 @@ public class CupboardController { * INTERNAL_SERVER_ERROR otherwise */ @PostMapping("") - public ResponseEntity<Need> createNeed(@RequestBody Map<String, Object> params) { - System.out.println(params); + public ResponseEntity<Object> createNeed(@RequestBody Map<String, Object> 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"); + String image = (String) params.get("image"); + String location = (String) params.get("location"); + double maxGoal = ((Number) params.get("maxGoal")).doubleValue(); + boolean urgent = (Boolean) params.get("urgent"); + String description = (String) params.get("description"); Need.GoalType goalType = GoalType.valueOf((String) params.get("type")); try { - Need need = cupboardService.createNeed(name, maxGoal, goalType); + authService.keyHasAccessToCupboard(key); + Need need = cupboardService.createNeed(name, image, location, maxGoal, goalType, urgent, description); return new ResponseEntity<>(need, HttpStatus.OK); } catch (DuplicateKeyException ex) { - return new ResponseEntity<>(HttpStatus.CONFLICT); + LOG.log(Level.WARNING, ex.getLocalizedMessage()); + return new ResponseEntity<>(ex.getMessage(), HttpStatus.CONFLICT); } catch (IllegalArgumentException ex) { - return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + LOG.log(Level.WARNING, ex.getLocalizedMessage()); + return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST); + } catch (IllegalAccessException ex) { + LOG.log(Level.WARNING, ex.getLocalizedMessage()); + return new ResponseEntity<>(ex.getMessage(), HttpStatus.UNAUTHORIZED); } catch (IOException ex) { - return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + LOG.log(Level.SEVERE, ex.getLocalizedMessage()); + return new ResponseEntity<>(ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); } } @@ -74,39 +90,39 @@ public class CupboardController { * ResponseEntity with HTTP status of INTERNAL_SERVER_ERROR otherwise */ @GetMapping("") - public ResponseEntity<Need[]> getNeeds() { - LOG.info("GET /needs"); + public ResponseEntity<Object> getNeeds() { + LOG.info("GET /cupboard"); try { Need[] needs = cupboardService.getNeeds(); return new ResponseEntity<>(needs, HttpStatus.OK); - } catch (IOException e) { - LOG.log(Level.SEVERE, e.getLocalizedMessage()); - return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } catch (IOException ex) { + LOG.log(Level.SEVERE, ex.getLocalizedMessage()); + return new ResponseEntity<>(ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); } } /** - * Responds to the GET request for all {@linkplain Need need} whose name contains - * the text in name - * - * @param name The name parameter which contains the text used to find the {@link Need need} - * - * @return ResponseEntity with array of {@link Need need} objects (may be empty) and - * HTTP status of OK<br> - * ResponseEntity with HTTP status of INTERNAL_SERVER_ERROR otherwise - * <p> - */ + * Responds to the GET request for all {@linkplain Need need} whose name contains + * the text in name + * + * @param name The name parameter which contains the text used to find the {@link Need need} + * + * @return ResponseEntity with array of {@link Need need} objects (may be empty) and + * HTTP status of OK<br> + * ResponseEntity with HTTP status of INTERNAL_SERVER_ERROR otherwise + * <p> + */ @GetMapping("/") - public ResponseEntity<Need[]> searchNeeds(@RequestParam String name) { - LOG.info("GET /need/?name="+name); + public ResponseEntity<Object> searchNeeds(@RequestParam String name) { + LOG.info("GET /cupboard/?name="+name); try { Need[] needs = cupboardService.searchNeeds(name); return new ResponseEntity<>(needs, HttpStatus.OK); - } catch (IOException e) { - LOG.log(Level.SEVERE,e.getLocalizedMessage()); - return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } catch (IOException ex) { + LOG.log(Level.SEVERE,ex.getLocalizedMessage()); + return new ResponseEntity<>(ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); } } @@ -119,8 +135,8 @@ public class CupboardController { * ResponseEntity with HTTP status of NOT_FOUND if not found<br> */ @GetMapping("/{id}") - public ResponseEntity<Need> getNeed(@PathVariable int id) { - LOG.log(Level.INFO, "GET /need/{0}", id); + public ResponseEntity<Object> getNeed(@PathVariable int id) { + LOG.log(Level.INFO, "GET /cupboard/{0}", id); try { Need need = cupboardService.getNeed(id); @@ -129,9 +145,9 @@ public class CupboardController { } else { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } - } catch (IOException e) { - LOG.log(Level.SEVERE, e.getLocalizedMessage()); - return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } catch (IOException ex) { + LOG.log(Level.SEVERE, ex.getLocalizedMessage()); + return new ResponseEntity<>(ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); } } @@ -143,9 +159,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<Need> updateNeed(@RequestBody Need need, @PathVariable int id) { - LOG.log(Level.INFO, "Updating need: " + need); + public ResponseEntity<Object> 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); @@ -153,11 +170,41 @@ public class CupboardController { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } } catch (IllegalArgumentException ex) { - ex.printStackTrace(); - return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + LOG.log(Level.WARNING, ex.getLocalizedMessage()); + return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST); + } catch (IllegalAccessException ex) { + LOG.log(Level.WARNING, ex.getLocalizedMessage()); + return new ResponseEntity<>(ex.getMessage(), HttpStatus.UNAUTHORIZED); + } catch (IOException ex) { + LOG.log(Level.SEVERE, ex.getLocalizedMessage()); + return new ResponseEntity<>(ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + /** + * Checks out a need by checkoutAmount + * + * @param data JSON object with parameters needID and amount + * @param key Key used to authenticate user + * @return OK if successful, other statuses if failure + */ + @PutMapping("/checkout") + public ResponseEntity<Object> checkoutNeeds(@RequestBody Map<String, Integer> data, @RequestHeader("jelly-api-key") String key) { + int needID = data.get("needID"); + int checkoutAmount = data.get("amount"); + LOG.log(Level.INFO, "PUT /need/checkout body={0}", data); + try { + cupboardService.checkoutNeed(needID, checkoutAmount, key); + return new ResponseEntity<>(HttpStatus.OK); + } catch (IllegalArgumentException ex) { + LOG.log(Level.WARNING, ex.getLocalizedMessage()); + return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST); + } catch (IllegalAccessException ex) { + LOG.log(Level.WARNING, ex.getLocalizedMessage()); + return new ResponseEntity<>(ex.getMessage(), HttpStatus.UNAUTHORIZED); } catch (IOException ex) { - ex.printStackTrace(); - return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + LOG.log(Level.SEVERE, ex.getLocalizedMessage()); + return new ResponseEntity<>(ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); } } @@ -168,17 +215,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<Need> deleteNeed(@PathVariable int id) { + public ResponseEntity<Object> 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 (IOException e) { - return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } + } catch (IllegalAccessException ex) { + LOG.log(Level.WARNING, ex.getLocalizedMessage()); + return new ResponseEntity<>(ex.getMessage(), HttpStatus.UNAUTHORIZED); + } catch (IOException ex) { + LOG.log(Level.SEVERE, ex.getLocalizedMessage()); + return new ResponseEntity<>(ex.getMessage(), 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 dfaad3a..a34e891 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 @@ -1,7 +1,6 @@ package com.ufund.api.ufundapi.controller; import java.io.IOException; -import java.security.InvalidParameterException; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; @@ -42,7 +41,8 @@ public class UserController { * otherwise */ @PostMapping("") - public ResponseEntity<User> createUser(@RequestBody Map<String, String> params) { + public ResponseEntity<Object> createUser(@RequestBody Map<String, String> params) { + LOG.log(Level.INFO, "POST /users body={0}", params); String username = params.get("username"); String password = params.get("password"); @@ -54,9 +54,11 @@ public class UserController { return new ResponseEntity<>(HttpStatus.CONFLICT); } } catch (DuplicateKeyException ex) { - return new ResponseEntity<>(HttpStatus.CONFLICT); + LOG.log(Level.WARNING, ex.getLocalizedMessage()); + return new ResponseEntity<>(ex.getMessage(), HttpStatus.CONFLICT); } catch (IOException ex) { - return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + LOG.log(Level.SEVERE, ex.getLocalizedMessage()); + return new ResponseEntity<>(ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); } } @@ -71,11 +73,11 @@ public class UserController { * ResponseEntity with HTTP status of INTERNAL_SERVER_ERROR otherwise */ @GetMapping("/{username}") - public ResponseEntity<User> getUser(@PathVariable String username, @RequestHeader("jelly-api-key") String key) { - LOG.log(Level.INFO, "GET /user/{0}", username); + public ResponseEntity<Object> getUser(@PathVariable String username, @RequestHeader("jelly-api-key") String key) { + 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); @@ -83,10 +85,11 @@ public class UserController { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } } catch (IllegalAccessException ex) { - return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); - } catch (IOException e) { - LOG.log(Level.SEVERE, e.getLocalizedMessage()); - return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + LOG.log(Level.WARNING, ex.getLocalizedMessage()); + return new ResponseEntity<>(ex.getMessage(), HttpStatus.UNAUTHORIZED); + } catch (IOException ex) { + LOG.log(Level.SEVERE, ex.getLocalizedMessage()); + return new ResponseEntity<>(ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); } } @@ -101,24 +104,26 @@ public class UserController { * INTERNAL_SERVER_ERROR if there was an issue */ @PutMapping("/{username}") - public ResponseEntity<User> updateUser(@RequestBody User user, @PathVariable String username, @RequestHeader("jelly-api-key") String key) { - LOG.log(Level.INFO,"PUT: " + user + " " + username + " " + key.toString()); + public ResponseEntity<Object> 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); } else { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } - } catch (InvalidParameterException ex) { - return new ResponseEntity<>(HttpStatus.BAD_REQUEST); - } catch (IOException e) { - return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); - } - // catch (IllegalAccessException e) { - // return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); - // } + } catch (IllegalArgumentException ex) { + LOG.log(Level.WARNING, ex.getLocalizedMessage()); + return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST); + } catch (IllegalAccessException ex) { + LOG.log(Level.WARNING, ex.getLocalizedMessage()); + return new ResponseEntity<>(ex.getMessage(), HttpStatus.UNAUTHORIZED); + } catch (IOException ex) { + LOG.log(Level.SEVERE, ex.getLocalizedMessage()); + return new ResponseEntity<>(ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); + } } /** @@ -130,20 +135,27 @@ public class UserController { * INTERNAL_SERVER_ERROR if an error occurred */ @DeleteMapping("/{username}") - public ResponseEntity<Boolean> deleteUser(@PathVariable String username, @RequestHeader("jelly-api-key") String key) { + public ResponseEntity<Object> deleteUser(@PathVariable String username, @RequestHeader("jelly-api-key") String key) { + 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 { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } - } catch (IOException e) { - return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); - } catch (IllegalAccessException e) { - return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); + } catch (IllegalAccessException ex) { + LOG.log(Level.WARNING, ex.getLocalizedMessage()); + return new ResponseEntity<>(ex.getMessage(), HttpStatus.UNAUTHORIZED); + } catch (IOException ex) { + LOG.log(Level.SEVERE, ex.getLocalizedMessage()); + return new ResponseEntity<>(ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); } } + private Object[] of(Object ...params) { + return params; + } + } diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/model/Need.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/model/Need.java index c0e9214..9b6170b 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/model/Need.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/model/Need.java @@ -10,38 +10,57 @@ public class Need { } @JsonProperty("name") private String name; + @JsonProperty("image") private String image; + @JsonProperty("location") private String location; @JsonProperty("id") private int id; @JsonProperty("filterAttributes") private String[] filterAttributes; @JsonProperty("type") final private GoalType type; @JsonProperty("maxGoal") private double maxGoal; + @JsonProperty("urgent") private boolean urgent; @JsonProperty("current") private double current; + @JsonProperty("description") private String description; /** - * Create a new need + * Create a new need, used by the controller * * @param name The name of the need + * @param location The physical location of the need * @param id The unique ID of the need * @param maxGoal The maximum goal for this need * @param type The type of need (monetary, physical) + * @param urgent The urgency of the need + * @param description The description of the need */ - public Need(@JsonProperty("name") String name, @JsonProperty("id") int id, @JsonProperty("maxGoal") double maxGoal, GoalType type) { + public Need(@JsonProperty("name") String name, @JsonProperty("image") String image, @JsonProperty("location") String location, @JsonProperty("id") int id, @JsonProperty("maxGoal") double maxGoal, @JsonProperty("type") GoalType type, @JsonProperty("urgent") boolean urgent, @JsonProperty("Description") String description) { this.id = id; + this.image = image; + this.location = location; this.name = name; this.maxGoal = maxGoal; this.type = type; + this.urgent = urgent; + this.description = description; } /** * Create a new need * * @param name The name of the need + * @param image The image representation of the need + * @param location The location of the need * @param maxGoal The maximum goal for this need * @param type The type of need (monetary, physical) + * @param urgent The urgency of the need + * @param description The description of the need */ - public Need(String name, GoalType type, double maxGoal) { + public Need(String name, String image, String location, double maxGoal, GoalType type, boolean urgent, String description) { this.name = name; + this.image = image; + this.location = location; this.type = type; this.maxGoal = maxGoal; + this.urgent = urgent; + this.description = description; } /** @@ -51,11 +70,15 @@ public class Need { */ public Need(Need other) { this.name = other.name; + this.image = other.image; + this.location = other.location; this.id = other.id; this.filterAttributes = other.filterAttributes; this.type = other.type; this.maxGoal = other.maxGoal; this.current = other.current; + this.urgent = other.urgent; + this.description = other.description; } public String getName() { @@ -82,10 +105,15 @@ public class Need { return current; } + public void setCurrent(double current) { this.current = current; } + public void incrementCurrent(double incrementAmount) { + this.current += incrementAmount; + } + public void setFilterAttributes(String[] filterAttributes) { this.filterAttributes = filterAttributes; } diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/model/User.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/model/User.java index 6de1a8a..58b62df 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/model/User.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/model/User.java @@ -12,18 +12,23 @@ public class User { MANAGER } - @JsonProperty("username") private final String username; - @JsonProperty("passwordHash") private int passwordHash; - @JsonProperty("basket") private final List<Integer> basket; - @JsonProperty("type") private final UserType type; + @JsonProperty("username") + private final String username; + @JsonProperty("passwordHash") + private int passwordHash; + @JsonProperty("basket") + private final List<Integer> basket; + @JsonProperty("type") + private final UserType type; /** * Create a new user * - * @param username The name of the user - * @param basket A basket to copy from + * @param username The name of the user + * @param basket A basket to copy from */ - public User(@JsonProperty("username") String username, @JsonProperty("passwordHash") int passwordHash, @JsonProperty("basket") List<Integer> basket, @JsonProperty("type") UserType userType) { + public User(@JsonProperty("username") String username, @JsonProperty("passwordHash") int passwordHash, + @JsonProperty("basket") List<Integer> basket, @JsonProperty("type") UserType userType) { this.username = username; this.basket = basket; this.passwordHash = passwordHash; @@ -35,30 +40,46 @@ public class User { username, password.hashCode(), new ArrayList<>(), - UserType.HELPER - ); + UserType.HELPER); } public String getUsername() { return username; } + /** + * Verifies if the provided password's hash is the same as the user's actual + * hash + * + * @param password The password to check if valid + * @return True or false depending on if it's equal + */ public boolean verifyPassword(String password) { return password.hashCode() == passwordHash; } + /** + * Adds a need's ID to a user's basket + * + * @param need The need to add + */ public void addToBasket(Need need) { basket.add(need.getId()); } - public Integer[] getNeeds() { + public Integer[] getBasket() { return basket.toArray(Integer[]::new); } - public boolean removeBasketNeed(Integer needID) { - return basket.remove(needID); + public void removeBasketNeed(Integer needID) { + basket.remove(needID); } + /** + * Returns a user without a password hash for security purposes + * + * @return new User with empty password hash + */ public User withoutPasswordHash() { return new User(this.username, 0, this.basket, this.type); } @@ -71,6 +92,7 @@ public class User { this.passwordHash = other.passwordHash; } + @Override public String toString() { return this.username + "; basket: " + this.basket + "; type:" + this.type + "; hash: " + this.passwordHash; } diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/model/UserAuth.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/model/UserAuth.java index 1c11a28..78dccec 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/model/UserAuth.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/model/UserAuth.java @@ -1,10 +1,10 @@ package com.ufund.api.ufundapi.model; -import com.fasterxml.jackson.annotation.JsonProperty; - import java.time.LocalDateTime; import java.util.UUID; +import com.fasterxml.jackson.annotation.JsonProperty; + public class UserAuth { @JsonProperty("key") String key; @JsonProperty("username") String username; @@ -12,12 +12,13 @@ public class UserAuth { public UserAuth(@JsonProperty("key") String key, @JsonProperty("username") String username, @JsonProperty("expiration") LocalDateTime expiration) { this.key = key; - this.expiration = expiration; this.username = username; + this.expiration = expiration; } /** * Generate a new user authentication profile + * * @param username the username the key will belong to * @return The new user authentication profile */ diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/CupboardFileDAO.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/CupboardFileDAO.java index 521acae..7efda83 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/CupboardFileDAO.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/CupboardFileDAO.java @@ -1,15 +1,16 @@ package com.ufund.api.ufundapi.persistence; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.ufund.api.ufundapi.model.Need; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - import java.io.File; import java.io.IOException; import java.util.Map; import java.util.TreeMap; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.ufund.api.ufundapi.model.Need; + @Component public class CupboardFileDAO implements CupboardDAO { @@ -52,31 +53,20 @@ public class CupboardFileDAO implements CupboardDAO { } /** - * Return an array of the needs - * - * @return An array of all the needs - */ - private Need[] getNeedsArray() { - return needs.values().toArray(Need[]::new); - } - - /** * Saves the needs to json * - * @return True if the save was successful, false otherwise * @throws IOException If there was an IO issue saving the file */ - private boolean save() throws IOException { - Need[] needArray = getNeedsArray(); + private void save() throws IOException { + Need[] needArray = needs.values().toArray(Need[]::new); objectMapper.writeValue(new File(filename), needArray); - return true; } @Override public Need[] getNeeds() { synchronized (needs) { - return getNeedsArray(); + return needs.values().toArray(Need[]::new); } } @@ -117,7 +107,8 @@ public class CupboardFileDAO implements CupboardDAO { synchronized (needs) { if (needs.containsKey(id)) { needs.remove(id); - return save(); + save(); + return true; } else { return false; } diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthFIleDAO.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthFIleDAO.java index 1fc1e92..24a426b 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthFIleDAO.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthFIleDAO.java @@ -1,15 +1,17 @@ package com.ufund.api.ufundapi.persistence; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.ufund.api.ufundapi.model.UserAuth; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - import java.io.File; import java.io.IOException; +import java.time.LocalDateTime; import java.util.HashMap; import java.util.Map; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.ufund.api.ufundapi.model.UserAuth; + @Component public class UserAuthFIleDAO implements UserAuthDAO { @@ -35,7 +37,9 @@ public class UserAuthFIleDAO implements UserAuthDAO { UserAuth[] userAuthKeysArray = objectMapper.readValue(new File(filename), UserAuth[].class); for (UserAuth userAuth : userAuthKeysArray) { - userAuthMap.put(userAuth.getKey(), userAuth); + if (userAuth.getExpiration().isAfter(LocalDateTime.now())) { + userAuthMap.put(userAuth.getKey(), userAuth); + } } } diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java index 6e900aa..ec94da8 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserFileDAO.java @@ -2,9 +2,9 @@ package com.ufund.api.ufundapi.persistence; import java.io.File; import java.io.IOException; -import java.util.ArrayList; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @@ -44,12 +44,10 @@ public class UserFileDAO implements UserDAO { /** * Saves the needs to json * - * @return True if the save was successful, false otherwise * @throws IOException If there was an IO issue saving the file */ - private boolean save() throws IOException { + private void save() throws IOException { objectMapper.writeValue(new File(filename), users.values()); - return true; } @Override @@ -82,17 +80,8 @@ public class UserFileDAO implements UserDAO { public User updateUser(User user) throws IOException { synchronized (users) { if (users.containsKey(user.getUsername())) { - // var old = users.put(user.getUsername(), user); - // user.copyPassword(old); - if (user.getNeeds() == null || user.getType() == null) { - User oldData = users.get(user.getUsername()); - User crutch = new User(oldData.getUsername(), 0, new ArrayList<Integer>(), oldData.getType()); - crutch.copyPassword(oldData); - users.put(user.getUsername(), crutch); - } else { - var old = users.put(user.getUsername(), user); - user.copyPassword(old); - } + var old = users.put(user.getUsername(), user); + user.copyPassword(Objects.requireNonNull(old)); save(); return user; } else { @@ -106,7 +95,8 @@ public class UserFileDAO implements UserDAO { synchronized (users) { if (users.containsKey(username)) { users.remove(username); - return save(); + save(); + return true; } else { return false; } 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 87a16a6..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 @@ -1,11 +1,12 @@ package com.ufund.api.ufundapi.service; +import java.io.IOException; + +import org.springframework.stereotype.Component; + import com.ufund.api.ufundapi.model.User; import com.ufund.api.ufundapi.model.UserAuth; import com.ufund.api.ufundapi.persistence.UserAuthDAO; -import org.springframework.stereotype.Component; - -import java.io.IOException; @Component public class AuthService { @@ -24,18 +25,51 @@ 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 keyHasAccessToUser(String targetUsername, String key) throws IllegalAccessException, IOException { + 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 (!username.equals(targetUsername) && userType != User.UserType.MANAGER) { + throw new IllegalAccessException("Provided key does not grant access to perform the requested operation"); + } + } + + /** + * 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 authenticate(String targetUsername, String key) throws IllegalAccessException, IOException { + public void keyHasAccessToCupboard(String key) throws IOException, IllegalAccessException { var userAuth = userAuthDAO.getUserAuth(key); if (userAuth == null) { - throw new IllegalAccessException("Unauthenticated"); + 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"); } -// -// var username = userAuth.getUsername(); -// var userType = userService.getUser(username).getType(); -// if (!username.equals(targetUsername) && userType != User.UserType.MANAGER) { -// throw new IllegalAccessException("Unauthorized"); -// } } /** @@ -50,7 +84,7 @@ public class AuthService { public String login(String username, String password) throws IllegalAccessException, IOException { var usr = userService.getUser(username); if (usr == null || !usr.verifyPassword(password)) { - throw new IllegalAccessException("Unauthorized"); + throw new IllegalAccessException("Incorrect username or password"); } var userAuth = UserAuth.generate(username); userAuthDAO.addUserAuth(userAuth); 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 2398745..993e7c1 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 @@ -3,6 +3,7 @@ package com.ufund.api.ufundapi.service; import java.io.IOException; import java.util.Arrays; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import com.ufund.api.ufundapi.DuplicateKeyException; @@ -13,8 +14,10 @@ import com.ufund.api.ufundapi.persistence.CupboardDAO; public class CupboardService { private final CupboardDAO cupboardDAO; + final AuthService authService; - public CupboardService(CupboardDAO cupboardDAO) { + public CupboardService(@Lazy AuthService authService, CupboardDAO cupboardDAO) { + this.authService = authService; this.cupboardDAO = cupboardDAO; } @@ -22,16 +25,22 @@ public class CupboardService { * Creates a new Need * * @param name The name of the need to create + * @param image The image representation of the need to create + * @param location The location of the new need * @param maxGoal The max goal of the new need * @param goalType The goal type of the new need + * @param urgent The urgency of the new need + * @param description The description of the new need * @return The need that was created * @throws IOException Thrown if there was any issue saving the data * @throws DuplicateKeyException If there already exists a need with the same name */ - public Need createNeed(String name, double maxGoal, Need.GoalType goalType) throws IOException, DuplicateKeyException { + public Need createNeed(String name, String image, String location, double maxGoal, Need.GoalType goalType, boolean urgent, String description) throws IOException, DuplicateKeyException { if (maxGoal <= 0) { throw new IllegalArgumentException("Max Goal must be greater than zero"); + } else if (goalType.equals(Need.GoalType.PHYSICAL) && maxGoal % 1 != 0) { + throw new IllegalArgumentException("Cannot have non whole number value for physical goal"); } for (Need searchNeed : cupboardDAO.getNeeds()) { @@ -40,7 +49,7 @@ public class CupboardService { } } - Need need = new Need(name, goalType, maxGoal); + Need need = new Need(name, image, location, maxGoal, goalType, urgent, description); return cupboardDAO.addNeed(need); } @@ -92,11 +101,30 @@ public class CupboardService { } if (need.getMaxGoal() <= 0) { throw new IllegalArgumentException("Goal must be greater than 0"); + } else if (need.getType().equals(Need.GoalType.PHYSICAL) && need.getMaxGoal() % 1 != 0) { + throw new IllegalArgumentException("Cannot have non whole number value for physical goal"); } return cupboardDAO.updateNeed(need); } /** + * Checks out a need with the desired amount + * + * @param id The ID of the need to update + * @param checkoutAmount The amount to update the need by + * @throws IOException If there is an error reading the file + * @throws IllegalAccessException If the user has insufficient permission + */ + public void checkoutNeed(int id, double checkoutAmount, String key) throws IOException, IllegalAccessException { + if (checkoutAmount <= 0) { + throw new IllegalArgumentException("Amount must be greater than 0"); + } + authService.keyIsValid(key); + Need need = cupboardDAO.getNeed(id); + need.incrementCurrent(checkoutAmount); + } + + /** * Delete a need from the cupboard * * @param id the ID of the need @@ -104,6 +132,7 @@ public class CupboardService { * @throws IOException Thrown on any problem removing the need */ public boolean deleteNeed(int id) throws IOException { + return cupboardDAO.deleteNeed(id); } } diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java index caf9f4c..6e27f50 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/service/UserService.java @@ -12,7 +12,7 @@ import com.ufund.api.ufundapi.persistence.UserDAO; public class UserService { private final UserDAO userDAO; - private final CupboardService cupboardService; + final CupboardService cupboardService; public UserService(UserDAO userDao, CupboardService cupboardService) { this.userDAO = userDao; @@ -44,7 +44,10 @@ public class UserService { */ public User getUser(String username) throws IOException { User user = userDAO.getUser(username); - for (int needId : user.getNeeds()) { + if (user == null) { + return null; + } + for (int needId : user.getBasket()) { if (cupboardService.getNeed(needId) == null) { user.removeBasketNeed(needId); } @@ -55,7 +58,7 @@ public class UserService { /** * Updates a user * - * @param user The ID of the user to update + * @param user The ID of the user to update * @param username The user object to set (note: the ID is ignored) * @return The updated user object * @throws IOException Thrown if there was any issue saving the data @@ -77,5 +80,5 @@ public class UserService { public boolean deleteUser(String username) throws IOException { return userDAO.deleteUser(username); } - + } |