From f293353d366abf6ecb4afdf4636d6d71cbbaa022 Mon Sep 17 00:00:00 2001 From: heyethereum Date: Mon, 15 Jul 2024 00:29:39 +0800 Subject: [PATCH] write to scan_history table --- .../safeqr/app/constants/APIConstants.java | 15 +++++ .../safeqr/app/constants/CommonConstants.java | 9 +++ .../controller/QRCodeTypeController.java | 27 +++++++-- .../app/qrcode/dto/response/ScanResponse.java | 12 ++++ .../com/safeqr/app/qrcode/entity/QRCode.java | 8 +-- .../safeqr/app/qrcode/entity/QRCodeType.java | 1 - .../safeqr/app/qrcode/entity/ScanHistory.java | 31 ++++++++++ .../qrcode/repository/QRCodeRepository.java | 8 +++ .../repository/ScanHistoryRepository.java | 9 +++ .../app/qrcode/service/QRCodeTypeService.java | 57 +++++++++++++++++++ src/main/resources/keystore/safeqr.crt | 55 ++++++++++++++++++ 11 files changed, 221 insertions(+), 11 deletions(-) create mode 100644 src/main/java/com/safeqr/app/constants/APIConstants.java create mode 100644 src/main/java/com/safeqr/app/constants/CommonConstants.java create mode 100644 src/main/java/com/safeqr/app/qrcode/dto/response/ScanResponse.java create mode 100644 src/main/java/com/safeqr/app/qrcode/entity/ScanHistory.java create mode 100644 src/main/java/com/safeqr/app/qrcode/repository/QRCodeRepository.java create mode 100644 src/main/java/com/safeqr/app/qrcode/repository/ScanHistoryRepository.java create mode 100644 src/main/resources/keystore/safeqr.crt diff --git a/src/main/java/com/safeqr/app/constants/APIConstants.java b/src/main/java/com/safeqr/app/constants/APIConstants.java new file mode 100644 index 0000000..91a4e24 --- /dev/null +++ b/src/main/java/com/safeqr/app/constants/APIConstants.java @@ -0,0 +1,15 @@ +package com.safeqr.app.constants; + +public class APIConstants { + private APIConstants() { + //private constructor to prevent instantiation + } + public static final String API_VERSION = "v1"; + public static final String API_URL_QRCODE_GET_ALL = "/qrcodetypes"; + public static final String API_URL_QRCODE_SCAN = "/qrcodetypes/scan"; + public static final String API_URL_QRCODE_DETECT = "/qrcodetypes/detect"; + public static final String API_URL_QRCODE_VERIFY_URL = "/qrcodetypes/verifyurl"; + public static final String API_URL_QRCODE_VIRUS_TOTAL_CHECK = "/qrcodetypes/virustotalcheck"; + public static final String API_URL_QRCODE_REDIRECT_COUNT = "/qrcodetypes/redirectcount"; + +} diff --git a/src/main/java/com/safeqr/app/constants/CommonConstants.java b/src/main/java/com/safeqr/app/constants/CommonConstants.java new file mode 100644 index 0000000..6c16e19 --- /dev/null +++ b/src/main/java/com/safeqr/app/constants/CommonConstants.java @@ -0,0 +1,9 @@ +package com.safeqr.app.constants; + +public class CommonConstants { + private CommonConstants() { + //private constructor to prevent instantiation + } + public static final String HEADER_USER_ID = "X-USER-ID"; + public static final String DEFAULT_QR_CODE_TYPE = "TEXT"; +} diff --git a/src/main/java/com/safeqr/app/qrcode/controller/QRCodeTypeController.java b/src/main/java/com/safeqr/app/qrcode/controller/QRCodeTypeController.java index cf2a098..604e84d 100644 --- a/src/main/java/com/safeqr/app/qrcode/controller/QRCodeTypeController.java +++ b/src/main/java/com/safeqr/app/qrcode/controller/QRCodeTypeController.java @@ -1,22 +1,30 @@ package com.safeqr.app.qrcode.controller; +import com.safeqr.app.constants.APIConstants; +import com.safeqr.app.constants.CommonConstants; import com.safeqr.app.qrcode.dto.QRCodePayload; import com.safeqr.app.qrcode.dto.RedirectCountResponse; import com.safeqr.app.qrcode.dto.URLVerificationResponse; +import com.safeqr.app.qrcode.dto.response.ScanResponse; import com.safeqr.app.qrcode.entity.QRCodeType; import com.safeqr.app.qrcode.service.QRCodeTypeService; import com.safeqr.app.qrcode.service.RedirectCountService; import com.safeqr.app.qrcode.service.URLVerificationService; import com.safeqr.app.qrcode.service.VirusTotalService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; + import java.util.List; @RestController -@RequestMapping("/v1/qrcodetypes") +@RequestMapping(APIConstants.API_VERSION) public class QRCodeTypeController { + private static final Logger logger = LoggerFactory.getLogger(QRCodeTypeService.class); @Autowired private QRCodeTypeService qrCodeTypeService; @@ -30,23 +38,30 @@ public class QRCodeTypeController { @Autowired private RedirectCountService redirectCountService; - @GetMapping + @GetMapping(value = APIConstants.API_URL_QRCODE_GET_ALL) public ResponseEntity> getAllTypes() { return ResponseEntity.ok(qrCodeTypeService.getAllTypes()); } - @PostMapping("/detect") + @PostMapping(value = APIConstants.API_URL_QRCODE_SCAN, produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity scanQRCode(@RequestBody QRCodePayload payload, + @RequestHeader(required = false, name = CommonConstants.HEADER_USER_ID) String userId) { + logger.info("Invoking scan endpoint"); + return ResponseEntity.ok(qrCodeTypeService.scanQRCode(userId, payload)); + } + + @PostMapping(APIConstants.API_URL_QRCODE_DETECT) public ResponseEntity detectType(@RequestBody QRCodePayload payload) { return ResponseEntity.ok(qrCodeTypeService.detectType(payload).block()); } - @PostMapping("/verifyURL") + @PostMapping(APIConstants.API_URL_QRCODE_VERIFY_URL) public ResponseEntity verifyURL(@RequestBody QRCodePayload payload) { URLVerificationResponse response = urlVerificationService.verifyURL(payload); return ResponseEntity.ok(response); } - @PostMapping("/virusTotalCheck") + @PostMapping(APIConstants.API_URL_QRCODE_VIRUS_TOTAL_CHECK) public ResponseEntity virusTotalCheck(@RequestBody QRCodePayload payload) { try { String analysisId = virusTotalService.scanURL(payload); @@ -57,7 +72,7 @@ public class QRCodeTypeController { } } - @PostMapping("/checkRedirects") + @PostMapping(APIConstants.API_URL_QRCODE_REDIRECT_COUNT) public ResponseEntity checkRedirects(@RequestBody QRCodePayload payload) { return ResponseEntity.ok(redirectCountService.countRedirects(payload).block()); } diff --git a/src/main/java/com/safeqr/app/qrcode/dto/response/ScanResponse.java b/src/main/java/com/safeqr/app/qrcode/dto/response/ScanResponse.java new file mode 100644 index 0000000..86f6e32 --- /dev/null +++ b/src/main/java/com/safeqr/app/qrcode/dto/response/ScanResponse.java @@ -0,0 +1,12 @@ +package com.safeqr.app.qrcode.dto.response; + +import com.safeqr.app.qrcode.entity.QRCodeType; +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class ScanResponse { + private String contents; + private String qrType; +} diff --git a/src/main/java/com/safeqr/app/qrcode/entity/QRCode.java b/src/main/java/com/safeqr/app/qrcode/entity/QRCode.java index 23b5481..9984c49 100644 --- a/src/main/java/com/safeqr/app/qrcode/entity/QRCode.java +++ b/src/main/java/com/safeqr/app/qrcode/entity/QRCode.java @@ -2,6 +2,7 @@ package com.safeqr.app.qrcode.entity; import jakarta.persistence.*; +import lombok.Builder; import lombok.Data; import org.hibernate.annotations.GenericGenerator; @@ -10,6 +11,7 @@ import java.util.UUID; @Entity @Table(name = "qr_code", schema = "safeqr") @Data +@Builder public class QRCode { @Id @@ -18,10 +20,8 @@ public class QRCode { @Column(updatable = false, nullable = false) private UUID id; - @ManyToOne - @JoinColumn(name = "qr_code_type_id", nullable = false) - private QRCodeType qrCodeType; - + @Column(name = "qr_code_type_id", nullable = false) + private Long qrCodeTypeId; private String userId; private String contents; diff --git a/src/main/java/com/safeqr/app/qrcode/entity/QRCodeType.java b/src/main/java/com/safeqr/app/qrcode/entity/QRCodeType.java index 2d4b9f0..777668c 100644 --- a/src/main/java/com/safeqr/app/qrcode/entity/QRCodeType.java +++ b/src/main/java/com/safeqr/app/qrcode/entity/QRCodeType.java @@ -12,7 +12,6 @@ public class QRCodeType { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - private String type; private String description; private String prefix; diff --git a/src/main/java/com/safeqr/app/qrcode/entity/ScanHistory.java b/src/main/java/com/safeqr/app/qrcode/entity/ScanHistory.java new file mode 100644 index 0000000..fd36e17 --- /dev/null +++ b/src/main/java/com/safeqr/app/qrcode/entity/ScanHistory.java @@ -0,0 +1,31 @@ +package com.safeqr.app.qrcode.entity; + +import jakarta.persistence.*; +import lombok.Builder; + +import java.util.UUID; + +@Entity +@Builder +@Table(name = "scan_history", schema = "safeqr") +public class ScanHistory { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "qr_code_id", nullable = false) + private UUID qrCodeId; + + @Column(name = "user_id", nullable = false) + private String userId; + + @Enumerated(EnumType.STRING) + @Column(name = "status") + private ScanStatus scanStatus; + + public enum ScanStatus { + ACTIVE, + INACTIVE + } +} \ No newline at end of file diff --git a/src/main/java/com/safeqr/app/qrcode/repository/QRCodeRepository.java b/src/main/java/com/safeqr/app/qrcode/repository/QRCodeRepository.java new file mode 100644 index 0000000..26c749b --- /dev/null +++ b/src/main/java/com/safeqr/app/qrcode/repository/QRCodeRepository.java @@ -0,0 +1,8 @@ +package com.safeqr.app.qrcode.repository; + +import com.safeqr.app.qrcode.entity.QRCode; +import org.springframework.data.jpa.repository.JpaRepository; +import java.util.UUID; + +public interface QRCodeRepository extends JpaRepository { +} diff --git a/src/main/java/com/safeqr/app/qrcode/repository/ScanHistoryRepository.java b/src/main/java/com/safeqr/app/qrcode/repository/ScanHistoryRepository.java new file mode 100644 index 0000000..9c33282 --- /dev/null +++ b/src/main/java/com/safeqr/app/qrcode/repository/ScanHistoryRepository.java @@ -0,0 +1,9 @@ +package com.safeqr.app.qrcode.repository; + +import com.safeqr.app.qrcode.entity.ScanHistory; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface ScanHistoryRepository extends JpaRepository { +} \ No newline at end of file diff --git a/src/main/java/com/safeqr/app/qrcode/service/QRCodeTypeService.java b/src/main/java/com/safeqr/app/qrcode/service/QRCodeTypeService.java index 0440bf0..3099b11 100644 --- a/src/main/java/com/safeqr/app/qrcode/service/QRCodeTypeService.java +++ b/src/main/java/com/safeqr/app/qrcode/service/QRCodeTypeService.java @@ -1,10 +1,18 @@ package com.safeqr.app.qrcode.service; +import com.safeqr.app.constants.CommonConstants; import com.safeqr.app.qrcode.dto.QRCodePayload; +import com.safeqr.app.qrcode.dto.response.ScanResponse; +import com.safeqr.app.qrcode.entity.QRCode; import com.safeqr.app.qrcode.entity.QRCodeType; +import com.safeqr.app.qrcode.entity.ScanHistory; +import com.safeqr.app.qrcode.repository.QRCodeRepository; import com.safeqr.app.qrcode.repository.QRCodeTypeRepository; +import com.safeqr.app.qrcode.repository.ScanHistoryRepository; import jakarta.annotation.PostConstruct; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import reactor.core.publisher.Mono; @@ -14,24 +22,73 @@ import java.util.List; @Service public class QRCodeTypeService { + private static final Logger logger = LoggerFactory.getLogger(QRCodeTypeService.class); @Autowired private QRCodeTypeRepository qrCodeTypeRepository; + @Autowired + private ScanHistoryRepository scanHistoryRepository; + @Autowired + private QRCodeRepository qrCodeRepository; @Autowired private SafeBrowsingService safeBrowsingService; private List configs; + private QRCodeType defaultQRCodeType; @PostConstruct public void loadQRCodeTypes() { + // Fetch all QR Code Types from the database configs = qrCodeTypeRepository.findAll(); + // Set the default QR Code Type + defaultQRCodeType = configs.stream() + .filter(config -> config.getType().equals(CommonConstants.DEFAULT_QR_CODE_TYPE)) + .findFirst() + .orElse(null); } public List getAllTypes() { return configs; } + public ScanResponse scanQRCode(String userId, QRCodePayload payload) { + String data = payload.getData(); + logger.info("scanQRCode: userId={}, data={}", userId, data); + + // Get the QR Code Type + QRCodeType qrType = getQRCodeType(data); + + // Insert the QR Code into main qrcode table + QRCode scannedQR = qrCodeRepository.save(QRCode.builder() + .userId(userId) + .contents(data) + .qrCodeTypeId(qrType.getId()) + .build()); + // Insert qrcode into respective table based on type + + // Insert into Scan History table if userId is not null + logger.info("scanQRCode: scannedQR new ID={}", scannedQR.getId()); + if (userId != null) { + scanHistoryRepository.save(ScanHistory.builder() + .qrCodeId(scannedQR.getId()) + .userId(userId) + .scanStatus(ScanHistory.ScanStatus.ACTIVE) + .build()); + } + + return ScanResponse.builder() + .contents(data) + .qrType(qrType.getType()) + .build(); + } + private QRCodeType getQRCodeType(String data) { + return configs.stream() + .filter(config -> data.toLowerCase().startsWith(config.getPrefix().toLowerCase())) + .findFirst() + .orElse(defaultQRCodeType); + } + public Mono detectType(QRCodePayload payload) { String data = payload.getData(); diff --git a/src/main/resources/keystore/safeqr.crt b/src/main/resources/keystore/safeqr.crt new file mode 100644 index 0000000..4118b81 --- /dev/null +++ b/src/main/resources/keystore/safeqr.crt @@ -0,0 +1,55 @@ +Bag Attributes + friendlyName: safeqr + localKeyID: 54 69 6D 65 20 31 37 31 38 35 31 32 38 36 35 30 37 39 +subject=C=SG, ST=SG, L=Singapore, O=UOW, OU=IT, CN=SafeQR App +issuer=C=SG, ST=SG, L=Singapore, O=UOW, OU=IT, CN=SafeQR App +-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgIId40EXlih454wDQYJKoZIhvcNAQEMBQAwXjELMAkGA1UE +BhMCU0cxCzAJBgNVBAgTAlNHMRIwEAYDVQQHEwlTaW5nYXBvcmUxDDAKBgNVBAoT +A1VPVzELMAkGA1UECxMCSVQxEzARBgNVBAMTClNhZmVRUiBBcHAwHhcNMjQwNjE2 +MDQ0MTA0WhcNMzQwNjE0MDQ0MTA0WjBeMQswCQYDVQQGEwJTRzELMAkGA1UECBMC +U0cxEjAQBgNVBAcTCVNpbmdhcG9yZTEMMAoGA1UEChMDVU9XMQswCQYDVQQLEwJJ +VDETMBEGA1UEAxMKU2FmZVFSIEFwcDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBALHaMAOaBuX2D9Gq6islUVOKQr343FOHilQiNG7H4Pm43flr4MbdvEaa +Dcqi5tWufssoWnvV0cVCwFGvMnWWt/21/LV76jd9XhQdY6tDJZfdccgRco8LS8VL +G30d2NhcUbMVH//wsLcvoIkic4w++0SjeoKa5qnS4VX2dkI3fpOYJUMlRogIm1uV +Rlv6jcUK3uYE734lHulwB/ggtmbG2ulcxgOsW4O3HTafU6wZIC8pwfBjbpUnrFc7 +xcIM2VGHkmaP1vyIRbOT5TWMDMlNP+yR/abA8EJX6VqUE08ENen5G66DfzoAeuiE +UwY7OwIbXfuQR97RszAx6vnMQfKak30CAwEAAaMhMB8wHQYDVR0OBBYEFJ1MqzAY +b2qwbXEmZcAYEGyZ6EVNMA0GCSqGSIb3DQEBDAUAA4IBAQBFZH7Gp/PiZOpubsi1 +oVrZYLU6yAucq+RSreO2YrCMJtqiPtYxhB84kaQ277u4BW4xsDjo7mxh1qwEZvSA +9K13Pu3FgilzdJnm5E54jLSvmW62gIjziuMg34ioVh6WnD4/Tre5vHRcR2IHYghh +5rI6gFiSHvjHun71xoeaUdJQUjx4LFDP5cA5U3oGcN08yMopja9ABQIUAF5MWg3U +VDN74JRkae4VIXQ/4gbuDwjmU6vyJHKNxrFxbtJCvmSBLdCNONcEHiHkKb4SIx4C +QwOSuhRc8bqWepfLgCqskCVqEJW1s2lK2swEN3gM7D89jtuHOG/itJH6J1+COgea +dwSP +-----END CERTIFICATE----- +Bag Attributes + friendlyName: safeqr-nlb-cert + localKeyID: 54 69 6D 65 20 31 37 31 39 37 39 33 35 39 34 36 33 32 +subject=C=SG, ST=SG, L=Singapore, O=UOW, OU=IT, CN=safeqr-nlb-6bd79c7ba50f3cb5.elb.ap-southeast-1.amazonaws.com +issuer=C=SG, ST=SG, L=Singapore, O=UOW, OU=IT, CN=safeqr-nlb-6bd79c7ba50f3cb5.elb.ap-southeast-1.amazonaws.com +-----BEGIN CERTIFICATE----- +MIIEAzCCAuugAwIBAgIUMBgs2njKVCOQNmrQZmtHE04JYwgwDQYJKoZIhvcNAQEL +BQAwgZAxCzAJBgNVBAYTAlNHMQswCQYDVQQIDAJTRzESMBAGA1UEBwwJU2luZ2Fw +b3JlMQwwCgYDVQQKDANVT1cxCzAJBgNVBAsMAklUMUUwQwYDVQQDDDxzYWZlcXIt +bmxiLTZiZDc5YzdiYTUwZjNjYjUuZWxiLmFwLXNvdXRoZWFzdC0xLmFtYXpvbmF3 +cy5jb20wHhcNMjQwNjMwMTMzNjA0WhcNMjUwNjMwMTMzNjA0WjCBkDELMAkGA1UE +BhMCU0cxCzAJBgNVBAgMAlNHMRIwEAYDVQQHDAlTaW5nYXBvcmUxDDAKBgNVBAoM +A1VPVzELMAkGA1UECwwCSVQxRTBDBgNVBAMMPHNhZmVxci1ubGItNmJkNzljN2Jh +NTBmM2NiNS5lbGIuYXAtc291dGhlYXN0LTEuYW1hem9uYXdzLmNvbTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAO08NbCn1ANY/JCrxtqR2LZRovSbGaI1 +nVQalLSfQolEJT9J1bP5haCf002us5Zw5+zgfhcbkNgyom0vakmU6I1n9iMZyVQx +sb8MJfkJlB/vR6VPSAowrvWy344aPhsQJkxERGOHcuIfaZroLhqdwoYl1lTLz5AD +5lt266AwiUY4GehEIZUo5fgdoueMqCjnPD7lH2nALS9DH0R9uGh/urZJJ40yYkSi +MKzEStYR0qdmoIipNW7u3uBiqbulbVlhdgXABRrsWgxWaDH40UvJss0/a5ojmxxU +W5JQJCKyF150Xr6UoE8XCsd1XptVDbsMy//xKkkuns0m3t24aOw1S8cCAwEAAaNT +MFEwHQYDVR0OBBYEFE+wiD0lcUAnJ4ITiEQsBmQjobehMB8GA1UdIwQYMBaAFE+w +iD0lcUAnJ4ITiEQsBmQjobehMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBAKjjNVMxEsjk4ivdjC6qbmEXJogSofw5We2GhcJyria/r2u9k65vHEk1 +/T79lF2H8IvqNpSAnuyKroJBXD16p+ZOOeUQu8oCudl4iFT2T2DMP102AwP9L902 +vaHqBobhTCkG++ZzefmnpMyaa7hrVFnW9iP4vmHhM4yqR5ThIEvofVJUbJHhGug4 +gDBRW6xV0zaVp+thFXmGLXuURilQTfCen2WVL8DndjQxMc+jobCN4vxSAxR23Ndo +Zm5/a5SnxXypcpjXnI17gzvybpZrCD/NZcNmpnZw8vf5m7BC9C5uX4CGujkto7R5 +mY4JDDt0XlP7hiGlHMkr/IUaVARoocE= +-----END CERTIFICATE-----