diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index d92ab55..f881002 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -1,9 +1,9 @@ -name: Continuous Integration for User Service +name: Continuous Integration for Comment Service on: push: branches: - - main + - ducbao jobs: testing: @@ -26,11 +26,11 @@ jobs: - name: Unit Tests run: mvn -B test --file pom.xml -# sonar-cloud-scan: -# needs: testing -# uses: ./.github/workflows/SonarQube.yaml -# secrets: -# SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + # sonar-cloud-scan: + # needs: testing + # uses: ./.github/workflows/SonarQube.yaml + # secrets: + # SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} build-image: needs: testing @@ -38,12 +38,12 @@ jobs: secrets: DOCKER_HUB_ACCESS_TOKEN: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} -# scan-image: -# needs: build-image -# uses: ./.github/workflows/scan-image.yaml + # scan-image: + # needs: build-image + # uses: ./.github/workflows/scan-image.yaml -# notify: -# needs: scan-image -# uses: ./.github/workflows/notifyCI.yaml -# secrets: -# SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + # notify: + # needs: scan-image + # uses: ./.github/workflows/notifyCI.yaml + # secrets: + # SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} diff --git a/.gitignore b/.gitignore index 8ff02c9..549e00a 100755 --- a/.gitignore +++ b/.gitignore @@ -31,4 +31,3 @@ build/ ### VS Code ### .vscode/ -#logback-*.xml \ No newline at end of file diff --git a/README.md b/README.md index fe4f062..267c7d5 100644 --- a/README.md +++ b/README.md @@ -29,4 +29,3 @@ The backend of this streaming project exposes the following API endpoints: "# UserService" -# UserService-NT548 diff --git a/pom.xml b/pom.xml index 02dbe9f..c450841 100644 --- a/pom.xml +++ b/pom.xml @@ -82,39 +82,6 @@ gson 2.10.1 - - io.jsonwebtoken - jjwt-api - 0.11.5 - - - io.jsonwebtoken - jjwt-impl - 0.11.5 - runtime - - - io.jsonwebtoken - jjwt-jackson - 0.11.5 - runtime - - - - - - - redis.clients - jedis - 5.1.0 - - - - org.springframework.boot - spring-boot-starter-data-redis - 3.2.4 - - net.logstash.logback diff --git a/resources.yaml b/resources.yaml deleted file mode 100644 index c4edba2..0000000 --- a/resources.yaml +++ /dev/null @@ -1,44 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: user-service - labels: - app: user-service -spec: - replicas: 1 - selector: - matchLabels: - app: user-service - template: - metadata: - labels: - app: user-service - spec: - containers: - - name: user-service - image: datuits/devops-user-service:latest - ports: - - containerPort: 8081 - env: - - name: SPRING_DATA_MONGODB_URI - value: mongodb://user-mongo-service:27017/user-service - resources: - requests: - memory: "32Mi" - cpu: "0.2" - limits: - memory: "64Mi" - cpu: "0.4" ---- -apiVersion: v1 -kind: Service -metadata: - name: user-service -spec: - selector: - app: user-service - ports: - - protocol: TCP - port: 8081 - targetPort: 8081 - type: NodePort \ No newline at end of file diff --git a/src/main/java/com/programming/userService/config/JwtRequestFilter.java b/src/main/java/com/programming/userService/config/JwtRequestFilter.java deleted file mode 100644 index 787c269..0000000 --- a/src/main/java/com/programming/userService/config/JwtRequestFilter.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.programming.userService.config; - -import com.programming.userService.util.JwtUtil; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; -import org.springframework.stereotype.Component; -import org.springframework.web.filter.OncePerRequestFilter; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; - -import java.io.IOException; - -@Component -public class JwtRequestFilter extends OncePerRequestFilter { - - @Autowired - private UserDetailsService userDetailsService; - - @Autowired - private JwtUtil jwtUtil; - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) - throws ServletException, IOException { - - final String authorizationHeader = request.getHeader("Authorization"); - - String username = null; - String jwt = null; - - if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) { - jwt = authorizationHeader.substring(7); - username = jwtUtil.extractUsername(jwt); - } - - if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { - - UserDetails userDetails = this.userDetailsService.loadUserByUsername(username); - - if (jwtUtil.validateToken(jwt, userDetails)) { - - UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken( - userDetails, null, userDetails.getAuthorities()); - usernamePasswordAuthenticationToken - .setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); - SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken); - } - } - chain.doFilter(request, response); - } -} \ No newline at end of file diff --git a/src/main/java/com/programming/userService/config/RedisConfig.java b/src/main/java/com/programming/userService/config/RedisConfig.java deleted file mode 100644 index e56cce5..0000000 --- a/src/main/java/com/programming/userService/config/RedisConfig.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.programming.userService.config; - -import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; -import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.redis.connection.RedisConnectionFactory; -import org.springframework.data.redis.connection.RedisStandaloneConfiguration; -import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.serializer.StringRedisSerializer; -import org.springframework.beans.factory.annotation.Value; -@Configuration -@EnableRedisRepositories -public class RedisConfig { - - @Value("${spring.redis.host}") - private String redisHost; - - @Value("${spring.redis.port}") - private int redisPort; - - @Value("${spring.redis.password}") - private String redisPassword; - - @Bean - public RedisConnectionFactory redisConnectionFactory() { - RedisStandaloneConfiguration redisConfig = new RedisStandaloneConfiguration(); - redisConfig.setHostName(redisHost); - redisConfig.setPort(redisPort); - redisConfig.setPassword(redisPassword); - - return new LettuceConnectionFactory(redisConfig); - } - - - @Bean - public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) { - RedisTemplate template = new RedisTemplate<>(); - template.setConnectionFactory(connectionFactory); - template.setKeySerializer(new StringRedisSerializer()); - template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); - return template; - } -} diff --git a/src/main/java/com/programming/userService/config/SecurityConfig.java b/src/main/java/com/programming/userService/config/SecurityConfig.java index 8b60bb0..998a2ec 100644 --- a/src/main/java/com/programming/userService/config/SecurityConfig.java +++ b/src/main/java/com/programming/userService/config/SecurityConfig.java @@ -12,7 +12,6 @@ import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import java.util.ArrayList; import java.util.Collection; @@ -22,26 +21,25 @@ public class SecurityConfig { @Bean public SecurityFilterChain defaultFilterChain(HttpSecurity httpSecurity) throws Exception { - JwtRequestFilter jwtRequestFilter = new JwtRequestFilter(); return httpSecurity .csrf(csrf -> csrf.disable()) - .authorizeHttpRequests(auth -> auth.requestMatchers("/user/register", "/user/error").permitAll() - // .requestMatchers("/listUser").permitAll() - .requestMatchers("/user/login2").permitAll() - .requestMatchers("/user/login3").permitAll() - .requestMatchers("/user/listUserbyId/**").permitAll() - .requestMatchers("/user/hello-world").permitAll() - .requestMatchers("/user/send-verification-email").permitAll() - .requestMatchers("/user/logout").permitAll() - .requestMatchers("/user/listUserbyUsername").permitAll() - .requestMatchers("/user/listUserbyId/**").permitAll() - .requestMatchers("/user/updateProfile/**").permitAll() - .requestMatchers("/user/changePassword/**").permitAll() - .requestMatchers("/user/listUser").permitAll() - - .requestMatchers("/user/").authenticated() + .authorizeHttpRequests(auth -> auth.requestMatchers("/register", "/error").permitAll() + .requestMatchers("/listUser").permitAll() + .requestMatchers("/video/upload").permitAll() + .requestMatchers("/video/list").permitAll() + .requestMatchers("/file/upload").permitAll() + .requestMatchers("/file/list").permitAll() + .requestMatchers("/file/downloadZipFile").permitAll() + .requestMatchers("/comments/upload").permitAll() + .requestMatchers("/comments/**").permitAll() + .requestMatchers("/video/**").permitAll() + .requestMatchers("/login2").permitAll() + .requestMatchers("/listUserbyId/**").permitAll() + .requestMatchers("/updateProfile/**").permitAll() + .requestMatchers("/send-verification-email").permitAll() + .requestMatchers("/hello-world").permitAll() + .requestMatchers("/**").permitAll() .anyRequest().authenticated()) - .addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class) .httpBasic(Customizer.withDefaults()) .formLogin(Customizer.withDefaults()) .build(); diff --git a/src/main/java/com/programming/userService/controller/UserController.java b/src/main/java/com/programming/userService/controller/UserController.java index aa5496a..ad9f4c2 100644 --- a/src/main/java/com/programming/userService/controller/UserController.java +++ b/src/main/java/com/programming/userService/controller/UserController.java @@ -22,10 +22,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; - -import org.slf4j.MDC; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cache.annotation.Cacheable; import org.springframework.mail.MailException; import org.springframework.mail.SimpleMailMessage; import org.springframework.mail.javamail.JavaMailSender; @@ -33,13 +30,10 @@ import java.util.Date; import java.util.HashMap; import java.util.Map; -import com.programming.userService.util.JwtUtil; -import com.programming.userService.entity.CustomUserDetails; -import com.programming.userService.entity.AuthUser; -import org.springframework.data.redis.core.RedisTemplate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.slf4j.MDC; @RestController @AllArgsConstructor @@ -48,13 +42,8 @@ public class UserController { private final AuthUserRepository userRepository; private final PasswordEncoder passwordEncoder; - private final RedisTemplate redisTemplate; - private static final String USER_CACHE = "USER_CACHE"; private static final Logger logger = LoggerFactory.getLogger(UserController.class); - @Autowired - private JwtUtil jwtUtil; - @Autowired private JavaMailSender javaMailSender; @@ -67,9 +56,6 @@ public String getEmail() { } @GetMapping("/") public String getServiceName(){ - //ELK - MDC.put("type", "userservice"); - logger.info("User Service Start"); return "User Service"; } @@ -115,34 +101,12 @@ public ResponseEntity registerUser(@RequestBody AuthUser user) { private byte[] getDefaultAvatar() throws IOException { String defaultAvatarPath = "/app/images/avatar.png"; // Path inside the Docker container - //String defaultAvatarPath = "src/main/java/com/programming/userService/images/avatar.png"; // Path on local machine Path path = Paths.get(defaultAvatarPath); return Files.readAllBytes(path); } - @PostMapping("/login3") - public ResponseEntity loginUser3(@RequestBody AuthUser user) { - try { - AuthUser userFromDb = userRepository.findByUsername(user.getUsername()) - .orElseThrow(() -> new Exception("User not found")); - - if (passwordEncoder.matches(user.getPassword(), userFromDb.getPassword())) { - // Chuyển AuthUser sang CustomUserDetails - CustomUserDetails userDetails = new CustomUserDetails(userFromDb); - String token = jwtUtil.generateToken(userDetails); // Tạo JWT token với UserDetails - Map response = new HashMap<>(); - response.put("token", token); - return ResponseEntity.ok(response); - } else { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid credentials"); - } - } catch (Exception e) { - return ResponseEntity.internalServerError().body(e.getMessage()); - } - } - @PostMapping("/login2") - public ResponseEntity loginUser2(@RequestBody AuthUser user) { + public ResponseEntity loginUser(@RequestBody AuthUser user) { try { AuthUser userFromDb = userRepository.findByUsername(user.getUsername()) .orElseThrow(() -> new Exception("User not found")); @@ -186,27 +150,10 @@ public ResponseEntity listUserbyUsername(@RequestBody AuthUser user) { } } - @Cacheable(value = USER_CACHE, key = "#id") @GetMapping("/listUserbyId/{id}") public ResponseEntity listUserbyId(@PathVariable("id") String id) { try { - // Attempt to fetch user from Redis cache - AuthUser cachedUser = (AuthUser) redisTemplate.opsForHash().get(USER_CACHE, id); - - if (cachedUser != null) { - // If user is found in Redis, return it - return ResponseEntity.ok(cachedUser); - } - - // If user is not found in Redis, fetch from MongoDB - AuthUser user = userRepository.findById(id) - .orElseThrow(() -> new Exception("User not found")); - - // Cache the user in Redis - redisTemplate.opsForHash().put(USER_CACHE, id, user); - - // Return the user data - return ResponseEntity.ok(user); + return ResponseEntity.ok(userRepository.findById(id)); } catch (Exception e) { return ResponseEntity.internalServerError().body(e.getMessage()); } @@ -221,12 +168,9 @@ public ResponseEntity updateProfile(@PathVariable("id") String id, @RequestBody userFromDb.setFirstName(user.getFirstName()); userFromDb.setLastName(user.getLastName()); AuthUser save = userRepository.save(userFromDb); - String tempUserId = userFromDb.getId(); - MDC.put("type", "userservice"); MDC.put("action", "update-profile"); logger.info("UserID: " + userFromDb.getId()); - return ResponseEntity.ok(HttpStatus.OK); } catch (Exception e) { return ResponseEntity.internalServerError().body(e.getMessage()); @@ -246,11 +190,9 @@ public ResponseEntity changePassword(@PathVariable("id") String id, userFromDb.setPassword(passwordEncoder.encode(changePasswordRequest.getNewPassword())); AuthUser save = userRepository.save(userFromDb); - MDC.put("type", "userservice"); MDC.put("action", "change-password"); logger.info("UserID: " + userFromDb.getId()); - return ResponseEntity.ok("Password changed successfully"); } catch (Exception e) { return ResponseEntity.internalServerError().body(e.getMessage()); diff --git a/src/main/java/com/programming/userService/entity/AuthUser.java b/src/main/java/com/programming/userService/entity/AuthUser.java index b145c1b..0a031e4 100644 --- a/src/main/java/com/programming/userService/entity/AuthUser.java +++ b/src/main/java/com/programming/userService/entity/AuthUser.java @@ -7,12 +7,11 @@ import org.springframework.data.mongodb.core.mapping.Document; import java.util.Date; -import java.io.Serializable; @Data @Builder @Document("user") -public class AuthUser implements Serializable { +public class AuthUser { @Id private String id; @Indexed @@ -25,7 +24,4 @@ public class AuthUser implements Serializable { private byte[] avatar; private String email; private Date timestamp; - - - private static final long serialVersionUID = 1L; } diff --git a/src/main/java/com/programming/userService/entity/CustomUserDetails.java b/src/main/java/com/programming/userService/entity/CustomUserDetails.java deleted file mode 100644 index fe8a276..0000000 --- a/src/main/java/com/programming/userService/entity/CustomUserDetails.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.programming.userService.entity; - -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.userdetails.UserDetails; -import java.util.Collection; -import java.util.Collections; - -public class CustomUserDetails implements UserDetails { - - private final AuthUser authUser; - - public CustomUserDetails(AuthUser authUser) { - this.authUser = authUser; - } - - @Override - public Collection getAuthorities() { - return Collections.emptyList(); // Thêm danh sách quyền nếu cần thiết - } - - @Override - public String getPassword() { - return authUser.getPassword(); - } - - @Override - public String getUsername() { - return authUser.getUsername(); - } - - @Override - public boolean isAccountNonExpired() { - return true; - } - - @Override - public boolean isAccountNonLocked() { - return true; - } - - @Override - public boolean isCredentialsNonExpired() { - return true; - } - - @Override - public boolean isEnabled() { - return true; - } -} diff --git a/src/main/java/com/programming/userService/util/JwtUtil.java b/src/main/java/com/programming/userService/util/JwtUtil.java deleted file mode 100644 index c7ea8e8..0000000 --- a/src/main/java/com/programming/userService/util/JwtUtil.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.programming.userService.util; - -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.SignatureAlgorithm; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.stereotype.Service; - -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Function; - - -@Service -public class JwtUtil { - private String SECRET_KEY = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"; // Thay đổi SECRET_KEY với giá trị bảo mật của bạn - - public String extractUsername(String token) { - return extractClaim(token, Claims::getSubject); - } - - public Date extractExpiration(String token) { - return extractClaim(token, Claims::getExpiration); - } - - public T extractClaim(String token, Function claimsResolver) { - final Claims claims = extractAllClaims(token); - return claimsResolver.apply(claims); - } - - private Claims extractAllClaims(String token) { - return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody(); - } - - private Boolean isTokenExpired(String token) { - return extractExpiration(token).before(new Date()); - } - - public String generateToken(UserDetails userDetails) { - Map claims = new HashMap<>(); - return createToken(claims, userDetails.getUsername()); - } - - private String createToken(Map claims, String subject) { - return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis())) - .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // Token có hiệu lực trong 10 - // giờ - .signWith(SignatureAlgorithm.HS256, SECRET_KEY).compact(); - } - - public Boolean validateToken(String token, UserDetails userDetails) { - final String username = extractUsername(token); - return (username.equals(userDetails.getUsername()) && !isTokenExpired(token)); - } -} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 511a171..2705ff0 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -19,9 +19,6 @@ spring.mail.password=vkux umrv rtkd svsy spring.mail.properties.mail.smtp.auth=true spring.mail.properties.mail.smtp.starttls.enable=true + # logstash -logging.config=classpath:logback-spring.xml -# Redis -spring.redis.host=localhost -spring.redis.port=6379 -spring.redis.password= \ No newline at end of file +logging.config=classpath:logback-spring.xml \ No newline at end of file diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml index 6b0ebf7..982fc84 100644 --- a/src/main/resources/logback-spring.xml +++ b/src/main/resources/logback-spring.xml @@ -4,7 +4,6 @@ - diff --git a/src/test/java/TestUserService.java b/src/test/java/TestUserService.java index 672144b..edff7ed 100644 --- a/src/test/java/TestUserService.java +++ b/src/test/java/TestUserService.java @@ -1,19 +1,19 @@ -// import org.junit.jupiter.api.Test; -// import org.springframework.beans.factory.annotation.Autowired; -// import org.springframework.test.web.servlet.MockMvc; -// import org.springframework.boot.test.context.SpringBootTest; -// import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -// import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -// import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -// @SpringBootTest(classes = com.programming.userService.StreamingApplication.class) -// @AutoConfigureMockMvc -// public class TestUserService { -// @Autowired -// private MockMvc mockMvc; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +@SpringBootTest(classes = com.programming.userService.StreamingApplication.class) +@AutoConfigureMockMvc +public class TestUserService { + @Autowired + private MockMvc mockMvc; -// @Test -// public void testGetServiceName() throws Exception { -// mockMvc.perform(get("/user/")) -// .andExpect(status().isOk()); -// } -// } + @Test + public void testGetServiceName() throws Exception { + mockMvc.perform(get("/user/")) + .andExpect(status().isOk()); + } +}