Skip to content

Commit

Permalink
Merge pull request #77 from teknologi-umum/fix/qr-ticketing
Browse files Browse the repository at this point in the history
fix: qr ticketing sending and verification
  • Loading branch information
aldy505 authored Oct 19, 2023
2 parents 90e05db + 4222401 commit 88e6801
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 18 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
--health-timeout 5s
--health-retries 5
smtp:
image: mailhog/mailhog
image: marlonb/mailcrab:latest
ports:
- 1025:1025
steps:
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -410,4 +410,5 @@ cython_debug/


./backend/*.sh
!./backend/blast-email.sh
!./backend/blast-email.sh
./backend/configuration.yml
77 changes: 77 additions & 0 deletions backend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Backend

## Developing

To develop the backend, just create integration test and make sure it's running on CI.

But, if you want to run it locally, copy the `configuration.example.yml` into `configuration.yml`,
then start the Docker Compose file on the root directory using `docker compose up -d postgres mailcrab`.

Your `configuration.yml` file should be similar to:

```yaml
feature_flags:
registration_closed: false

environment: local

database:
host: localhost
port: 5432
user: conference
password: VeryStrongPassword
database: conference

port: 8080

mailer:
hostname: localhost
port: 1025
from: administrator@localhost
password:

blob_url: file:///tmp/teknologi-umum-conference

signature:
public_key: 2bb6b9b9e1d9e12bfdd4196bfba6a081156ac...
private_key: 48d0ca64011fec1cb23b21820e9f7e880843e71f236b7f8decfe3568f...

validate_payment_key: 24326124313024514d56324d782f4a7342446f36363653784b324175657341...
```
Generate the `signature.public_key` and `signature.private_key` using this simple Go script:

```go
package main
import (
"crypto/ed25519"
"encoding/hex"
"fmt"
)
func main() {
pub, priv, _ := ed25519.GenerateKey(nil)
fmt.Println(hex.EncodeToString(pub))
fmt.Println(hex.EncodeToString(priv))
}
```

Generate the `validate_payment_key` using this simple Go script:

```go
package main
import (
"fmt"
"golang.org/x/crypto/bcrypt"
)
func main() {
passphrase := "test"
value, _ := bcrypt.GenerateFromPassword([]byte(passphrase), 10)
fmt.Printf("%x", value)
}
```
2 changes: 1 addition & 1 deletion backend/mailer.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func (m *Mailer) messageBuilder(ctx context.Context, mail *Mail) []byte {
msg.WriteString("Content-Transfer-Encoding: 8bit\n")
msg.WriteString("\n")
msg.WriteString(mail.PlainTextBody)
msg.WriteString("\n")
msg.WriteString("\n\n")
msg.WriteString("--" + alternateBoundary + "\n")
msg.WriteString("Content-Type: text/html; charset=\"utf-8\"\n")
msg.WriteString("Content-Transfer-Encoding: 8bit\n")
Expand Down
13 changes: 12 additions & 1 deletion backend/server.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"encoding/hex"
"errors"
"mime"
"net/http"
Expand Down Expand Up @@ -248,7 +249,17 @@ func (s *ServerDependency) DayTicketScan(c echo.Context) error {
}

// Validate key
if err := bcrypt.CompareHashAndPassword([]byte(s.validateTicketKey), []byte(requestBody.Key)); err != nil {
decodedPassphrase, err := hex.DecodeString(s.validateTicketKey)
if err != nil {
sentryHub.CaptureException(err)
return c.JSON(http.StatusInternalServerError, echo.Map{
"message": "Internal server error",
"errors": "Internal server error",
"request_id": requestId,
})
}

if err := bcrypt.CompareHashAndPassword(decodedPassphrase, []byte(requestBody.Key)); err != nil {
if errors.Is(err, bcrypt.ErrMismatchedHashAndPassword) {
return c.JSON(http.StatusForbidden, echo.Map{
"message": "Wrong passphrase",
Expand Down
8 changes: 4 additions & 4 deletions backend/ticketing.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ func (t *TicketDomain) VerifyTicket(ctx context.Context, payload []byte) (email
return "", "", false, ErrInvalidTicket
}

ticketId, err := uuid.FromBytes(rawTicketId)
ticketId, err := uuid.ParseBytes(rawTicketId)
if err != nil {
return "", "", false, ErrInvalidTicket
}
Expand Down Expand Up @@ -380,20 +380,20 @@ func (t *TicketDomain) VerifyTicket(ctx context.Context, payload []byte) (email

tx, err := conn.BeginTx(ctx, pgx.TxOptions{
IsoLevel: pgx.RepeatableRead,
AccessMode: pgx.ReadOnly,
AccessMode: pgx.ReadWrite,
})
if err != nil {
return "", "", false, fmt.Errorf("creating transaction: %w", err)
}

err = tx.QueryRow(ctx, "SELECT email, student FROM ticketing WHERE id = $1", ticketId).Scan(&email, &student)
err = tx.QueryRow(ctx, "SELECT email, student FROM ticketing WHERE id = $1 AND used = FALSE", ticketId).Scan(&email, &student)
if err != nil {
if e := tx.Rollback(ctx); e != nil {
return "", "", false, fmt.Errorf("rolling back transaction: %w (%s)", e, err.Error())
}

if errors.Is(err, pgx.ErrNoRows) {
return "", "", false, ErrInvalidTicket
return "", "", false, fmt.Errorf("%w (id not exists, or ticket has been used)", ErrInvalidTicket)
}

return "", "", false, fmt.Errorf("acquiring data from table: %w", err)
Expand Down
8 changes: 4 additions & 4 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ services:
options:
max-size: 10M

mailhog:
image: mailhog/mailhog:latest
mailcrab:
image: marlonb/mailcrab:latest
ports:
- 127.0.0.1:1025:1025
- 127.0.0.1:8025:8025
- 127.0.0.1:8025:1080
deploy:
mode: replicated
replicas: 1
Expand Down Expand Up @@ -94,7 +94,7 @@ services:
condition: service_completed_successfully
postgres:
condition: service_healthy
mailhog:
mailcrab:
condition: service_started
logging:
driver: local
Expand Down
20 changes: 14 additions & 6 deletions frontend/pages/verify.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,15 @@ const key = ref([])
const config = useRuntimeConfig();
const alertClass = ref<null|boolean>(null);
const paused = ref(false)
const invalidTicketReason = ref<string>("");
interface ScanResponse {
message: string
student: boolean
name: string
email: string
}
const onDetect = async (a: any) => {
const response = await useFetch(`${config.public.backendBaseUrl}/scan-tiket`, {
const response = await useFetch<ScanResponse>(`${config.public.backendBaseUrl}/scan-tiket`, {
method: "POST",
body: {
code: a[0].rawValue,
Expand All @@ -23,12 +29,14 @@ const onDetect = async (a: any) => {
if (response.error.value?.statusCode && [406, 403].includes(response.error.value?.statusCode)) {
alertClass.value = false
invalidTicketReason.value = response.error.value?.data.errors;
} else {
const body = response.data.value;
alertClass.value = true
detectedUser.value = {
name: 'Aji',
student: true,
institution: "PT Mencari Cinta Sejati"
name: body?.name,
student: body?.student,
email: body?.email,
}
}
setTimeout(() => {
Expand All @@ -45,7 +53,7 @@ const scanNext = () => {
<div id="page">
<SinglePage title="Verify Guest">
<div :class="[`alert mb-5`, alertClass ? 'alert-success' : 'alert-danger']" v-if="alertClass !== null">
{{ alertClass ? 'User verified!' : 'Invalid ticket' }}
{{ alertClass ? 'User verified!' : invalidTicketReason }}
</div>
<template v-if="!detectedUser">
<input type="text" class="form-control-lg mb-5" placeholder="Key" v-model="key">
Expand Down

0 comments on commit 88e6801

Please sign in to comment.