Skip to content
8 changes: 7 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,18 @@ repositories {
}

dependencies {
// implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-groovy-templates'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly group: 'org.projectlombok', name: 'lombok', version: '1.12.4'
implementation 'org.springframework.boot:spring-boot-starter-security'
testImplementation 'org.springframework.security:spring-security-test'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
runtimeOnly 'com.h2database:h2'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
implementation group: 'org.springframework.security', name: 'spring-security-core', version: '6.4.1'
implementation group: 'com.auth0', name: 'java-jwt', version: '4.4.0'
}

tasks.named('test') {
Expand Down
2 changes: 1 addition & 1 deletion settings.gradle
Original file line number Diff line number Diff line change
@@ -1 +1 @@
rootProject.name = 'toBeNamed'
rootProject.name = 'TaskFlow'
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package com.example.tobenamed;
package com.example.taskflow;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ToBeNamedApplication {
public class TaskFlow {

public static void main(String[] args) {
SpringApplication.run(ToBeNamedApplication.class, args);
SpringApplication.run(TaskFlow.class, args);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.example.taskflow.config;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

private final JwtUtil jwtUtil;

@Autowired
public JwtAuthenticationFilter(JwtUtil jwtUtil) {
this.jwtUtil = jwtUtil;
}

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String authorizationHeader = request.getHeader("Authorization");

if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
String token = authorizationHeader.substring(7); // Remove "Bearer " prefix
if (jwtUtil.validateToken(token)) {
String username = jwtUtil.extractUsername(token);
// Normally set user details in the security context here
}
}

filterChain.doFilter(request, response);
}
}
41 changes: 41 additions & 0 deletions src/main/java/com/example/taskflow/config/JwtUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.example.taskflow.config;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.JWTVerifier;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component
public class JwtUtil {
private final String SECRET_KEY = "mySecretKey"; // here should be something coming from for example parameter store
private final long EXPIRATION_TIME = 1000 * 60 * 60 * 10; // 10 hours

private final Algorithm algorithm = Algorithm.HMAC256(SECRET_KEY);

public String generateToken(String username) {
return JWT.create()
.withSubject(username)
.withIssuedAt(new Date())
.withExpiresAt(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.sign(algorithm);
}

public boolean validateToken(String token) {
try {
JWTVerifier verifier = JWT.require(algorithm).build();
verifier.verify(token);
return true;
} catch (JWTVerificationException e) {
return false; // Token is invalid
}
}

public String extractUsername(String token) {
DecodedJWT decodedJWT = JWT.require(algorithm).build().verify(token);
return decodedJWT.getSubject();
}
}
24 changes: 24 additions & 0 deletions src/main/java/com/example/taskflow/config/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.example.taskflow.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

protected void configure(HttpSecurity http) throws Exception {
// http.csrf().disable()
// .authorizeRequests()
// .antMatchers("/register").permitAll()
// .antMatchers("/login").permitAll()
// .antMatchers("/logout").permitAll
}
}
55 changes: 55 additions & 0 deletions src/main/java/com/example/taskflow/controller/UserController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.example.taskflow.controller;

import com.example.taskflow.model.dto.UserLoginRequest;
import com.example.taskflow.model.dto.UserRegisterRequest;
import com.example.taskflow.model.dto.UserResponse;
import com.example.taskflow.model.dto.UserUpdateRequest;
import com.example.taskflow.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
public class UserController {
private final UserService userService;

@Autowired
public UserController(UserService userService) {
this.userService = userService;
}

@PostMapping("/register")
public ResponseEntity<Void> register(@RequestBody UserRegisterRequest userRequest) {
userService.register(userRequest);
return ResponseEntity.ok().build();
}

@PostMapping("/login")
public ResponseEntity<Void> login(@RequestBody UserLoginRequest userLoginRequest) {
userService.login(userLoginRequest);
return ResponseEntity.ok().build();
}

@PostMapping("/logout")
public ResponseEntity<Void> logout() {
userService.logout();
return ResponseEntity.ok().build();
}

@GetMapping("/profile")
public ResponseEntity<UserResponse> getProfile() {
return ResponseEntity.ok(userService.getProfile());
}

@PutMapping("/profile")
public ResponseEntity<Void> updateProfile(@RequestBody UserUpdateRequest userUpdateRequest) {
userService.updateProfile(userUpdateRequest);
return ResponseEntity.ok().build();
}

@DeleteMapping("/profile")
public ResponseEntity<Void> deleteProfile() {
userService.deleteProfile();
return ResponseEntity.ok().build();
}
}
11 changes: 11 additions & 0 deletions src/main/java/com/example/taskflow/mapper/UserMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.example.taskflow.mapper;

import com.example.taskflow.model.User;
import com.example.taskflow.model.dto.UserRegisterRequest;
import org.springframework.security.core.userdetails.User.UserBuilder;

public class UserMapper {
public static User map(UserRegisterRequest userRequest) {
return new User(userRequest.username(), userRequest.password(), userRequest.email());
}
}
71 changes: 71 additions & 0 deletions src/main/java/com/example/taskflow/model/User.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package com.example.taskflow.model;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

import java.util.Objects;

@Entity(name = "users")
public class User {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Id
private Long id;
private String username;
private String password;
private String email;

public User() {
}

public User(String username, String password, String email) {
this.username = username;
this.password = password;
this.email = email;
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(id, user.id) && Objects.equals(username, user.username) && Objects.equals(password, user.password) && Objects.equals(email, user.email);
}

@Override
public int hashCode() {
return Objects.hash(id, username, password, email);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.example.taskflow.model.dto;

public record UserLoginRequest(String username, String password) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.example.taskflow.model.dto;

public record UserRegisterRequest(String username, String password, String email) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.example.taskflow.model.dto;

public record UserResponse(String username, String email) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.example.taskflow.model.dto;

public record UserUpdateRequest(String username, String email) {
}
10 changes: 10 additions & 0 deletions src/main/java/com/example/taskflow/repository/UserRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.example.taskflow.repository;

import com.example.taskflow.model.User;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
}
63 changes: 63 additions & 0 deletions src/main/java/com/example/taskflow/service/UserService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.example.taskflow.service;

import com.example.taskflow.mapper.UserMapper;
import com.example.taskflow.model.User;
import com.example.taskflow.model.dto.UserLoginRequest;
import com.example.taskflow.model.dto.UserRegisterRequest;
import com.example.taskflow.model.dto.UserResponse;
import com.example.taskflow.model.dto.UserUpdateRequest;
import com.example.taskflow.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import org.springframework.security.crypto.password.PasswordEncoder;

import java.util.Optional;

@Service
public class UserService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;

@Autowired
public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
}

public void register(UserRegisterRequest userRequest) {
if (userRepository.findByUsername(userRequest.username()).isPresent()) {
throw new IllegalArgumentException("Username already exists");
}

User user = UserMapper.map(userRequest);
userRepository.save(user);
}

public void login(UserLoginRequest userLoginRequest) {
Optional<User> userOpt = userRepository.findByUsername(userLoginRequest.username());
if (userOpt.isEmpty() || !passwordEncoder.matches(userLoginRequest.password(), userOpt.get().getPassword())) {
throw new IllegalArgumentException("Invalid username or password");
}

// Handle login logic, e.g., issue a JWT token (if needed)
}

public void logout() {
// Handle logout logic (if session-based authentication is used)
}

public UserResponse getProfile() {
// Fetch and return the currently logged-in user's profile
// return new UserResponse(/* populate with user data */);
return null;
}

public void updateProfile(UserUpdateRequest userUpdateRequest) {
// Update the current user's profile
}

public void deleteProfile() {
// Delete the current user's profile
}
}
11 changes: 10 additions & 1 deletion src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -1 +1,10 @@
spring.application.name=toBeNamed
spring.application.name=TaskFlow
spring.security.user.name=admin
spring.security.user.password=admin
spring.datasource.url=jdbc:h2:mem:test
spring.datasource.password=sa
spring.datasource.username=sa
spring.h2.console.enabled=true
spring.jpa.defer-datasource-initialization=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
Loading
Loading