Skip to content

Commit

Permalink
Merge pull request #6 from rarimo/feature/passport-scan-verify
Browse files Browse the repository at this point in the history
Feature: connector and endpoint for passport scan verification
  • Loading branch information
violog authored Feb 28, 2024
2 parents 0c0deea + 4c85d77 commit ab91091
Show file tree
Hide file tree
Showing 18 changed files with 312 additions and 69 deletions.
1 change: 1 addition & 0 deletions .github/workflows/code-review.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ jobs:
with:
reporter: github-pr-review
cache: false
golangci_lint_flags: --timeout=2m
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ require (
github.com/rubenv/sql-migrate v1.6.1
gitlab.com/distributed_lab/ape v1.7.1
gitlab.com/distributed_lab/figure/v3 v3.1.3
gitlab.com/distributed_lab/json-api-connector v0.2.7
gitlab.com/distributed_lab/kit v1.11.2
gitlab.com/distributed_lab/logan v3.8.1+incompatible
gitlab.com/distributed_lab/running v0.0.0-20200706131153-4af0e83eb96c
Expand Down Expand Up @@ -62,6 +63,7 @@ require (
github.com/dustin/go-humanize v1.0.1-0.20200219035652-afde56e7acac // indirect
github.com/dvsekhvalnov/jose2go v1.5.0 // indirect
github.com/ethereum/c-kzg-4844 v0.4.0 // indirect
github.com/fatih/structs v1.1.0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/getsentry/raven-go v0.2.0 // indirect
github.com/getsentry/sentry-go v0.26.0 // indirect
Expand Down Expand Up @@ -144,7 +146,7 @@ require (
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/zondax/hid v0.9.1 // indirect
github.com/zondax/ledger-go v0.14.1 // indirect
gitlab.com/distributed_lab/figure v2.1.0+incompatible // indirect
gitlab.com/distributed_lab/figure v2.1.2+incompatible // indirect
gitlab.com/distributed_lab/lorem v0.2.1 // indirect
go.etcd.io/bbolt v1.3.6 // indirect
go.uber.org/multierr v1.11.0 // indirect
Expand Down
6 changes: 5 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,7 @@ github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqL
github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpmbhCOZJ293Lz68O7PYrF2EzeiFMwCLk=
github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0=
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o=
github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
Expand Down Expand Up @@ -1373,10 +1374,13 @@ github.com/zondax/ledger-go v0.14.1/go.mod h1:fZ3Dqg6qcdXWSOJFKMG8GCTnD7slO/RL2f
gitlab.com/distributed_lab/ape v1.6.1/go.mod h1:Qy9Y2arL0hmZIpVpctGEFhdrVsjWtyVJ5G+bZWcFT4s=
gitlab.com/distributed_lab/ape v1.7.1 h1:LpTmZgG7Lvx6ulopQbH2aWI3s8ey9FsKVjbic3ZQIy4=
gitlab.com/distributed_lab/ape v1.7.1/go.mod h1:Qy9Y2arL0hmZIpVpctGEFhdrVsjWtyVJ5G+bZWcFT4s=
gitlab.com/distributed_lab/figure v2.1.0+incompatible h1:8kNtvWO91BSQ4OsqL2P3qNWSBnh/Q/TdWB8vHy8xvNI=
gitlab.com/distributed_lab/figure v2.1.0+incompatible/go.mod h1:tk+aPBohT49MGPLy5+eVbE1HpD/CaC5drBHfVpRI8eE=
gitlab.com/distributed_lab/figure v2.1.2+incompatible h1:xO1KCYPK9KFx6OUBOaJ62d8vYd1R3aNgidHlC/ZtVBA=
gitlab.com/distributed_lab/figure v2.1.2+incompatible/go.mod h1:tk+aPBohT49MGPLy5+eVbE1HpD/CaC5drBHfVpRI8eE=
gitlab.com/distributed_lab/figure/v3 v3.1.3 h1:gCHplT1Ih8B1s4eYTeAhRZyto3gIWoUCUj3yYfNM4r8=
gitlab.com/distributed_lab/figure/v3 v3.1.3/go.mod h1:gYbCEdQBQCVEg+ap0zrpjY56BU95k9H8ELebL1ChONo=
gitlab.com/distributed_lab/json-api-connector v0.2.7 h1:cwKDOxY/WLNFUJqpj90gGwnrdOZctQPD6RiTEJ7rNw4=
gitlab.com/distributed_lab/json-api-connector v0.2.7/go.mod h1:/jNqcDl22LxF06EOYsU8DvLpYwB5okFvesDotsj4ClA=
gitlab.com/distributed_lab/kit v1.11.2 h1:3GYAVe/ih5fvFuM/44zIorv9mUyD3JBQe/5v+GL7x+k=
gitlab.com/distributed_lab/kit v1.11.2/go.mod h1:MZj5Vb71YBWJ2wLAb9fDvlCYKewmNDNVWjAiERwgbdA=
gitlab.com/distributed_lab/logan v3.7.2+incompatible/go.mod h1:25oL/FPFXmyYzWeA6vahMvnFJV8P7mOx0jZhRP7nhlc=
Expand Down
5 changes: 0 additions & 5 deletions internal/config/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,13 @@ import (
"github.com/rarimo/rarime-points-svc/internal/service/workers/sbtcheck"
"github.com/rarimo/saver-grpc-lib/broadcaster"
"gitlab.com/distributed_lab/kit/comfig"
"gitlab.com/distributed_lab/kit/copus"
"gitlab.com/distributed_lab/kit/copus/types"
"gitlab.com/distributed_lab/kit/kv"
"gitlab.com/distributed_lab/kit/pgdb"
)

type Config interface {
comfig.Logger
pgdb.Databaser
types.Copuser
comfig.Listenerer
auth.Auther
broadcaster.Broadcasterer
Expand All @@ -28,7 +25,6 @@ type Config interface {
type config struct {
comfig.Logger
pgdb.Databaser
types.Copuser
comfig.Listenerer
auth.Auther
broadcaster.Broadcasterer
Expand All @@ -43,7 +39,6 @@ func New(getter kv.Getter) Config {
return &config{
getter: getter,
Databaser: pgdb.NewDatabaser(getter),
Copuser: copus.NewCopuser(getter),
Listenerer: comfig.NewListenerer(getter),
Logger: comfig.NewLogger(getter, comfig.LoggerOpts{}),
Auther: auth.NewAuther(getter), //nolint:misspell
Expand Down
2 changes: 1 addition & 1 deletion internal/data/pg/balances.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func NewBalances(db *pgdb.DB) data.BalancesQ {
}

func (q *balances) New() data.BalancesQ {
return NewBalances(q.db.Clone())
return NewBalances(q.db)
}

func (q *balances) Insert(bal data.Balance) error {
Expand Down
2 changes: 1 addition & 1 deletion internal/data/pg/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func NewEvents(db *pgdb.DB) data.EventsQ {
}

func (q *events) New() data.EventsQ {
return NewEvents(q.db.Clone())
return NewEvents(q.db)
}

func (q *events) Insert(events ...data.Event) error {
Expand Down
2 changes: 1 addition & 1 deletion internal/data/pg/withdrawals.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func NewWithdrawals(db *pgdb.DB) data.WithdrawalsQ {
}

func (q *withdrawals) New() data.WithdrawalsQ {
return NewWithdrawals(q.db.Clone())
return NewWithdrawals(q.db)
}

func (q *withdrawals) Insert(w data.Withdrawal) (*data.Withdrawal, error) {
Expand Down
28 changes: 28 additions & 0 deletions internal/service/handlers/middleware.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package handlers

import (
"context"
"net/http"

"github.com/rarimo/auth-svc/pkg/auth"
"github.com/rarimo/rarime-points-svc/internal/data/pg"
"gitlab.com/distributed_lab/ape"
"gitlab.com/distributed_lab/ape/problems"
"gitlab.com/distributed_lab/kit/pgdb"
"gitlab.com/distributed_lab/logan/v3"
)

Expand All @@ -29,3 +32,28 @@ func AuthMiddleware(auth *auth.Client, log *logan.Entry) func(http.Handler) http
})
}
}

type ctxExtender func(context.Context) context.Context

// DBCloneMiddleware is designed to clone DB session on each request. You must
// put all new DB handlers here instead of ape.CtxMiddleware, unless you have a
// reason to do otherwise.
func DBCloneMiddleware(db *pgdb.DB) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
clone := db.Clone()
ctx := r.Context()

extenders := []ctxExtender{
CtxEventsQ(pg.NewEvents(clone)),
CtxBalancesQ(pg.NewBalances(clone)),
CtxWithdrawalsQ(pg.NewWithdrawals(clone)),
}

for _, extender := range extenders {
ctx = extender(ctx)
}
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}
91 changes: 91 additions & 0 deletions internal/service/handlers/verify_passport.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package handlers

import (
"fmt"
"net/http"

"github.com/rarimo/rarime-points-svc/internal/data"
"github.com/rarimo/rarime-points-svc/internal/data/evtypes"
"github.com/rarimo/rarime-points-svc/internal/service/requests"
"gitlab.com/distributed_lab/ape"
"gitlab.com/distributed_lab/ape/problems"
)

func VerifyPassport(w http.ResponseWriter, r *http.Request) {
req, err := requests.NewVerifyPassport(r)
if err != nil {
ape.RenderErr(w, problems.BadRequest(err)...)
return
}

balance, err := BalancesQ(r).FilterByDID(req.UserDID).Get()
if err != nil {
Log(r).WithError(err).Error("Failed to get balance by DID")
ape.RenderErr(w, problems.InternalError())
return
}
if balance == nil {
ape.RenderErr(w, problems.NotFound())
return
}

err = EventsQ(r).Transaction(func() error {
// If you make this endpoint public, you should check the passport hash for
// uniqueness and provide a better validation. Think about other changes too.
err = BalancesQ(r).FilterByDID(req.UserDID).SetPassport(req.Hash, req.Expiry)
if err != nil {
return fmt.Errorf("set passport for balance by DID: %w", err)
}

evType := EventTypes(r).Get(evtypes.TypeReferralSpecific, evtypes.FilterInactive)
if evType == nil {
Log(r).Debug("Referral event type is disabled or expired, not accruing points to referrer")
return nil
}

refDID, err := getReferrerDID(*balance, r)
if err != nil {
return fmt.Errorf("get referrer DID by referred_by: %w", err)
}
if refDID == "" {
return nil
}

err = EventsQ(r).Insert(data.Event{
UserDID: refDID,
Type: evType.Name,
Status: data.EventFulfilled,
})
if err != nil {
return fmt.Errorf("add event for referrer: %w", err)
}

return nil
})

if err != nil {
Log(r).WithError(err).Error("Failed to set passport and add event for referrer")
ape.RenderErr(w, problems.InternalError())
return
}

w.WriteHeader(http.StatusNoContent)
}

func getReferrerDID(balance data.Balance, r *http.Request) (string, error) {
if !balance.ReferredBy.Valid {
return "", nil
}

refBy := balance.ReferredBy.String
referrer, err := BalancesQ(r).FilterByReferralID(refBy).Get()
if err != nil {
return "", fmt.Errorf("failed to get balance by referral ID: %w", err)
}
if referrer == nil {
return "", fmt.Errorf("referrer not found: %s", refBy)
}

Log(r).Debugf("Found referrer: DID=%s", referrer.DID)
return referrer.DID, nil
}
43 changes: 0 additions & 43 deletions internal/service/main.go

This file was deleted.

36 changes: 36 additions & 0 deletions internal/service/requests/verify_passport.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package requests

import (
"encoding/json"
"errors"
"net/http"
"time"

validation "github.com/go-ozzo/ozzo-validation/v4"
"github.com/rarimo/rarime-points-svc/pkg/connector"
)

func NewVerifyPassport(r *http.Request) (req connector.VerifyPassportRequest, err error) {
if err = json.NewDecoder(r.Body).Decode(&req); err != nil {
return
}

return req, validation.Errors{
"user_did": validation.Validate(req.UserDID, validation.Required),
"hash": validation.Validate(req.Hash, validation.Required),
"expiry": validation.Validate(req.Expiry, validation.Required, validation.By(isNotExpiredRule)),
}.Filter()
}

func isNotExpiredRule(value interface{}) error {
v, ok := value.(time.Time)
if !ok {
panic("value is not a time.Time") // invalid function usage
}

if v.Before(time.Now().UTC()) {
return errors.New("expiry is in the past")
}

return nil
}
33 changes: 19 additions & 14 deletions internal/service/router.go
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
package service

import (
"context"

"github.com/go-chi/chi"
"github.com/rarimo/rarime-points-svc/internal/data/pg"
"github.com/rarimo/rarime-points-svc/internal/config"
"github.com/rarimo/rarime-points-svc/internal/service/handlers"
"gitlab.com/distributed_lab/ape"
)

func (s *service) router() chi.Router {
func Run(ctx context.Context, cfg config.Config) {
r := chi.NewRouter()

r.Use(
ape.RecoverMiddleware(s.log),
ape.LoganMiddleware(s.log),
ape.RecoverMiddleware(cfg.Log()),
ape.LoganMiddleware(cfg.Log()),
ape.CtxMiddleware(
handlers.CtxLog(s.log),
handlers.CtxEventsQ(pg.NewEvents(s.cfg.DB())),
handlers.CtxBalancesQ(pg.NewBalances(s.cfg.DB())),
handlers.CtxWithdrawalsQ(pg.NewWithdrawals(s.cfg.DB())),
handlers.CtxEventTypes(s.cfg.EventTypes()),
handlers.CtxBroadcaster(s.cfg.Broadcaster()),
handlers.CtxPointPrice(s.cfg.PointPrice()),
handlers.CtxLog(cfg.Log()),
handlers.CtxEventTypes(cfg.EventTypes()),
handlers.CtxBroadcaster(cfg.Broadcaster()),
handlers.CtxPointPrice(cfg.PointPrice()),
),
handlers.DBCloneMiddleware(cfg.DB()),
)
r.Route("/integrations/rarime-points-svc/v1", func(r chi.Router) {
r.Route("/balances", func(r chi.Router) {
r.Use(handlers.AuthMiddleware(s.cfg.Auth(), s.log))
r.Use(handlers.AuthMiddleware(cfg.Auth(), cfg.Log()))
r.Post("/", handlers.CreateBalance)
r.Route("/{did}", func(r chi.Router) {
r.Get("/", handlers.GetBalance)
Expand All @@ -34,13 +34,18 @@ func (s *service) router() chi.Router {
})
})
r.Route("/events", func(r chi.Router) {
r.Use(handlers.AuthMiddleware(s.cfg.Auth(), s.log))
r.Use(handlers.AuthMiddleware(cfg.Auth(), cfg.Log()))
r.Get("/", handlers.ListEvents)
r.Patch("/{id}", handlers.ClaimEvent)
})
r.Get("/balances", handlers.Leaderboard)
r.Get("/point_price", handlers.GetPointPrice)
// must be accessible only within the cluster
r.Route("/private", func(r chi.Router) {
r.Patch("/balances", handlers.VerifyPassport)
})
})

return r
cfg.Log().Info("Service started")
ape.Serve(ctx, r, cfg, ape.ServeOpts{})
}
Loading

0 comments on commit ab91091

Please sign in to comment.