diff options
| author | sowgro <tpoke.ferrari@gmail.com> | 2025-03-02 11:22:48 -0500 | 
|---|---|---|
| committer | sowgro <tpoke.ferrari@gmail.com> | 2025-03-02 11:22:48 -0500 | 
| commit | c02c47efcb00782feb1461534923023a711d4f15 (patch) | |
| tree | 8c59e17bc6039d76d0b9522e2535a49a33b3d340 | |
| parent | 8e93fe31c81c4c36e66c48e7efcdbfedb1877385 (diff) | |
| download | JellySolutions-c02c47efcb00782feb1461534923023a711d4f15.tar.gz JellySolutions-c02c47efcb00782feb1461534923023a711d4f15.tar.bz2 JellySolutions-c02c47efcb00782feb1461534923023a711d4f15.zip  | |
First attempt at an authentication system.
22 files changed, 373 insertions, 96 deletions
diff --git a/ufund-api/data/cupboard.json b/ufund-api/data/cupboard.json index bb7ec03..abba017 100644 --- a/ufund-api/data/cupboard.json +++ b/ufund-api/data/cupboard.json @@ -1,3 +1,10 @@  [ -    {"name":"Money for coral","id":1,"maxGoal":100.0,"type":"MONETARY","filterAttributes":null,"Current":0.0} +  { +    "name": "Money for coral", +    "id": 1, +    "maxGoal": 100.0, +    "type": "MONETARY", +    "filterAttributes": null, +    "Current": 0.0 +  }  ]
\ No newline at end of file diff --git a/ufund-api/data/userAuths.json b/ufund-api/data/userAuths.json new file mode 100644 index 0000000..e451862 --- /dev/null +++ b/ufund-api/data/userAuths.json @@ -0,0 +1,7 @@ +[ +  { +    "key": "eeea7b02-7265-4a26-96de-a8ad1860c533", +    "username": "phil", +    "expiration": "2025-03-31T23:04:47.455490668" +  } +]
\ No newline at end of file diff --git a/ufund-api/data/users.json b/ufund-api/data/users.json index 4e98a14..ae575b1 100644 --- a/ufund-api/data/users.json +++ b/ufund-api/data/users.json @@ -1 +1,12 @@ -[{"name":"steve","password":null}]
\ No newline at end of file +[ +  { +    "username": "phil", +    "passwordHash": -1054080181, +    "basket": [] +  }, +  { +    "username": "tbone", +    "passwordHash": 97526364, +    "basket": [] +  } +]
\ No newline at end of file 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 new file mode 100644 index 0000000..aa27e3f --- /dev/null +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/controller/AuthController.java @@ -0,0 +1,54 @@ +package com.ufund.api.ufundapi.controller; + +import com.ufund.api.ufundapi.model.UserAuth; +import com.ufund.api.ufundapi.persistence.UserAuthDAO; +import com.ufund.api.ufundapi.persistence.UserDAO; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.Map; + +@RestController +@RequestMapping("auth") +public class AuthController { +    private final UserDAO userDAO; +    private final UserAuthDAO userAuthDAO; + +    public AuthController(UserDAO userDAO, UserAuthDAO userAuthDAO) { +        this.userDAO = userDAO; +        this.userAuthDAO = userAuthDAO; +    } + +    /** +     * Attempts to log in as a user +     * @param params A map/json object in the format {username: string, password: string} +     * @return An api key if the auth was successful, null otherwise +     */ +    @PostMapping("") +    public ResponseEntity<String> login(@RequestBody Map<String, String> params) { +        String username = params.get("username"); +        String password = params.get("password"); +        try { +            var usr = userDAO.getUser(username); +            if (usr == null || !usr.verifyPassword(password)) { +                return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); +            } +            var userAuth = UserAuth.generate(username); +            userAuthDAO.addUserAuth(userAuth); +            return new ResponseEntity<>(userAuth.getKey(), HttpStatus.OK); +        } catch (IOException ex) { +            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); +        } +    } + +    /** +     * TODO +     * @return +     */ +    @DeleteMapping("") +    public ResponseEntity<Object> logout() { +        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); +    } +} 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 4e5f156..aa9598d 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,19 +1,14 @@  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 com.ufund.api.ufundapi.persistence.UserAuthDAO;  import org.springframework.http.HttpStatus;  import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -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.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*;  import com.ufund.api.ufundapi.model.User;  import com.ufund.api.ufundapi.persistence.UserDAO; @@ -21,30 +16,34 @@ import com.ufund.api.ufundapi.persistence.UserDAO;  @RestController  @RequestMapping("users")  public class UserController { -    private static final Logger LOG = Logger.getLogger(CupboardController.class.getName()); +    private static final Logger LOG = Logger.getLogger(UserController.class.getName());      private final UserDAO UserDAO; +    private final UserAuthDAO userAuthDAO;      /**       * Create a user controller to receive REST signals       *       * @param userDAO The Data Access Object       */ -    public UserController(UserDAO userDAO) { +    public UserController(UserDAO userDAO, UserAuthDAO userAuthDAO) {          this.UserDAO = userDAO; +        this.userAuthDAO = userAuthDAO;      }      /**       * Creates a User with the provided object       * -     * @param user The user to create       * @return OK response and the user if it was successful, INTERNAL_SERVER_ERROR       *         otherwise       */      @PostMapping("") -    public ResponseEntity<User> createUser(@RequestBody User user) { +    public ResponseEntity<Boolean> createUser(@RequestBody Map<String, String> params) { +        String username = params.get("username"); +        String password = params.get("password"); +          try { -            if (UserDAO.createUser(user) != null) { -                return new ResponseEntity<>(user, HttpStatus.CREATED); +            if (UserDAO.addUser(User.create(username, password)) != null) { +                return new ResponseEntity<>(true, HttpStatus.CREATED);              } else {                  return new ResponseEntity<>(HttpStatus.CONFLICT);              } @@ -62,14 +61,19 @@ public class UserController {       *         ResponseEntity with HTTP status of NOT_FOUND if not found<br>       *         ResponseEntity with HTTP status of INTERNAL_SERVER_ERROR otherwise       */ -    @GetMapping("/{name}") -    public ResponseEntity<User> getUser(@PathVariable String name) { -        LOG.log(Level.INFO, "GET /user/{0}", name); +    @GetMapping("/{username}") +    public ResponseEntity<User> getUser(@PathVariable String username, @RequestHeader("jelly-api-key") String key) { +        LOG.log(Level.INFO, "GET /user/{0}", username); + +        var userAuth = userAuthDAO.getUserAuth(key); +        if (userAuth == null || !userAuth.getUsername().equals(username)) { +            return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); +        }          try { -            User user = UserDAO.getUser(name); +            User user = UserDAO.getUser(username);              if (user != null) { -                return new ResponseEntity<>(user, HttpStatus.OK); +                return new ResponseEntity<>(user.withoutPasswordHash(), HttpStatus.OK);              } else {                  return new ResponseEntity<>(HttpStatus.NOT_FOUND);              } @@ -89,7 +93,13 @@ public class UserController {       *         INTERNAL_SERVER_ERROR if there was an issue       */      @PutMapping("/{name}") -    public ResponseEntity<User> updateUser(@RequestBody User user, @PathVariable String name) { +    public ResponseEntity<User> updateUser(@RequestBody User user, @PathVariable String name, @RequestHeader("jelly-api-key") String key) { + +        var userAuth = userAuthDAO.getUserAuth(key); +        if (userAuth == null || !userAuth.getUsername().equals(user.getUsername())) { +            return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); +        } +          try {              user = UserDAO.updateUser(user, name);              if (user != null) { @@ -106,14 +116,20 @@ public class UserController {      /**       * Deletes a user with the desired name       *  -     * @param name The name of the user +     * @param username The name of the user       * @return OK if the user was deleted, NOT_FOUND if the user was not found, or       *         INTERNAL_SERVER_ERROR if an error occurred       */ -    @DeleteMapping("/{name}") -    public ResponseEntity<User> deleteUser(@PathVariable String name) { +    @DeleteMapping("/{username}") +    public ResponseEntity<User> deleteUser(@PathVariable String username, @RequestHeader("jelly-api-key") String key) { + +        var userAuth = userAuthDAO.getUserAuth(key); +        if (userAuth == null || !userAuth.getUsername().equals(username)) { +            return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); +        } +          try { -            if (UserDAO.deleteUser(name)) { +            if (UserDAO.deleteUser(username)) {                  return new ResponseEntity<>(HttpStatus.OK);              } else {                  return new ResponseEntity<>(HttpStatus.NOT_FOUND); 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 59f4c46..1e182a6 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 @@ -7,8 +7,8 @@ import com.fasterxml.jackson.annotation.JsonProperty;  public class User { -    @JsonProperty("name") -    private final String name; +    @JsonProperty("username") +    private final String username;      @JsonProperty("passwordHash")      private int passwordHash;      @JsonProperty("basket") @@ -17,36 +17,35 @@ public class User {      /**       * Create a new user       *  -     * @param name The name of the user +     * @param username The name of the user       */ -    public User(String name) { -        this.name = name; +    public User(String username) { +        this.username = username;          basket = new ArrayList<>();      }      /**       * Create a new user       *  -     * @param name   The name of the user +     * @param username   The name of the user       * @param basket A basket to copy from       */ -    public User(@JsonProperty("name") String name, @JsonProperty("basket") List<Need> basket) { -        this.name = name; +    public User(@JsonProperty("username") String username, @JsonProperty("passwordHash") int passwordHash, @JsonProperty("basket") List<Need> basket) { +        this.username = username;          this.basket = basket; +        this.passwordHash = passwordHash;      } -    /** -     * Create a deep copy of another user -     * -     * @param other The user to copy from -     */ -    public User(User other) { -        this.name = other.name; -        this.basket = other.basket; +    public static User create(String username, String password) { +        return new User( +                username, +                password.hashCode(), +                new ArrayList<>() +        );      } -    public String getName() { -        return name; +    public String getUsername() { +        return username;      }      public boolean verifyPassword(String password) { @@ -65,4 +64,8 @@ public class User {          basket.remove(need);      } +    public User withoutPasswordHash() { +        return new User(this.username, 0, this.basket); +    } +  } 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 new file mode 100644 index 0000000..1c11a28 --- /dev/null +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/model/UserAuth.java @@ -0,0 +1,43 @@ +package com.ufund.api.ufundapi.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.time.LocalDateTime; +import java.util.UUID; + +public class UserAuth { +    @JsonProperty("key") String key; +    @JsonProperty("username") String username; +    @JsonProperty("expiration") LocalDateTime expiration; + +    public UserAuth(@JsonProperty("key") String key, @JsonProperty("username") String username, @JsonProperty("expiration") LocalDateTime expiration) { +        this.key = key; +        this.expiration = expiration; +        this.username = username; +    } + +    /** +     * Generate a new user authentication profile +     * @param username the username the key will belong to +     * @return The new user authentication profile +     */ +    public static UserAuth generate(String username) { +        return new UserAuth( +                UUID.randomUUID().toString(), +                username, +                LocalDateTime.now().plusDays(30) +        ); +    } + +    public String getKey() { +        return key; +    } + +    public String getUsername() { +        return username; +    } + +    public LocalDateTime getExpiration() { +        return expiration; +    } +} diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthDAO.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthDAO.java new file mode 100644 index 0000000..45515b8 --- /dev/null +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthDAO.java @@ -0,0 +1,23 @@ +package com.ufund.api.ufundapi.persistence; + +import com.ufund.api.ufundapi.model.UserAuth; + +import java.io.IOException; + +public interface UserAuthDAO { + +    /** +     * Get a user authentication profile +     * @param key The auth key +     * @return The authentication profile or null if there was none +     */ +    UserAuth getUserAuth(String key); + +    /** +     * Add a user authentication profile +     * @param userAuth The user auth profile to add +     * @return True if it was successful +     * @throws IOException On any file writing error +     */ +    boolean addUserAuth(UserAuth userAuth) throws IOException; +} 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 new file mode 100644 index 0000000..67918cc --- /dev/null +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserAuthFIleDAO.java @@ -0,0 +1,62 @@ +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.util.HashMap; +import java.util.Map; + +@Component +public class UserAuthFIleDAO implements UserAuthDAO { + +    private final Map<String, UserAuth> userAuthMap; +    private final ObjectMapper objectMapper; +    private final String filename; + +    public UserAuthFIleDAO(ObjectMapper objectMapper, @Value("${authKeys.file}") String filename) throws IOException { +        this.userAuthMap = new HashMap<>(); +        this.objectMapper = objectMapper; +        this.filename = filename; +        load(); +    } + +    private void load() throws IOException { +        userAuthMap.clear(); + +        UserAuth[] userAuthKeysArray = objectMapper.readValue(new File(filename), UserAuth[].class); + +        for (UserAuth userAuth : userAuthKeysArray) { +            userAuthMap.put(userAuth.getKey(), userAuth); +        } +    } + +    private void save() throws IOException { +        objectMapper.writeValue(new File(filename), userAuthMap.values()); +    } + +    public UserAuth[] getAuthKeys() { +        synchronized (userAuthMap) { +            return userAuthMap.values().toArray(UserAuth[]::new); +        } +    } + +    @Override +    public UserAuth getUserAuth(String key) { +        synchronized (userAuthMap) { +            return userAuthMap.get(key); +        } +    } + +    @Override +    public boolean addUserAuth(UserAuth userAuth) throws IOException { +        synchronized (userAuthMap) { +            userAuthMap.put(userAuth.getKey(), userAuth); +            save(); +            return true; +        } +    } +} diff --git a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserDAO.java b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserDAO.java index d456abc..6558ce2 100644 --- a/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserDAO.java +++ b/ufund-api/src/main/java/com/ufund/api/ufundapi/persistence/UserDAO.java @@ -21,17 +21,17 @@ public interface UserDAO {      User[] getUsers() throws IOException;      /** -     * Retrieves a {@linkplain User user} with the given name +     * Retrieves a {@linkplain User user} with the given username       *  -     * @param id The ID of the {@link User user} to get +     * @param username The ID of the {@link User user} to get       *  -     * @return a {@link User user} object with the matching name +     * @return a {@link User user} object with the matching username       *         <br> -     *         null if no {@link User user} with a matching name is found +     *         null if no {@link User user} with a matching username is found       *        * @throws IOException if an issue with underlying storage       */ -    User getUser(String name) throws IOException; +    User getUser(String username) throws IOException;      /**       * Creates and saves a {@linkplain User user} @@ -44,7 +44,7 @@ public interface UserDAO {       *        * @throws IOException if an issue with underlying storage       */ -    User createUser(User user) throws IOException; +    User addUser(User user) throws IOException;      /**       * Updates and saves a {@linkplain User user} @@ -62,7 +62,7 @@ public interface UserDAO {      /**       * Deletes a {@linkplain User user} with the given id       *  -     * @param id The id of the {@link User user} +     * @param username The id of the {@link User user}       *        * @return true if the {@link User user} was deleted       *         <br> @@ -70,5 +70,5 @@ public interface UserDAO {       *        * @throws IOException if underlying storage cannot be accessed       */ -    boolean deleteUser(String name) throws IOException; +    boolean deleteUser(String username) throws IOException;  } 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 18eec18..54ce74a 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 @@ -36,7 +36,7 @@ public class UserFileDAO implements UserDAO {          User[] usersArray = objectMapper.readValue(new File(filename), User[].class);          for (User user : usersArray) { -            users.put(user.getName(), user); +            users.put(user.getUsername(), user);          }      } @@ -72,15 +72,15 @@ public class UserFileDAO implements UserDAO {      /**       * Return the user with the String name name or null otherwise       *  -     * @param name Name of desired user +     * @param username Name of desired user       *        * @return Desired user, null otherwise       * @throws IOException If there was an IO issue saving the file       */      @Override -    public User getUser(String name) throws IOException { +    public User getUser(String username) throws IOException {          synchronized (users) { -            return users.getOrDefault(name, null); +            return users.getOrDefault(username, null);          }      } @@ -93,16 +93,11 @@ public class UserFileDAO implements UserDAO {       * @throws IOException If there was an IO issue saving the file       */      @Override -    public User createUser(User user) throws IOException { +    public User addUser(User user) throws IOException {          synchronized (users) { -            if (getUser(user.getName()) == null) { -                User newUser = new User(user); -                users.put(newUser.getName(), newUser); -                save(); -                return newUser; -            } else { -                return null; -            } +            var res = users.putIfAbsent(user.getUsername(), user); +            save(); +            return res;          }      } @@ -131,16 +126,16 @@ public class UserFileDAO implements UserDAO {      /**       * Delete a user matching the name       *  -     * @param name The name of the user +     * @param username The name of the user       *        * @return True if deleted, false otherwise       * @throws IOException If there was an IO issue saving the file       */      @Override -    public boolean deleteUser(String name) throws IOException { +    public boolean deleteUser(String username) throws IOException {          synchronized (users) { -            if (users.containsKey(name)) { -                users.remove(name); +            if (users.containsKey(username)) { +                users.remove(username);                  return save();              } else {                  return false; diff --git a/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/UserControllerTest.java b/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/UserControllerTest.java index 681f47c..496c68c 100644 --- a/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/UserControllerTest.java +++ b/ufund-api/src/test/java/com/ufund/api/ufundapi/controller/UserControllerTest.java @@ -84,7 +84,7 @@ public class UserControllerTest {          User user = new User(username);          // when createUser is called, return true simulating successful          // creation and save -        when(mockUserDAO.createUser(user)).thenReturn(user); +        when(mockUserDAO.addUser(user)).thenReturn(user);          // Invoke          ResponseEntity<User> response = userController.createUser(user); @@ -101,7 +101,7 @@ public class UserControllerTest {          User user = new User(username);          // when createUser is called, return false simulating failed          // creation and save -        when(mockUserDAO.createUser(user)).thenReturn(null); +        when(mockUserDAO.addUser(user)).thenReturn(null);          // Invoke          ResponseEntity<User> response = userController.createUser(user); @@ -117,7 +117,7 @@ public class UserControllerTest {          User user = new User(username);          // When createUser is called on the Mock User DAO, throw an IOException -        doThrow(new IOException()).when(mockUserDAO).createUser(user); +        doThrow(new IOException()).when(mockUserDAO).addUser(user);          // Invoke          ResponseEntity<User> response = userController.createUser(user); diff --git a/ufund-api/src/test/java/com/ufund/api/ufundapi/model/UserTest.java b/ufund-api/src/test/java/com/ufund/api/ufundapi/model/UserTest.java index 8b8dd99..8cc0900 100644 --- a/ufund-api/src/test/java/com/ufund/api/ufundapi/model/UserTest.java +++ b/ufund-api/src/test/java/com/ufund/api/ufundapi/model/UserTest.java @@ -26,7 +26,7 @@ public class UserTest {          User user = new User(expectedName); -        assertEquals(expectedName, user.getName()); +        assertEquals(expectedName, user.getUsername());      } diff --git a/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/UserFileDAOTest.java b/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/UserFileDAOTest.java index dfe9b10..52a1fdc 100644 --- a/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/UserFileDAOTest.java +++ b/ufund-api/src/test/java/com/ufund/api/ufundapi/persistence/UserFileDAOTest.java @@ -50,7 +50,7 @@ public class UserFileDAOTest {          for (int i = 0; i < testUsers.length;++i) {  			boolean isInArray = false;              for (User user : testUsers) { -				if (users[i].getName().equals(user.getName())) { +				if (users[i].getUsername().equals(user.getUsername())) {  					isInArray = true;  				}  			} @@ -77,12 +77,12 @@ public class UserFileDAOTest {  	@Test  	public void CreateUserTest() throws IOException {  		User newUser = new User("keshey"); -		userFileDAO.createUser(newUser); +		userFileDAO.addUser(newUser);  		User actualUser = userFileDAO.getUser("keshey");  		assertNotNull(actualUser); -		assertEquals(actualUser.getName(), newUser.getName()); +		assertEquals(actualUser.getUsername(), newUser.getUsername());  	}  	@Test @@ -106,7 +106,7 @@ public class UserFileDAOTest {  		updatedUser = userFileDAO.updateUser(updatedUser, "admin");  		assertNotEquals(toBeUpdatedUser, updatedUser); -		assertEquals("jellinadmin", updatedUser.getName()); +		assertEquals("jellinadmin", updatedUser.getUsername());  	}  } diff --git a/ufund-ui/src/app/app.component.html b/ufund-ui/src/app/app.component.html index cfebc2b..6b9338c 100644 --- a/ufund-ui/src/app/app.component.html +++ b/ufund-ui/src/app/app.component.html @@ -1,4 +1,6 @@ -<h1>jelly solutions:</h1> +<h1>jelly solutions</h1> +<span>{{currentUser$ | async}}</span> +<hr>  <router-outlet /> diff --git a/ufund-ui/src/app/app.component.ts b/ufund-ui/src/app/app.component.ts index 2dbf33c..a85d04b 100644 --- a/ufund-ui/src/app/app.component.ts +++ b/ufund-ui/src/app/app.component.ts @@ -1,4 +1,7 @@ -import { Component } from '@angular/core'; +import {Component, OnInit} from '@angular/core'; +import {UsersService} from './services/users.service'; +import {BehaviorSubject, Observable, Subject} from 'rxjs'; +import {User} from './models/User';  @Component({    selector: 'app-root', @@ -6,6 +9,18 @@ import { Component } from '@angular/core';    standalone: false,    styleUrl: './app.component.css'  }) -export class AppComponent { -  title = 'ufund-ui'; +export class AppComponent implements OnInit { +    // title = 'ufund-ui'; +    currentUser$: BehaviorSubject<string> = new BehaviorSubject<string>("Logged out."); + +    constructor( +        private userService: UsersService +    ) {} + +    ngOnInit() { +        this.userService.getCurrentUser().subscribe(r => { +            this.currentUser$?.next("Logged in as " + r.username) +        }) +    } +  } diff --git a/ufund-ui/src/app/components/login/login.component.html b/ufund-ui/src/app/components/login/login.component.html index 41427ae..178ddbf 100644 --- a/ufund-ui/src/app/components/login/login.component.html +++ b/ufund-ui/src/app/components/login/login.component.html @@ -1,5 +1,5 @@  <p>Login:</p> -<input placeholder="Username" type="text"> -<input placeholder="Password" type="password"> -<button>Login</button> -<button>Create Account...</button> +<input placeholder="Username" type="text" #username> +<input placeholder="Password" type="password" #password> +<button type="button" (click)="login(username.value, password.value)">Login</button> +<button type="button">Create Account...</button> diff --git a/ufund-ui/src/app/components/login/login.component.ts b/ufund-ui/src/app/components/login/login.component.ts index efb8a58..9a4eb0f 100644 --- a/ufund-ui/src/app/components/login/login.component.ts +++ b/ufund-ui/src/app/components/login/login.component.ts @@ -1,4 +1,5 @@ -import { Component } from '@angular/core'; +import { Component } from '@angular/core' +import {UsersService} from '../../services/users.service';  @Component({    selector: 'app-login', @@ -7,5 +8,16 @@ import { Component } from '@angular/core';    styleUrl: './login.component.css'  })  export class LoginComponent { +    constructor( +        protected usersService: UsersService +    ) {} +    login(username: string | null, password: string | null) { +        console.log(`attempting to log in with ${username} ${password}`) +        if (!username || !password) { +            return; +        } + +        this.usersService.login(username, password) +    }  } diff --git a/ufund-ui/src/app/models/Need.ts b/ufund-ui/src/app/models/Need.ts index c0425ec..9e97fd4 100644 --- a/ufund-ui/src/app/models/Need.ts +++ b/ufund-ui/src/app/models/Need.ts @@ -1,7 +1,7 @@  export interface Need {    name: string,    id: number, -  filterAttributes: String[], +  filterAttributes: string[],    type: GoalType;    maxGoal: number;    current: number; diff --git a/ufund-ui/src/app/models/User.ts b/ufund-ui/src/app/models/User.ts index 46fe4a1..9149fe7 100644 --- a/ufund-ui/src/app/models/User.ts +++ b/ufund-ui/src/app/models/User.ts @@ -2,5 +2,5 @@ import {Need} from './Need';  export interface User {    username: string; -  cupboard: Need[]; +  basket: Need[];  } diff --git a/ufund-ui/src/app/services/cupboard.service.ts b/ufund-ui/src/app/services/cupboard.service.ts index c123841..4a2b4b0 100644 --- a/ufund-ui/src/app/services/cupboard.service.ts +++ b/ufund-ui/src/app/services/cupboard.service.ts @@ -18,8 +18,7 @@ export class CupboardService {      ) {}      createNeed(need: Need): Observable<boolean> { -        return this.http.post<boolean>( -            this.url, need, this.httpOptions) +        return this.http.post<boolean>(this.url, need, this.httpOptions)      }      getNeeds(): Observable<Need[]> { diff --git a/ufund-ui/src/app/services/users.service.ts b/ufund-ui/src/app/services/users.service.ts index 571c004..28cc266 100644 --- a/ufund-ui/src/app/services/users.service.ts +++ b/ufund-ui/src/app/services/users.service.ts @@ -1,6 +1,6 @@  import { Injectable } from '@angular/core';  import {HttpClient, HttpHeaders} from '@angular/common/http'; -import {Observable} from 'rxjs'; +import {firstValueFrom, Observable, of, Subject} from 'rxjs';  import {User} from '../models/User';  @Injectable({ @@ -8,11 +8,24 @@ import {User} from '../models/User';  })  export class UsersService { -    private currentUserID? : number +    private currentUser : Subject<User> = new Subject(); +    private apiKey: string = "";      private url = "http://localhost:8080/users" +    private authUrl = "http://localhost:8080/auth"      private httpOptions = { -        headers: new HttpHeaders({'Content-Type': 'application/json'}) +        headers: new HttpHeaders({ +            'Content-Type': 'application/json', +            "jelly-api-key": this.apiKey +        }) +    }; +    private httpOptions2 = { +        headers: new HttpHeaders({ +            'Content-Type': 'application/json', +            "jelly-api-key": this.apiKey +        }), +        responseType: "text" as "json" // don't ask me how or why this works, bc i have no clue... +        // see the relevant angular bug report https://github.com/angular/angular/issues/18586      };      constructor( @@ -23,7 +36,7 @@ export class UsersService {          return this.http.post<User>(this.url, data, this.httpOptions)      } -    getUser(id: number): Observable<User> { +    getUser(id: string): Observable<User> {          return this.http.get<User>(`${this.url}/${id}`, this.httpOptions)      } @@ -35,7 +48,22 @@ export class UsersService {          return this.http.delete<boolean>(`${this.url}/${id}`, this.httpOptions)      } -    getCurrentUser(): Observable<User> | undefined { -        return this.currentUserID ? this.getUser(this.currentUserID) : undefined +    getCurrentUser() { +        return this.currentUser; +    } + +    async login(username: string, password: string) { +        let res = this.http.post<string>(this.authUrl, {username: username, password: password}, this.httpOptions2); +        this.apiKey = await firstValueFrom(res); +        console.log("apikey: "+this.apiKey) +        let res2 = this.http.get<User>(`${this.url}/${username}`, { +            headers: new HttpHeaders({ +                'Content-Type': 'application/json', +                "jelly-api-key": this.apiKey +            }) +        }) +        let currentU = await firstValueFrom(res2); +        this.currentUser.next(currentU); +        // this.currentUser.subscribe(r => console.log("currentUser: "+r.username))      }  }  | 
