From 2596c121786a5306373abff00bec507efbe5a6f2 Mon Sep 17 00:00:00 2001 From: Yohan Boujon Date: Sun, 22 Dec 2024 10:51:28 +0100 Subject: [PATCH] db: Created "connections" table to store tokens. backend: created "/connect" endpoint. --- db/db.sql | 10 +++ .../application/helpapp/rest/Connection.java | 72 +++++++++++++++++++ .../helpapp/rest/ConnectionRepository.java | 9 +++ .../helpapp/rest/UserRepository.java | 13 ++++ .../helpapp/rest/UserServiceApplication.java | 69 +++++++++++++++++- service.sh | 7 +- 6 files changed, 176 insertions(+), 4 deletions(-) create mode 100644 helpapp-backend/user-service/src/main/java/insa/application/helpapp/rest/Connection.java create mode 100644 helpapp-backend/user-service/src/main/java/insa/application/helpapp/rest/ConnectionRepository.java diff --git a/db/db.sql b/db/db.sql index 6bb5831..379aa1a 100644 --- a/db/db.sql +++ b/db/db.sql @@ -5,6 +5,14 @@ CREATE TABLE `users` ( `password` text NOT NULL ); +CREATE TABLE `connections` ( + `id` integer UNIQUE PRIMARY KEY AUTO_INCREMENT, + `id_user` integer NOT NULL, + `token` varchar(128) UNIQUE NOT NULL, + `created_at` timestamp DEFAULT (now()), + `expires_at` timestamp +); + CREATE TABLE `roles` ( `id` integer UNIQUE PRIMARY KEY AUTO_INCREMENT, `name` text UNIQUE NOT NULL @@ -30,6 +38,8 @@ CREATE TABLE `feedback` ( `message` text NOT NULL ); +ALTER TABLE `connections` ADD FOREIGN KEY (`id_user`) REFERENCES `users` (`id`); + ALTER TABLE `users` ADD FOREIGN KEY (`id_role`) REFERENCES `roles` (`id`); ALTER TABLE `requests` ADD FOREIGN KEY (`id_user`) REFERENCES `users` (`id`); diff --git a/helpapp-backend/user-service/src/main/java/insa/application/helpapp/rest/Connection.java b/helpapp-backend/user-service/src/main/java/insa/application/helpapp/rest/Connection.java new file mode 100644 index 0000000..f042053 --- /dev/null +++ b/helpapp-backend/user-service/src/main/java/insa/application/helpapp/rest/Connection.java @@ -0,0 +1,72 @@ +package insa.application.helpapp.rest; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.persistence.Column; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; + +import java.time.LocalDateTime; + +@Entity +@Table(name = "connections", schema = "service-architecture") +public class Connection { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + + @Column(name = "id_user", nullable = false) + private int idUser; + + @Column(name = "token", nullable = false, unique = true, length = 128) + private String token; + + @Column(name = "created_at", nullable = false) + private LocalDateTime createdAt; + + @Column(name = "expires_at", nullable = false) + private LocalDateTime expiresAt; + + // Getters and Setters + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public int getidUser() { + return idUser; + } + + public void setidUser(int idUser) { + this.idUser = idUser; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + + public LocalDateTime getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(LocalDateTime createdAt) { + this.createdAt = createdAt; + } + + public LocalDateTime getExpiresAt() { + return expiresAt; + } + + public void setExpiresAt(LocalDateTime expiresAt) { + this.expiresAt = expiresAt; + } +} diff --git a/helpapp-backend/user-service/src/main/java/insa/application/helpapp/rest/ConnectionRepository.java b/helpapp-backend/user-service/src/main/java/insa/application/helpapp/rest/ConnectionRepository.java new file mode 100644 index 0000000..78b6213 --- /dev/null +++ b/helpapp-backend/user-service/src/main/java/insa/application/helpapp/rest/ConnectionRepository.java @@ -0,0 +1,9 @@ +package insa.application.helpapp.rest; + +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface ConnectionRepository extends JpaRepository { + List findByIdUser(int idUser); +} \ No newline at end of file diff --git a/helpapp-backend/user-service/src/main/java/insa/application/helpapp/rest/UserRepository.java b/helpapp-backend/user-service/src/main/java/insa/application/helpapp/rest/UserRepository.java index cdd9d01..e1d7e07 100644 --- a/helpapp-backend/user-service/src/main/java/insa/application/helpapp/rest/UserRepository.java +++ b/helpapp-backend/user-service/src/main/java/insa/application/helpapp/rest/UserRepository.java @@ -1,6 +1,19 @@ package insa.application.helpapp.rest; import org.springframework.data.jpa.repository.JpaRepository; +/* +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +*/ + +import java.util.List; public interface UserRepository extends JpaRepository { + + List findByUsername(String username); + + /* + @Query("SELECT t FROM ConnectionToken t WHERE t.userId = :userId") + List findCustomTokensByUserId(@Param("userId") int userId); + */ } diff --git a/helpapp-backend/user-service/src/main/java/insa/application/helpapp/rest/UserServiceApplication.java b/helpapp-backend/user-service/src/main/java/insa/application/helpapp/rest/UserServiceApplication.java index 5ccf6ee..b7083fc 100644 --- a/helpapp-backend/user-service/src/main/java/insa/application/helpapp/rest/UserServiceApplication.java +++ b/helpapp-backend/user-service/src/main/java/insa/application/helpapp/rest/UserServiceApplication.java @@ -3,17 +3,38 @@ package insa.application.helpapp.rest; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.hibernate.type.descriptor.java.LocalDateTimeJavaType; import org.springframework.beans.factory.annotation.Autowired; +import java.util.HashMap; +import java.util.Map; +import java.util.List; +import java.time.LocalDateTime; +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.Base64; + @SpringBootApplication @RestController public class UserServiceApplication { @Autowired private UserRepository userRepository; + @Autowired + private ConnectionRepository connectionRepository; + + // https://stackoverflow.com/users/774398/patrick + public static String generateRandomBase64Token(int byteLength) { + SecureRandom secureRandom = new SecureRandom(); + byte[] token = new byte[byteLength-32]; + secureRandom.nextBytes(token); + return Base64.getUrlEncoder().withoutPadding().encodeToString(token); //base64 encoding + } public static void main(String[] args) { SpringApplication.run(UserServiceApplication.class, args); @@ -34,7 +55,7 @@ public class UserServiceApplication { // Post should be : /create_user?idRole=1&username=toto&password=1234 @PostMapping("/create_user") - public User createUser(int idRole, String username, String password) { + public User createUser(@RequestParam int idRole, @RequestParam String username, @RequestParam String password) { User user = new User(); user.setIdRole(idRole); user.setUsername(username); @@ -43,4 +64,50 @@ public class UserServiceApplication { return userRepository.save(user); } + // Post should be : /connect?username=toto&password=1234 + // Response can vary: Error if username/password doesn't exist/match + // Response if success: userId (int), expiresAt (time), token (varchar(128)) + @PostMapping("/connect") + public ResponseEntity connect(@RequestParam String username, @RequestParam String password) + { + List users = userRepository.findByUsername(username); + // Checks username + if(users.isEmpty()) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Username not known."); + } + User user = users.getFirst(); + // Checks password + if(!user.getPassword().equals(password)) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid password."); + } + // Checks if token already exists + List connections = connectionRepository.findByIdUser(user.getId()); + Map response = new HashMap<>(); + Connection connection = new Connection(); + if(!connections.isEmpty() && (connections.getFirst().getExpiresAt().isBefore(LocalDateTime.now()))) + { + // Remove the old token + connectionRepository.delete(connections.getFirst()); + // Create new token if password & username is correct + // Get User ID + connection.setidUser(user.getId()); + // Generate token + connection.setToken(generateRandomBase64Token(128)); + // Creation date and expiration date + connection.setCreatedAt(LocalDateTime.now()); + connection.setExpiresAt(LocalDateTime.now().plusDays(1)); + // Save created Connection + connectionRepository.save(connection); + } else { + connection = connections.getFirst(); + } + + // Reponse to client + response.put("token", connection.getToken()); + response.put("userId", connection.getidUser()); + response.put("expiresAt", connection.getExpiresAt()); + return ResponseEntity.ok(response); + } + + } diff --git a/service.sh b/service.sh index 353e27a..0cfae8f 100755 --- a/service.sh +++ b/service.sh @@ -43,12 +43,12 @@ check_services() { # Function to start services start_services() { - echo "" > ../process.tmp + echo "" > "$SCRIPT_DIR"/process.tmp cd "$SCRIPT_DIR/helpapp-backend" || exit 1 for service in "${services[@]}"; do echo "> Starting $service..." mvn spring-boot:run -pl "$service" > /dev/null 2>&1 & - echo -e "$!" >> ../process.tmp + echo -e "$!" >> "$SCRIPT_DIR"/process.tmp done echo "> Waiting for the services to establish connection..." check_services @@ -63,6 +63,7 @@ stop_services() { echo -e "\t> Killing process on port $port..." fuser -k $port/tcp done + rm "$SCRIPT_DIR"/process.tmp } # Check command line arguments @@ -77,4 +78,4 @@ elif [ "$1" == "compile" ]; then mvn clean install else echo -e $RED"Usage: $0 {compile|start|stop}"$RESET -fi \ No newline at end of file +fi