From b463379e523d8e336e425ff10ede2fa5cd54bab3 Mon Sep 17 00:00:00 2001 From: Yesitha Sathsara <60166952+yesitha@users.noreply.github.com> Date: Fri, 27 Dec 2024 05:53:58 +0530 Subject: [PATCH 1/3] getVideoMaterialSignedUrl method implimentation --- .../controller/StudentController.java | 9 ++- .../java/com/itgura/util/URIPathVariable.java | 1 + .../main/java/com/itgura/util/URIPrefix.java | 1 + .../itgura/controller/MaterialController.java | 13 ++++ .../src/main/resources/application.properties | 11 ++- .../com/itgura/request/SignedUrlRequest.java | 20 +++++ .../com/itgura/service/MaterialService.java | 4 + .../service/impl/MaterialServiceImpl.java | 73 ++++++++++++++++++- 8 files changed, 124 insertions(+), 8 deletions(-) create mode 100644 resource-management/resource-management-dao/src/main/java/com/itgura/request/SignedUrlRequest.java diff --git a/auth-service/src/main/java/com/itgura/authservice/controller/StudentController.java b/auth-service/src/main/java/com/itgura/authservice/controller/StudentController.java index 80286eb..4ad1380 100644 --- a/auth-service/src/main/java/com/itgura/authservice/controller/StudentController.java +++ b/auth-service/src/main/java/com/itgura/authservice/controller/StudentController.java @@ -1,13 +1,16 @@ package com.itgura.authservice.controller; import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.CrossOrigin; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @RestController @CrossOrigin(origins = "*", allowedHeaders = "*") @RequestMapping("api/v1/student") @RequiredArgsConstructor + + public class StudentController { + + + } diff --git a/lib-global/src/main/java/com/itgura/util/URIPathVariable.java b/lib-global/src/main/java/com/itgura/util/URIPathVariable.java index ccd7412..103bcc9 100644 --- a/lib-global/src/main/java/com/itgura/util/URIPathVariable.java +++ b/lib-global/src/main/java/com/itgura/util/URIPathVariable.java @@ -17,4 +17,5 @@ public class URIPathVariable { public static final String PAYMENT_SERVICE = "/payment-service"; public static final String QUIZ_SERVICE = "/quiz-service"; public static final String CONTENT_ID = "/{contentId}"; + public static final String REF = "/{ref}"; } diff --git a/lib-global/src/main/java/com/itgura/util/URIPrefix.java b/lib-global/src/main/java/com/itgura/util/URIPrefix.java index dd0d7ba..995ba5f 100644 --- a/lib-global/src/main/java/com/itgura/util/URIPrefix.java +++ b/lib-global/src/main/java/com/itgura/util/URIPrefix.java @@ -27,4 +27,5 @@ public class URIPrefix { public static final String GET_ACCESS_TIME_DURATION = "/get-access-time-duration"; public static final String GET_PRICE = "/get-price"; public static final String UPDATE_TAGS = "/update-tags"; + public static final String GET_VIDEO_Signed_Url = "/get-video-signed-url"; } diff --git a/resource-management/resource-management-all/src/main/java/com/itgura/controller/MaterialController.java b/resource-management/resource-management-all/src/main/java/com/itgura/controller/MaterialController.java index 109ad5a..7bb7dde 100644 --- a/resource-management/resource-management-all/src/main/java/com/itgura/controller/MaterialController.java +++ b/resource-management/resource-management-all/src/main/java/com/itgura/controller/MaterialController.java @@ -3,6 +3,7 @@ import com.itgura.dto.AppRequest; import com.itgura.dto.AppResponse; import com.itgura.request.MaterialRequest; +import com.itgura.request.SignedUrlRequest; import com.itgura.service.MaterialService; import com.itgura.util.ResourceManagementURI; import com.itgura.util.URIPathVariable; @@ -49,5 +50,17 @@ public AppResponse deleteMaterial(@PathVariable UUID materialId) { } } + @GetMapping(ResourceManagementURI.MATERIAL + URIPrefix.GET_VIDEO_Signed_Url) + public AppResponse getVideoMaterialSignedUrl(@RequestBody AppRequest request) { + try { + String s = materialService.getVideoMaterialSignedUrl(request.getData()); + return AppResponse.ok(s); + } catch (Exception e) { + e.printStackTrace(); + return AppResponse.error(null, e.getMessage(), "Server Error", "500", ""); + } + } + + } diff --git a/resource-management/resource-management-all/src/main/resources/application.properties b/resource-management/resource-management-all/src/main/resources/application.properties index c883ae1..70b6c44 100644 --- a/resource-management/resource-management-all/src/main/resources/application.properties +++ b/resource-management/resource-management-all/src/main/resources/application.properties @@ -3,4 +3,13 @@ spring.application.name=resource-management logging.level.org.springframework.core.env=DEBUG spring.profiles.active=dev spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration -logging.level.org.springframework.security=DEBUG \ No newline at end of file +logging.level.org.springframework.security=DEBUG + +#CloudFront +#cloudfront.domain=https://yourdistribution.cloudfront.net +#cloudfront.keyPairId=YOUR_KEY_PAIR_ID +#cloudfront.privateKeyPath=classpath:private-key.pem # Ensure the private key file is in the classpath + +cloudfront.domain=https://yourdistribution.cloudfront.net +cloudfront.keyPairId=YOUR_KEY_PAIR_ID +cloudfront.privateKeyPath=classpath:private-key.pem # Ensure the private key file is in the classpath \ No newline at end of file diff --git a/resource-management/resource-management-dao/src/main/java/com/itgura/request/SignedUrlRequest.java b/resource-management/resource-management-dao/src/main/java/com/itgura/request/SignedUrlRequest.java new file mode 100644 index 0000000..e890164 --- /dev/null +++ b/resource-management/resource-management-dao/src/main/java/com/itgura/request/SignedUrlRequest.java @@ -0,0 +1,20 @@ +package com.itgura.request; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class SignedUrlRequest { + @JsonProperty("filePath") + String filePath; + @JsonProperty("userIpAddress") + String userIpAddress; + @JsonProperty("expiresInHours") + int expiresInHours; +} diff --git a/resource-management/resource-management-service/src/main/java/com/itgura/service/MaterialService.java b/resource-management/resource-management-service/src/main/java/com/itgura/service/MaterialService.java index 6260ecc..08f6f18 100644 --- a/resource-management/resource-management-service/src/main/java/com/itgura/service/MaterialService.java +++ b/resource-management/resource-management-service/src/main/java/com/itgura/service/MaterialService.java @@ -1,10 +1,12 @@ package com.itgura.service; +import com.itgura.dto.AppRequest; import com.itgura.exception.ApplicationException; import com.itgura.exception.BadRequestRuntimeException; import com.itgura.exception.ValueNotExistException; import com.itgura.request.MaterialRequest; +import com.itgura.request.SignedUrlRequest; import com.itgura.response.dto.MaterialResponseDto; import javax.security.auth.login.CredentialNotFoundException; @@ -15,6 +17,8 @@ public interface MaterialService { String addMaterial(UUID sessionId, MaterialRequest request) throws ApplicationException, CredentialNotFoundException, BadRequestRuntimeException; String updateMaterial(UUID materialId, MaterialRequest request) throws ApplicationException, CredentialNotFoundException, BadRequestRuntimeException; String deleteMaterial(UUID materialId) throws ApplicationException, CredentialNotFoundException, BadRequestRuntimeException; + + String getVideoMaterialSignedUrl(SignedUrlRequest signedUrlRequest) throws Exception; // MaterialResponseDto findMaterialById(UUID materialId) throws ApplicationException, CredentialNotFoundException, BadRequestRuntimeException; // List findAllMaterial(UUID sessionId) throws ApplicationException, CredentialNotFoundException, BadRequestRuntimeException; } diff --git a/resource-management/resource-management-service/src/main/java/com/itgura/service/impl/MaterialServiceImpl.java b/resource-management/resource-management-service/src/main/java/com/itgura/service/impl/MaterialServiceImpl.java index fd2ee60..5e70df9 100644 --- a/resource-management/resource-management-service/src/main/java/com/itgura/service/impl/MaterialServiceImpl.java +++ b/resource-management/resource-management-service/src/main/java/com/itgura/service/impl/MaterialServiceImpl.java @@ -10,6 +10,7 @@ import com.itgura.repository.MaterialTypeRepository; import com.itgura.repository.SessionRepository; import com.itgura.request.MaterialRequest; +import com.itgura.request.SignedUrlRequest; import com.itgura.request.dto.UserResponseDto; import com.itgura.response.dto.MaterialResponseDto; import com.itgura.response.dto.SessionResponseDto; @@ -20,14 +21,25 @@ import com.itgura.util.UserUtil; import jakarta.transaction.Transactional; import jakarta.ws.rs.ForbiddenException; + import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import org.springframework.util.ResourceUtils; import javax.security.auth.login.CredentialNotFoundException; -import java.util.Date; -import java.util.List; -import java.util.Optional; -import java.util.UUID; +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.PrivateKey; +import java.security.Signature; +import java.security.spec.PKCS8EncodedKeySpec; +import java.time.Instant; +import java.util.*; + + + + @Service public class MaterialServiceImpl implements MaterialService { @@ -40,6 +52,16 @@ public class MaterialServiceImpl implements MaterialService { @Autowired private MaterialRepository materialRepository; + @Value("${cloudfront.domain}") + private String cloudFrontDomain; + + @Value("${cloudfront.keyPairId}") + private String keyPairId; + + @Value("${cloudfront.privateKeyPath}") + private String privateKeyPath; + + @Override @Transactional @@ -131,6 +153,49 @@ public String deleteMaterial(UUID materialId) throws ApplicationException, Crede } } + // Generate signed url for video material + @Override + public String getVideoMaterialSignedUrl(SignedUrlRequest signedUrlRequest) throws Exception { + + long expirationTime = Instant.now().getEpochSecond() + (signedUrlRequest.getExpiresInHours() * 3600L); + + // Define policy with IP restriction and expiration time + String policy = String.format( + "{\"Statement\":[{\"Resource\":\"%s/%s\",\"Condition\":{\"DateLessThan\":{\"AWS:EpochTime\":%d},\"IpAddress\":{\"AWS:SourceIp\":\"%s\"}}}]}", + cloudFrontDomain, signedUrlRequest.getFilePath(), expirationTime, signedUrlRequest.getUserIpAddress()); + // Base64 encode the policy + String base64EncodedPolicy = Base64.getEncoder().encodeToString(policy.getBytes()); + + // Sign the policy with the private key + String signature = signPolicyWithPrivateKey(base64EncodedPolicy); + + + // Construct the signed URL + return String.format("%s/%s?Policy=%s&Signature=%s&Key-Pair-Id=%s", + cloudFrontDomain, signedUrlRequest.getFilePath(), + urlEncode(base64EncodedPolicy), + urlEncode(signature), + keyPairId); + + } + + private String signPolicyWithPrivateKey(String base64EncodedPolicy) throws Exception { + byte[] privateKeyBytes = Files.readAllBytes(Paths.get(privateKeyPath)); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes); + PrivateKey privateKey = java.security.KeyFactory.getInstance("RSA").generatePrivate(keySpec); + + Signature signature = Signature.getInstance("SHA1withRSA"); + signature.initSign(privateKey); + signature.update(base64EncodedPolicy.getBytes()); + byte[] signedBytes = signature.sign(); + + return Base64.getEncoder().encodeToString(signedBytes); + } + + + private String urlEncode(String value) { + return value.replace("+", "-").replace("=", "_").replace("/", "~"); + } // @Override // public MaterialResponseDto findMaterialById(UUID materialId) throws ApplicationException, CredentialNotFoundException, BadRequestRuntimeException { From 9a8dc32234d4dab26523d5aa635dadfa05bfcb8c Mon Sep 17 00:00:00 2001 From: Yesitha Sathsara <60166952+yesitha@users.noreply.github.com> Date: Fri, 27 Dec 2024 05:59:25 +0530 Subject: [PATCH 2/3] security bug fix, changeUserRole Admin Controller implemented --- .../config/SecurityConfiguration.java | 6 ++--- .../controller/AdminController.java | 26 +++++++++++++++++-- .../controller/AuthenticationController.java | 4 +-- .../controller/StudentController.java | 7 ++++- .../controller/TeacherController.java | 8 +++++- .../dto/request/changeRoleRequest.java | 4 +++ .../services/AuthenticationService.java | 17 ++++++++++-- .../src/main/resources/application.properties | 13 ++++++++++ 8 files changed, 74 insertions(+), 11 deletions(-) diff --git a/auth-service/src/main/java/com/itgura/authservice/config/SecurityConfiguration.java b/auth-service/src/main/java/com/itgura/authservice/config/SecurityConfiguration.java index dbd3e97..bdbcea1 100644 --- a/auth-service/src/main/java/com/itgura/authservice/config/SecurityConfiguration.java +++ b/auth-service/src/main/java/com/itgura/authservice/config/SecurityConfiguration.java @@ -34,9 +34,9 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti "/webjars/**") .permitAll() - .requestMatchers("/api/v1/admin/**").hasAnyAuthority(Role.ADMIN.name()) - .requestMatchers("/api/v1/student/**").hasAnyAuthority(Role.STUDENT.name()) - .requestMatchers("/api/v1/teacher/**").hasAnyAuthority(Role.TEACHER.name()) + .requestMatchers("/api/v1/auth-service-protected/admin/**").hasAnyAuthority(Role.ADMIN.name()) + .requestMatchers("/api/v1/auth-service-protected/student/**").hasAnyAuthority(Role.STUDENT.name(), Role.ADMIN.name()) + .requestMatchers("/api/v1/auth-service-protected/teacher/**").hasAnyAuthority(Role.TEACHER.name(), Role.ADMIN.name()) .anyRequest().authenticated()) .sessionManagement(manager->manager.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) diff --git a/auth-service/src/main/java/com/itgura/authservice/controller/AdminController.java b/auth-service/src/main/java/com/itgura/authservice/controller/AdminController.java index f73c511..4261016 100644 --- a/auth-service/src/main/java/com/itgura/authservice/controller/AdminController.java +++ b/auth-service/src/main/java/com/itgura/authservice/controller/AdminController.java @@ -1,19 +1,41 @@ package com.itgura.authservice.controller; +import com.itgura.authservice.dto.request.changeRoleRequest; +import com.itgura.authservice.services.AuthenticationService; +import com.itgura.dto.AppResponse; +import com.itgura.exception.ApplicationException; +import com.itgura.exception.ValueNotExistException; +import com.itgura.exception.ValueNotFoundException; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @CrossOrigin(origins = "*", allowedHeaders = "*") @RestController -@RequestMapping("api/v1/admin") - +@RequestMapping("api/v1/auth-service-protected/admin") +@RequiredArgsConstructor public class AdminController { + @Autowired + private final AuthenticationService authenticationService; + @PostMapping("/testAdmin") public ResponseEntity seyHello() { return ResponseEntity.ok("Hello from Admin Controller!"); } + @PostMapping("/changeRole") + public AppResponse changeUserRole(@RequestBody changeRoleRequest request) { + try { + return AppResponse.ok(authenticationService.changeUserRole(request)); + + } catch (ValueNotExistException e) { + return AppResponse.error(null, "Value not found", "404", "", e.getMessage()); + + } + + } + } diff --git a/auth-service/src/main/java/com/itgura/authservice/controller/AuthenticationController.java b/auth-service/src/main/java/com/itgura/authservice/controller/AuthenticationController.java index 5cd4dd4..8240cb3 100644 --- a/auth-service/src/main/java/com/itgura/authservice/controller/AuthenticationController.java +++ b/auth-service/src/main/java/com/itgura/authservice/controller/AuthenticationController.java @@ -69,8 +69,8 @@ public AppResponse validateToken(@RequestParam("token") String token){ //ToDo: Remove this endpoint after testing - @PostMapping("/changeUserRole") - public AppResponse changeUserRole(@RequestBody changeRoleRequest role) { + @PostMapping("/changeUserRoleTemp/{role}") + public AppResponse changeUserRoleTemp(@PathVariable String role) { { try { return AppResponse.ok(authenticationService.changeUserRole(role)); diff --git a/auth-service/src/main/java/com/itgura/authservice/controller/StudentController.java b/auth-service/src/main/java/com/itgura/authservice/controller/StudentController.java index 4ad1380..f90ab8d 100644 --- a/auth-service/src/main/java/com/itgura/authservice/controller/StudentController.java +++ b/auth-service/src/main/java/com/itgura/authservice/controller/StudentController.java @@ -5,12 +5,17 @@ @RestController @CrossOrigin(origins = "*", allowedHeaders = "*") -@RequestMapping("api/v1/student") +@RequestMapping("api/v1/auth-service-protected/student") @RequiredArgsConstructor public class StudentController { + @PostMapping("/testStudent") + public String seyHello() { + return "Hello from Student Controller!"; + } + } diff --git a/auth-service/src/main/java/com/itgura/authservice/controller/TeacherController.java b/auth-service/src/main/java/com/itgura/authservice/controller/TeacherController.java index a3c1798..01a50b0 100644 --- a/auth-service/src/main/java/com/itgura/authservice/controller/TeacherController.java +++ b/auth-service/src/main/java/com/itgura/authservice/controller/TeacherController.java @@ -2,12 +2,18 @@ import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @CrossOrigin(origins = "*", allowedHeaders = "*") -@RequestMapping("api/v1/teacher") +@RequestMapping("api/v1/auth-service-protected/admin/teacher") @RequiredArgsConstructor public class TeacherController { + + @PostMapping("/testTeacher") + public String seyHello() { + return "Hello from Teacher Controller!"; + } } diff --git a/auth-service/src/main/java/com/itgura/authservice/dto/request/changeRoleRequest.java b/auth-service/src/main/java/com/itgura/authservice/dto/request/changeRoleRequest.java index 853f7f6..4bfe0ad 100644 --- a/auth-service/src/main/java/com/itgura/authservice/dto/request/changeRoleRequest.java +++ b/auth-service/src/main/java/com/itgura/authservice/dto/request/changeRoleRequest.java @@ -1,6 +1,7 @@ package com.itgura.authservice.dto.request; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -11,5 +12,8 @@ @AllArgsConstructor @NoArgsConstructor public class changeRoleRequest { + @JsonProperty("username(email)") + private String username; + @JsonProperty("changeRole") private String changeRole; } diff --git a/auth-service/src/main/java/com/itgura/authservice/services/AuthenticationService.java b/auth-service/src/main/java/com/itgura/authservice/services/AuthenticationService.java index 4d134fb..f8cdd80 100644 --- a/auth-service/src/main/java/com/itgura/authservice/services/AuthenticationService.java +++ b/auth-service/src/main/java/com/itgura/authservice/services/AuthenticationService.java @@ -81,7 +81,7 @@ public Boolean validateToken(String token) { return jwtService.validateToken(token); } - public String changeUserRole( changeRoleRequest role) throws ApplicationException { + public String changeUserRole( String role) throws ApplicationException { String authorizationHeader = UserUtil.extractToken(); @@ -99,7 +99,7 @@ public String changeUserRole( changeRoleRequest role) throws ApplicationExceptio Optional user = userRepository.findByEmail(email); if (user.isPresent()) { - user.get().setRole(Role.valueOf(role.getChangeRole())); + user.get().setRole(Role.valueOf(role)); userRepository.save(user.get()); return "Role changed successfully"; }else { @@ -111,4 +111,17 @@ public String changeUserRole( changeRoleRequest role) throws ApplicationExceptio } } + + public String changeUserRole(changeRoleRequest request) throws ValueNotExistException { + String email = request.getUsername(); + Optional user = userRepository.findByEmail(email); + + if (user.isPresent()) { + user.get().setRole(Role.valueOf(request.getChangeRole())); + userRepository.save(user.get()); + return "Role changed successfully"; + }else { + throw new ValueNotExistException("User: "+email+ " not found"); + } + } } diff --git a/lms-gateway/src/main/resources/application.properties b/lms-gateway/src/main/resources/application.properties index ab89c37..5de35e4 100644 --- a/lms-gateway/src/main/resources/application.properties +++ b/lms-gateway/src/main/resources/application.properties @@ -68,6 +68,19 @@ spring.cloud.gateway.routes[2].filters[0].name=RewritePath spring.cloud.gateway.routes[2].filters[0].args.regexp=/auth-service/(?.*) spring.cloud.gateway.routes[2].filters[0].args.replacement=/api/v1/auth-service/${remaining} +#auth-service-protected +spring.cloud.gateway.routes[5].id=auth-service-protected +spring.cloud.gateway.routes[5].uri=lb://auth-service +spring.cloud.gateway.routes[5].predicates[0].name=Path +spring.cloud.gateway.routes[5].predicates[0].args.pattern=/auth-service-protected/** + + +spring.cloud.gateway.routes[5].filters[0].name=RewritePath +spring.cloud.gateway.routes[5].filters[0].args.regexp=/auth-service-protected/(?.*) +spring.cloud.gateway.routes[5].filters[0].args.replacement=/api/v1/auth-service-protected/${remaining} + + + #payment-service spring.cloud.gateway.routes[3].id=payment-service spring.cloud.gateway.routes[3].uri=lb://payment-service From 53ee5273b9d6d25da0baa18856687b305ffb3ef5 Mon Sep 17 00:00:00 2001 From: Yesitha Sathsara <60166952+yesitha@users.noreply.github.com> Date: Fri, 27 Dec 2024 06:02:44 +0530 Subject: [PATCH 3/3] add image upload to lessons and class. --- .../itgura/controller/ClassController.java | 15 +++++---- .../itgura/controller/LessonController.java | 13 ++++---- .../src/main/resources/application.properties | 4 +++ .../main/java/com/itgura/entity/Content.java | 3 ++ .../java/com/itgura/entity/QuizImage.java | 4 +-- .../itgura/response/dto/ClassResponseDto.java | 2 ++ .../response/dto/LessonResponseDto.java | 2 ++ .../response/dto/mapper/ClassMapper.java | 14 +++++++++ .../response/dto/mapper/LessonMapper.java | 16 ++++++++++ .../java/com/itgura/service/ClassService.java | 5 +-- .../com/itgura/service/LessonService.java | 5 +-- .../itgura/service/impl/ClassServiceImpl.java | 31 +++++++++++++++---- .../service/impl/LessonServiceImpl.java | 27 ++++++++++++++-- 13 files changed, 115 insertions(+), 26 deletions(-) diff --git a/resource-management/resource-management-all/src/main/java/com/itgura/controller/ClassController.java b/resource-management/resource-management-all/src/main/java/com/itgura/controller/ClassController.java index be07228..8e43387 100644 --- a/resource-management/resource-management-all/src/main/java/com/itgura/controller/ClassController.java +++ b/resource-management/resource-management-all/src/main/java/com/itgura/controller/ClassController.java @@ -12,6 +12,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import java.util.List; import java.util.UUID; @@ -34,18 +35,20 @@ public AppResponse> getAllClasses() { } } @PreAuthorize("hasAuthority('ROLE_ADMIN')") - @PostMapping(ResourceManagementURI.CLASS + ResourceManagementURI.CREATE) - public AppResponse createClass(@RequestBody AppRequest request) { + @PostMapping(value = ResourceManagementURI.CLASS + ResourceManagementURI.CREATE,consumes = "multipart/form-data") + public AppResponse createClass(@RequestPart("request") AppRequest request,@RequestPart(value = "image", required = false) MultipartFile file) { try { - String response = this.classService.create(request.getData()); + String response = this.classService.create(request.getData(), file); return AppResponse.ok(response); } catch (Exception e) { return AppResponse.error(null, e.getMessage(), "Server Error", "500", ""); } } + + //getClassFee @GetMapping(ResourceManagementURI.CLASS + ResourceManagementURI.GET_CLASS_FEE+URIPrefix.BY_ID) @@ -68,10 +71,10 @@ public AppResponse getClass(@PathVariable UUID id) { } } - @PatchMapping(ResourceManagementURI.CLASS + URIPrefix.UPDATE + URIPrefix.ID) - public AppResponse updateClass(@PathVariable("id") UUID id, @RequestBody AppRequest request) { + @PatchMapping(value=ResourceManagementURI.CLASS + URIPrefix.UPDATE + URIPrefix.ID,consumes = "multipart/form-data") + public AppResponse updateClass(@PathVariable("id") UUID id, @RequestPart("request") AppRequest request,@RequestPart(value = "image", required = false) MultipartFile file) { try { - String update = this.classService.update(id, request.getData()); + String update = this.classService.update(id, request.getData(), file); return AppResponse.ok(update); } catch (Exception e) { return AppResponse.error(null, e.getMessage(), "Server Error", "500", ""); diff --git a/resource-management/resource-management-all/src/main/java/com/itgura/controller/LessonController.java b/resource-management/resource-management-all/src/main/java/com/itgura/controller/LessonController.java index a50a536..33043d1 100644 --- a/resource-management/resource-management-all/src/main/java/com/itgura/controller/LessonController.java +++ b/resource-management/resource-management-all/src/main/java/com/itgura/controller/LessonController.java @@ -11,6 +11,7 @@ import com.itgura.util.URIPrefix; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import java.util.List; import java.util.UUID; @@ -21,11 +22,11 @@ public class LessonController { @Autowired private LessonService lessonService; - @PostMapping(ResourceManagementURI.LESSON + URIPrefix.CREATE) - public AppResponse createLesson(@RequestBody AppRequest request) { + @PostMapping(value = ResourceManagementURI.LESSON + URIPrefix.CREATE, consumes = "multipart/form-data") + public AppResponse createLesson(@RequestPart("request")AppRequest request,@RequestPart (value = "image", required = false) MultipartFile file) { try { - String s = lessonService.saveLesson( request.getData()); + String s = lessonService.saveLesson( request.getData(), file); return AppResponse.ok(s); } catch (ValueNotExistException e) { return AppResponse.error(null, e.getMessage(), "Value Not Found", "404", ""); @@ -33,10 +34,10 @@ public AppResponse createLesson(@RequestBody AppRequest r return AppResponse.error(null, e.getMessage(), "Server Error", "500", ""); } } - @PatchMapping(ResourceManagementURI.LESSON + URIPrefix.UPDATE+ URIPrefix.ID) - public AppResponse updateLesson(@RequestBody AppRequest request, @PathVariable UUID id){ + @PatchMapping(value=ResourceManagementURI.LESSON + URIPrefix.UPDATE+ URIPrefix.ID, consumes = "multipart/form-data") + public AppResponse updateLesson(@RequestPart("request") AppRequest request, @PathVariable UUID id,@RequestPart (value = "image", required = false) MultipartFile file){ try { - String s = lessonService.updateLesson(request.getData(), id); + String s = lessonService.updateLesson(request.getData(), id, file); return AppResponse.ok(s); } catch (ValueNotExistException e) { return AppResponse.error(null, e.getMessage(), "Value Not Found", "404", ""); diff --git a/resource-management/resource-management-all/src/main/resources/application.properties b/resource-management/resource-management-all/src/main/resources/application.properties index 70b6c44..5defccc 100644 --- a/resource-management/resource-management-all/src/main/resources/application.properties +++ b/resource-management/resource-management-all/src/main/resources/application.properties @@ -5,6 +5,10 @@ spring.profiles.active=dev spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration logging.level.org.springframework.security=DEBUG +spring.servlet.multipart.enabled=true +spring.servlet.multipart.max-file-size=10MB +spring.servlet.multipart.max-request-size=10MB + #CloudFront #cloudfront.domain=https://yourdistribution.cloudfront.net #cloudfront.keyPairId=YOUR_KEY_PAIR_ID diff --git a/resource-management/resource-management-dao/src/main/java/com/itgura/entity/Content.java b/resource-management/resource-management-dao/src/main/java/com/itgura/entity/Content.java index 29aaee5..f4df839 100644 --- a/resource-management/resource-management-dao/src/main/java/com/itgura/entity/Content.java +++ b/resource-management/resource-management-dao/src/main/java/com/itgura/entity/Content.java @@ -43,6 +43,9 @@ public class Content { private List contentTagList; @Enumerated(EnumType.STRING) private ContentAccessType contentAccessType; + @Lob + @Column(name = "image") + private Byte[] image; } diff --git a/resource-management/resource-management-dao/src/main/java/com/itgura/entity/QuizImage.java b/resource-management/resource-management-dao/src/main/java/com/itgura/entity/QuizImage.java index 528bd6a..8bff658 100644 --- a/resource-management/resource-management-dao/src/main/java/com/itgura/entity/QuizImage.java +++ b/resource-management/resource-management-dao/src/main/java/com/itgura/entity/QuizImage.java @@ -14,8 +14,8 @@ @Getter @PrimaryKeyJoinColumn(name = "quiz_image_id") public class QuizImage extends Content{ - @Column(name = "image") - private byte[] image; + @Column(name = "quiz_image") + private byte[] quizImage; @OneToOne @JoinColumn(name = "quiz_question_id") private QuizQuestion quizQuestion; diff --git a/resource-management/resource-management-dao/src/main/java/com/itgura/response/dto/ClassResponseDto.java b/resource-management/resource-management-dao/src/main/java/com/itgura/response/dto/ClassResponseDto.java index cefe1ed..0d0ab37 100644 --- a/resource-management/resource-management-dao/src/main/java/com/itgura/response/dto/ClassResponseDto.java +++ b/resource-management/resource-management-dao/src/main/java/com/itgura/response/dto/ClassResponseDto.java @@ -22,5 +22,7 @@ public class ClassResponseDto { private String className; @JsonProperty("content_access_type") private ContentAccessType contentAccesstype; + @JsonProperty("image") + private String image; } diff --git a/resource-management/resource-management-dao/src/main/java/com/itgura/response/dto/LessonResponseDto.java b/resource-management/resource-management-dao/src/main/java/com/itgura/response/dto/LessonResponseDto.java index 2a6a64d..5d8281f 100644 --- a/resource-management/resource-management-dao/src/main/java/com/itgura/response/dto/LessonResponseDto.java +++ b/resource-management/resource-management-dao/src/main/java/com/itgura/response/dto/LessonResponseDto.java @@ -55,5 +55,7 @@ public class LessonResponseDto { private String updatedByName; @JsonProperty("content_access_type") private ContentAccessType contentAccesstype; + @JsonProperty("image") + private String image; } diff --git a/resource-management/resource-management-dao/src/main/java/com/itgura/response/dto/mapper/ClassMapper.java b/resource-management/resource-management-dao/src/main/java/com/itgura/response/dto/mapper/ClassMapper.java index 78b4c47..711d7b5 100644 --- a/resource-management/resource-management-dao/src/main/java/com/itgura/response/dto/mapper/ClassMapper.java +++ b/resource-management/resource-management-dao/src/main/java/com/itgura/response/dto/mapper/ClassMapper.java @@ -6,6 +6,7 @@ import org.mapstruct.Mapping; import org.mapstruct.factory.Mappers; +import java.util.Base64; import java.util.List; import java.util.stream.Collectors; @@ -15,8 +16,21 @@ public interface ClassMapper { @Mapping(source = "contentId", target = "id") @Mapping(source = "contentAccessType", target = "contentAccesstype") + @Mapping(target = "image", expression = "java(convertImageToBase64(aClass.getImage()))") ClassResponseDto toDto(AClass aClass); List toDtoList(List aClassList); + // Utility method for image conversion + default String convertImageToBase64(Byte[] image) { + if (image == null) return null; + + // Convert Byte[] to byte[] + byte[] primitiveBytes = new byte[image.length]; + for (int i = 0; i < image.length; i++) { + primitiveBytes[i] = image[i]; + } + + return Base64.getEncoder().encodeToString(primitiveBytes); + } } diff --git a/resource-management/resource-management-dao/src/main/java/com/itgura/response/dto/mapper/LessonMapper.java b/resource-management/resource-management-dao/src/main/java/com/itgura/response/dto/mapper/LessonMapper.java index 87e8ac8..fcfa2f1 100644 --- a/resource-management/resource-management-dao/src/main/java/com/itgura/response/dto/mapper/LessonMapper.java +++ b/resource-management/resource-management-dao/src/main/java/com/itgura/response/dto/mapper/LessonMapper.java @@ -6,6 +6,7 @@ import org.mapstruct.Mapping; import org.mapstruct.factory.Mappers; +import java.util.Base64; import java.util.List; @Mapper @@ -18,7 +19,22 @@ public interface LessonMapper { @Mapping(source = "createdBy", target = "createdByUserId") @Mapping(source = "lastModifiedBy", target = "lastModifiedByUserId") @Mapping(source = "contentAccessType", target = "contentAccesstype") + @Mapping(target = "image", expression = "java(convertImageToBase64(lesson.getImage()))") LessonResponseDto toDto(Lesson lesson); List toDtoList(List lessons); + + // Utility method for image conversion + default String convertImageToBase64(Byte[] image) { + + if (image == null) return null; + + // Convert Byte[] to byte[] + byte[] primitiveBytes = new byte[image.length]; + for (int i = 0; i < image.length; i++) { + primitiveBytes[i] = image[i]; + } + + return Base64.getEncoder().encodeToString(primitiveBytes); + } } \ No newline at end of file diff --git a/resource-management/resource-management-service/src/main/java/com/itgura/service/ClassService.java b/resource-management/resource-management-service/src/main/java/com/itgura/service/ClassService.java index d073647..3cb42e0 100644 --- a/resource-management/resource-management-service/src/main/java/com/itgura/service/ClassService.java +++ b/resource-management/resource-management-service/src/main/java/com/itgura/service/ClassService.java @@ -5,6 +5,7 @@ import com.itgura.request.ClassRequest; import com.itgura.response.dto.ClassResponseDto; import com.itgura.response.hasPermissionResponse; +import org.springframework.web.multipart.MultipartFile; import java.net.URISyntaxException; import java.util.List; @@ -14,8 +15,8 @@ public interface ClassService { public ClassResponseDto getClassById(UUID id) throws ValueNotExistException; List getAllClasses(); - public String create(ClassRequest request) throws ValueNotExistException; - public String update( UUID classId, ClassRequest request) throws ValueNotExistException; + public String create(ClassRequest request, MultipartFile file) throws ValueNotExistException; + public String update( UUID classId, ClassRequest request,MultipartFile file) throws ValueNotExistException; Double getClassFee(UUID id) throws ValueNotExistException; diff --git a/resource-management/resource-management-service/src/main/java/com/itgura/service/LessonService.java b/resource-management/resource-management-service/src/main/java/com/itgura/service/LessonService.java index 6a00d79..ffce582 100644 --- a/resource-management/resource-management-service/src/main/java/com/itgura/service/LessonService.java +++ b/resource-management/resource-management-service/src/main/java/com/itgura/service/LessonService.java @@ -5,14 +5,15 @@ import com.itgura.exception.ValueNotExistException; import com.itgura.request.LessonRequest; import com.itgura.response.dto.LessonResponseDto; +import org.springframework.web.multipart.MultipartFile; import javax.security.auth.login.CredentialNotFoundException; import java.util.List; import java.util.UUID; public interface LessonService { - String saveLesson(LessonRequest request) throws ValueNotExistException; - String updateLesson(LessonRequest request, UUID id) throws ValueNotExistException; + String saveLesson(LessonRequest request, MultipartFile file) throws ValueNotExistException; + String updateLesson(LessonRequest request, UUID id,MultipartFile file) throws ValueNotExistException; String deleteLesson(UUID id) throws ApplicationException, CredentialNotFoundException, BadRequestRuntimeException; LessonResponseDto findLesson(UUID id) throws ValueNotExistException; diff --git a/resource-management/resource-management-service/src/main/java/com/itgura/service/impl/ClassServiceImpl.java b/resource-management/resource-management-service/src/main/java/com/itgura/service/impl/ClassServiceImpl.java index ec4cdca..5c0aa33 100644 --- a/resource-management/resource-management-service/src/main/java/com/itgura/service/impl/ClassServiceImpl.java +++ b/resource-management/resource-management-service/src/main/java/com/itgura/service/impl/ClassServiceImpl.java @@ -21,6 +21,7 @@ import com.itgura.util.UserUtil; import jakarta.transaction.Transactional; import jakarta.ws.rs.ForbiddenException; +import org.apache.commons.lang3.ArrayUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; @@ -29,13 +30,11 @@ import org.springframework.stereotype.Service; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; +import org.springframework.web.multipart.MultipartFile; import java.net.URI; import java.net.URISyntaxException; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.UUID; +import java.util.*; @Service @@ -71,7 +70,7 @@ public List getAllClasses() { @Override @Transactional - public String create(ClassRequest request) throws ValueNotExistException { + public String create(ClassRequest request,MultipartFile file) throws ValueNotExistException { try { UserResponseDto loggedUserDetails = userDetailService.getLoggedUserDetails(UserUtil.extractToken()); if(loggedUserDetails == null){ @@ -91,6 +90,16 @@ public String create(ClassRequest request) throws ValueNotExistException { aClass.setLastModifiedOn(new Date(System.currentTimeMillis())); aClass.setLastModifiedBy(userId); aClass.setContentAccessType(request.getContentAccesstype()); + + if (file!=null) { + + + String ext = Objects.requireNonNull(file.getOriginalFilename()).substring(file.getOriginalFilename().lastIndexOf('.') + 1).toLowerCase(); + if (ext.equals("jpeg") || ext.equals("jpe") || ext.equals("jpg") || ext.equals("png") || ext.equals("gif")) { + byte[] content = file.getBytes(); + aClass.setImage(ArrayUtils.toObject(content)); + } + } classRepository.save(aClass); @@ -102,7 +111,7 @@ public String create(ClassRequest request) throws ValueNotExistException { @Override @Transactional - public String update(UUID classId, ClassRequest request) throws ValueNotExistException { + public String update(UUID classId, ClassRequest request,MultipartFile file) throws ValueNotExistException { try{ UserResponseDto loggedUserDetails = userDetailService.getLoggedUserDetails(UserUtil.extractToken()); if(loggedUserDetails == null){ @@ -121,6 +130,16 @@ public String update(UUID classId, ClassRequest request) throws ValueNotExistExc aClass.setContentAccessType(request.getContentAccesstype()); aClass.setLastModifiedOn(new Date(System.currentTimeMillis())); aClass.setLastModifiedBy(userId); + + if (file != null) { + + + String ext = Objects.requireNonNull(file.getOriginalFilename()).substring(file.getOriginalFilename().lastIndexOf('.') + 1).toLowerCase(); + if (ext.equals("jpeg") || ext.equals("jpe") || ext.equals("jpg") || ext.equals("png") || ext.equals("gif")) { + byte[] content = file.getBytes(); + aClass.setImage(ArrayUtils.toObject(content)); + } + } classRepository.save(aClass); return "Class updated successfully"; } diff --git a/resource-management/resource-management-service/src/main/java/com/itgura/service/impl/LessonServiceImpl.java b/resource-management/resource-management-service/src/main/java/com/itgura/service/impl/LessonServiceImpl.java index cb70bf2..90abd70 100644 --- a/resource-management/resource-management-service/src/main/java/com/itgura/service/impl/LessonServiceImpl.java +++ b/resource-management/resource-management-service/src/main/java/com/itgura/service/impl/LessonServiceImpl.java @@ -16,8 +16,10 @@ import com.itgura.util.UserUtil; import jakarta.transaction.Transactional; import jakarta.ws.rs.ForbiddenException; +import org.apache.commons.lang3.ArrayUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; import javax.security.auth.login.CredentialNotFoundException; import java.util.*; @@ -34,7 +36,7 @@ public class LessonServiceImpl implements LessonService { @Override @Transactional - public String saveLesson(LessonRequest request) throws ValueNotExistException { + public String saveLesson(LessonRequest request, MultipartFile file) throws ValueNotExistException { try { UserResponseDto loggedUserDetails = userDetailService.getLoggedUserDetails(UserUtil.extractToken()); if(loggedUserDetails == null){ @@ -64,6 +66,18 @@ public String saveLesson(LessonRequest request) throws ValueNotExistException { lesson.setCreatedBy(userId); lesson.setLastModifiedBy(userId); lesson.setContentAccessType(request.getContentAccesstype()); + + if (file != null) { + + + String ext = Objects.requireNonNull(file.getOriginalFilename()).substring(file.getOriginalFilename().lastIndexOf('.') + 1).toLowerCase(); + if (ext.equals("jpeg") || ext.equals("jpe") || ext.equals("jpg") || ext.equals("png") || ext.equals("gif")) { + byte[] content = file.getBytes(); + lesson.setImage(ArrayUtils.toObject(content)); + } + }else{ + System.out.println("Image File is null"); + } lessonRepository.save(lesson); return "Lesson saved successfully"; } @@ -74,7 +88,7 @@ public String saveLesson(LessonRequest request) throws ValueNotExistException { @Override @Transactional - public String updateLesson(LessonRequest request, UUID id) throws ValueNotExistException { + public String updateLesson(LessonRequest request, UUID id,MultipartFile file) throws ValueNotExistException { try{ UserResponseDto loggedUserDetails = userDetailService.getLoggedUserDetails(UserUtil.extractToken()); if(loggedUserDetails == null){ @@ -115,6 +129,15 @@ public String updateLesson(LessonRequest request, UUID id) throws ValueNotExistE if (request.getContentAccesstype() != null) { lesson.setContentAccessType(request.getContentAccesstype()); } + if(file!= null){ + + String ext = Objects.requireNonNull(file.getOriginalFilename()).substring(file.getOriginalFilename().lastIndexOf('.') + 1).toLowerCase(); + if (ext.equals("jpeg") || ext.equals("jpe") || ext.equals("jpg") || ext.equals("png") || ext.equals("gif")) { + byte[] content = file.getBytes(); + + lesson.setImage(ArrayUtils.toObject(content)); + } + } if (request.getClassId() != null) { Optional classOptional = classRepository.findById(request.getClassId()); if (classOptional.isPresent()) {