diff --git a/.env b/.env index 5c2577a..0e603ab 100644 --- a/.env +++ b/.env @@ -2,9 +2,6 @@ VITE_BACKEND=http://localhost:4320/api/ # VITE_BACKEND=https://ocboapps.davaocity.gov.ph/esign-server/api/ # VITE_BACKEND=http://192.168.7.163/server/api/ -VITE_SECRET_KEY="_q]e88#^vfHYZUwO@CI%r=VNsIW8EohK" -VITE_IV="vLXE!H~M&*u-1)bB" - VITE_HEAD=ARCH. KHASHAYAR L. TOGHYANI VITE_PESO=₱ VITE_HEADID=276 diff --git a/.gitignore b/.gitignore index eddba5f..e752e65 100644 --- a/.gitignore +++ b/.gitignore @@ -25,7 +25,8 @@ dist-ssr *.sw? # environment variables -.env +.env.local +/backend/.env # jetbrains setting folder .idea/ diff --git a/backend/main.go b/backend/main.go index 09bdea5..a3641a0 100644 --- a/backend/main.go +++ b/backend/main.go @@ -4,15 +4,10 @@ import ( "database/sql" "log" "net/http" - - // "crypto/rand" - // "crypto/rsa" - // "crypto/x509" - // "encoding/base64" - // "encoding/pem" - // "errors" "os" + "ocbo-esign-backend/middleware" + "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" _ "github.com/go-sql-driver/mysql" @@ -982,8 +977,7 @@ func connect() { } }) - // router.POST("/api/post-registration", middleware.tokenChecker, func(c *gin.Context) { - router.POST("/api/post-registration", func(c *gin.Context) { + router.POST("/api/post-registration", middleware.TokenChecker(), func(c *gin.Context) { type RegistrationData struct { Data int `json:"data"` Data2 string `json:"data2"` diff --git a/backend/middleware/tokenChecker.go b/backend/middleware/tokenChecker.go new file mode 100644 index 0000000..012aa8f --- /dev/null +++ b/backend/middleware/tokenChecker.go @@ -0,0 +1,102 @@ +package middleware + +import ( + "log" + "net/http" + "os" + + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/base64" + "encoding/pem" + "errors" + "strconv" + "strings" + "time" + + "github.com/gin-gonic/gin" + _ "github.com/go-sql-driver/mysql" + "github.com/joho/godotenv" +) + +func decrypt(encrypted string) (string, error) { + err := godotenv.Load() + if err != nil { + log.Fatal("Error loading .env file") + } + + privateKey := os.Getenv("PRIVATE_KEY") + + cipherText, err := base64.StdEncoding.DecodeString(encrypted) + if err != nil { + return "", errors.New("cannot decode encrypted text") + } + + block, _ := pem.Decode([]byte(privateKey)) + if block == nil { + return "", errors.New("private key error") + } + priv, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + return "", err + } + result, err := rsa.DecryptPKCS1v15(rand.Reader, priv, cipherText) + if err != nil { + return "", err + } + return string(result), nil +} + +func TokenChecker() gin.HandlerFunc { + return func(c *gin.Context) { + token := c.GetHeader("OCBO-Token") + start := strings.Index(token, "ocbo=") + len("ocbo=") + end := strings.LastIndex(token, "token") + + if token == "" { + c.JSON(http.StatusUnauthorized, gin.H{"error": "The request is missing an OCBO Token"}) + c.Abort() + return + } + + if start == -1 || end == -1 || start >= end { + c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid OCBO Token format"}) + c.Abort() + return + } + + extractedToken := token[start:end] + extractedToken = strings.ReplaceAll(extractedToken, "~", "/") + + decrypted, err := decrypt(extractedToken) + if err != nil { + c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid OCBO Token Value"}) + c.Abort() + return + } + + parts := strings.Split(decrypted, "-") + requested := parts[2] + requestedNum, _ := strconv.Atoi(requested) + expiration := parts[3] + expirationNum, _ := strconv.Atoi(expiration) + + unix := strconv.FormatInt(time.Now().UTC().Unix(), 10) + unixNum, _ := strconv.Atoi(unix) + + if requestedNum > expirationNum { + c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid OCBO Token Value"}) + c.Abort() + return + } + + if unixNum > expirationNum { + c.JSON(http.StatusUnauthorized, gin.H{"error": "OCBO Token Expired"}) + c.Abort() + return + } + + c.Next() + } +} diff --git a/package.json b/package.json index e485ef5..6675e54 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "dayjs": "^1.11.19", "gsap": "^3.13.0", "jimp": "^1.6.0", + "jsencrypt": "^3.5.4", "jspdf": "^3.0.3", "jspdf-barcode": "^1.0.2", "nanostores": "^1.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 12adc2a..0f106bf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,6 +35,9 @@ importers: jimp: specifier: ^1.6.0 version: 1.6.0 + jsencrypt: + specifier: ^3.5.4 + version: 3.5.4 jspdf: specifier: ^3.0.3 version: 3.0.3 @@ -1262,6 +1265,9 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + jsencrypt@3.5.4: + resolution: {integrity: sha512-kNjfYEMNASxrDGsmcSQh/rUTmcoRfSUkxnAz+MMywM8jtGu+fFEZ3nJjHM58zscVnwR0fYmG9sGkTDjqUdpiwA==} + jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} @@ -2926,6 +2932,8 @@ snapshots: js-tokens@4.0.0: {} + jsencrypt@3.5.4: {} + jsesc@3.1.0: {} json5@2.2.3: {} diff --git a/src/assets/security/rsa/backend/private.pem.cpt b/src/assets/security/rsa/backend/private.pem.cpt new file mode 100644 index 0000000..f99d50b Binary files /dev/null and b/src/assets/security/rsa/backend/private.pem.cpt differ diff --git a/src/assets/security/rsa/backend/public.pem.cpt b/src/assets/security/rsa/backend/public.pem.cpt new file mode 100644 index 0000000..7b07c09 Binary files /dev/null and b/src/assets/security/rsa/backend/public.pem.cpt differ diff --git a/src/assets/security/rsa/frontend/private.pem.cpt b/src/assets/security/rsa/frontend/private.pem.cpt new file mode 100644 index 0000000..8e1cf65 Binary files /dev/null and b/src/assets/security/rsa/frontend/private.pem.cpt differ diff --git a/src/assets/security/rsa/frontend/public.pem.cpt b/src/assets/security/rsa/frontend/public.pem.cpt new file mode 100644 index 0000000..5e33d12 Binary files /dev/null and b/src/assets/security/rsa/frontend/public.pem.cpt differ diff --git a/src/pages/AssessorPage/Assessor.tsx b/src/pages/AssessorPage/Assessor.tsx index f5096b2..aa8c9c7 100644 --- a/src/pages/AssessorPage/Assessor.tsx +++ b/src/pages/AssessorPage/Assessor.tsx @@ -127,28 +127,28 @@ export default () => { const getopdetails = async (applicationNo: string) => { const op = await ofetch(API + 'get-opdetails-electrical/' + applicationNo, { parseResponse: JSON.parse }) - setAssessor(op.result7[0]) - setLocation(op.result5[0]) - setType(op.result6[0]) - setDateOp(dayjs(op.result10[0]).format('MMMM DD, YYYY')) - setApplicationId(op.result11[0]) - // setAssessorId(op.result12[0]) - setTotalOp(calculateTotal(op.result9)) + setLocation(op.result[0]) + setType(op.result2[0]) + setAssessor(op.result3[0]) + setTotalOp(calculateTotal(op.result4)) + setDateOp(dayjs(op.result5[0]).format('MMMM DD, YYYY')) + setApplicationId(op.result6[0]) + // setAssessorId(op.result12[0]) // setDescriptionList(op.result8) // setAmountList(op.result9) // setDateOpList(op.result10) // calculateAmounts() - getPrintDetails(op.result11[0]) - getPrintDetailsFees(op.result11[0]) + getPrintDetails(op.result6[0]) + getPrintDetailsFees(op.result6[0]) getSignatureImage(employeeId()) const approversignId = await geteSignId(276) getApprovedDate(approversignId, applicationNo) // const assessorId = await getEmployeeId(op.result7[0]) // const assessorsignId = await geteSignId(assessorId) - getAssessedDate(op.result11[0]) + getAssessedDate(op.result6[0]) } const calculateTotal = (list: number[]) => { diff --git a/src/utils/functions/encryptRsa.ts b/src/utils/functions/encryptRsa.ts new file mode 100644 index 0000000..5b3eeea --- /dev/null +++ b/src/utils/functions/encryptRsa.ts @@ -0,0 +1,12 @@ +const PUBLIC_KEY = import.meta.env.VITE_PUBLIC_KEY +import { JSEncrypt } from 'jsencrypt' + +const enc = new JSEncrypt() + +export default async (message: string) => { + enc.setPublicKey(PUBLIC_KEY) + const encrypted = enc.encrypt(message).toString() + const fixedEncrypted = encrypted.replace(/\//g, '~') + + return fixedEncrypted +} diff --git a/src/utils/functions/getApi.ts b/src/utils/functions/getApi.ts index 30b4523..c902370 100644 --- a/src/utils/functions/getApi.ts +++ b/src/utils/functions/getApi.ts @@ -6,9 +6,9 @@ export default async (api: string, value?: any) => { try { let fetch if (!value) { - fetch = await ofetch(API + api, { parseResponse: JSON.parse }) + fetch = await ofetch(API + api, { parseResponse: JSON.parse, retry: 3, retryDelay: 500, retryStatusCodes: [400, 404, 405, 500, 502] }) } else { - fetch = await ofetch(API + `${api}/${value}`, { parseResponse: JSON.parse }) + fetch = await ofetch(API + `${api}/${value}`, { parseResponse: JSON.parse, retry: 3, retryDelay: 500, retryStatusCodes: [400, 404, 405, 500, 502] }) } const result = fetch.result return result diff --git a/src/utils/functions/getApiMulti.ts b/src/utils/functions/getApiMulti.ts index 69d1b3e..c595379 100644 --- a/src/utils/functions/getApiMulti.ts +++ b/src/utils/functions/getApiMulti.ts @@ -6,9 +6,9 @@ export default async (api: string, value?: any) => { let fetch try { if (!value) { - fetch = await ofetch(API + api, { parseResponse: JSON.parse }) + fetch = await ofetch(API + api, { parseResponse: JSON.parse, retry: 3, retryDelay: 500, retryStatusCodes: [400, 404, 405, 500, 502] }) } else { - fetch = await ofetch(API + `${api}/${value}`, { parseResponse: JSON.parse }) + fetch = await ofetch(API + `${api}/${value}`, { parseResponse: JSON.parse, retry: 3, retryDelay: 500, retryStatusCodes: [400, 404, 405, 500, 502] }) } return fetch