Skip to content

Commit

Permalink
feature: optional expiration lower bound param; expand JWT auth endpo…
Browse files Browse the repository at this point in the history
…ints (#18)

* update (docs): adding expiration lower bound param in paths via query params or request body

* update (migrations): add expiration_lower_bound column

* update (data): handle ExpirationLowerBound field during insert

* update (helpers): move selector params in separate structure, use BirthDateFormat as general DateFormat for params, add expiration lower bound handling to build selector and set proof params

* update (service): to use refactored helpers package, handle expiration lower bound for verification links and proof params

* fix: expiration lower bound default value during Inserting new VerifyUser instance; move default timestamp value in constant

* update: Auth middleware to store claims in ctx for future check in endpoints

* feature: add authentication for sensitive endpoints

* fix: make auth service optional using Enabled config field, set it disabled by default, handle disabled client in helpers authenticates method

* fix: build event data in the same way as smart contract does

* fix: use user id keccak256 hash in event data

* add: extra logs to debug

* fix: ExtractEventData without prepending

* feature: implement upsert to update params for users with the same user id

* add: helper function to check default ZK date

* update: use Upsert instead of Get/Insert approach, use helper default zk date for clear code

* update: auth version

* fix: var-naming linter error
  • Loading branch information
mhrynenko authored Dec 16, 2024
1 parent 4e668e9 commit 3a66346
Show file tree
Hide file tree
Showing 23 changed files with 333 additions and 234 deletions.
1 change: 1 addition & 0 deletions config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,5 @@ poseidonsmt_root_verifier:
request_timeout: 10s

auth:
enabled: false
addr: http://rarime-auth
10 changes: 10 additions & 0 deletions docs/spec/components/parameters/expirationLowerBoundParam.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
in: query
name: 'expiration_lower_bound'
required: false
description: |
Param to enable or disable passport expiration lower bound check.\n
- Empty value or `false` - default date is used (52983525027888)
- `true` - encoded current UTC timestamp is used
example: true
schema:
type: boolean
7 changes: 6 additions & 1 deletion docs/spec/components/schemas/User.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,9 @@ allOf:
sex:
type: boolean
example: true
description: "Enable verification of sex param"
description: "Enable verification of sex param"
expiration_lower_bound:
type: boolean
example: true
description: "Enable verification of expiration lower bound param. When nothing (or `false`) set default value is used,
otherwise encoded current UTC timestamp will be stored."
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ get:
- $ref: '#/components/parameters/typeOfVerificationUniquenessParam'
- $ref: '#/components/parameters/nationalityParam'
- $ref: '#/components/parameters/eventIdParam'
- $ref: '#/components/parameters/expirationLowerBoundParam'
responses:
200:
description: Success
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@ require (
github.com/ethereum/go-ethereum v1.14.12
github.com/go-chi/chi v4.1.2+incompatible
github.com/go-ozzo/ozzo-validation/v4 v4.3.0
github.com/iden3/go-iden3-crypto v0.0.15
github.com/iden3/go-rapidsnark/types v0.0.3
github.com/pkg/errors v0.9.1
github.com/rarimo/web3-auth-svc v0.1.1-rc0
github.com/rarimo/web3-auth-svc v0.1.4
github.com/rarimo/zkverifier-kit v1.2.4
github.com/rubenv/sql-migrate v1.7.0
github.com/status-im/keycard-go v0.2.0
Expand Down Expand Up @@ -49,6 +48,7 @@ require (
github.com/gorilla/websocket v1.5.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/holiman/uint256 v1.3.1 // indirect
github.com/iden3/go-iden3-crypto v0.0.15 // indirect
github.com/iden3/go-rapidsnark/verifier v0.0.5 // indirect
github.com/jmoiron/sqlx v1.3.5 // indirect
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1962,8 +1962,8 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rarimo/web3-auth-svc v0.1.1-rc0 h1:Gqz2rU7vUgQGQDUBHw7wnakBkXsUzFsepd+oU5lOLY0=
github.com/rarimo/web3-auth-svc v0.1.1-rc0/go.mod h1:d1SsDcwzSqSPZABy3wMhrVrhNb/JQ40DLcjxTm0KPBQ=
github.com/rarimo/web3-auth-svc v0.1.4 h1:p57MgO4belYkELTVvWRO4979Z1WshujajTnDcjzf2PM=
github.com/rarimo/web3-auth-svc v0.1.4/go.mod h1:d1SsDcwzSqSPZABy3wMhrVrhNb/JQ40DLcjxTm0KPBQ=
github.com/rarimo/zkverifier-kit v1.2.4 h1:AJ5ZAyOYOGR2QiDlOA2ul/QMZnjBZ/VzPqLjSIUbZgw=
github.com/rarimo/zkverifier-kit v1.2.4/go.mod h1:3YDg5dTkDRr4IdfaDHGYetopd6gS/2SuwSeseYTWwNw=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
Expand Down
4 changes: 4 additions & 0 deletions internal/assets/migrations/006_expirationLowerBound.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- +migrate Up
ALTER TABLE verify_users ADD COLUMN expiration_lower_bound TEXT NOT NULL DEFAULT '0x303030303030';
-- +migrate Down
ALTER TABLE verify_users DROP COLUMN expiration_lower_bound;
57 changes: 32 additions & 25 deletions internal/data/pg/verify_users.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@ import (
"fmt"
"time"

"github.com/pkg/errors"
"gitlab.com/distributed_lab/kit/pgdb"

sq "github.com/Masterminds/squirrel"

"github.com/pkg/errors"
"github.com/rarimo/verificator-svc/internal/data"
"gitlab.com/distributed_lab/kit/pgdb"
)

type VerifyUsersQ struct {
Expand Down Expand Up @@ -62,33 +60,42 @@ func (q *VerifyUsersQ) Get() (*data.VerifyUsers, error) {
return &result, nil
}

func (q *VerifyUsersQ) Insert(VerifyUsers *data.VerifyUsers) error {
func (q *VerifyUsersQ) Upsert(VerifyUsers *data.VerifyUsers) (data.VerifyUsers, error) {
var response data.VerifyUsers
proofJSON, err := json.Marshal(VerifyUsers.Proof)
if err != nil {
return fmt.Errorf("failed to marshal proof for user %s: %w", VerifyUsers.UserID, err)
return response, fmt.Errorf("failed to marshal proof for user %s: %w", VerifyUsers.UserID, err)
}

stmt := sq.Insert(verifyUsersTableName).SetMap(map[string]interface{}{
"user_id": VerifyUsers.UserID,
"user_id_hash": VerifyUsers.UserIDHash,
"age_lower_bound": VerifyUsers.AgeLowerBound,
"nationality": VerifyUsers.Nationality,
"uniqueness": VerifyUsers.Uniqueness,
"event_id": VerifyUsers.EventId,
"status": VerifyUsers.Status,
"proof": proofJSON,
"sex": VerifyUsers.Sex,
"sex_enable": VerifyUsers.SexEnable,
"nationality_enable": VerifyUsers.NationalityEnable,
"anonymous_id": VerifyUsers.AnonymousID,
"nullifier": VerifyUsers.Nullifier,
})

if err = q.db.Exec(stmt); err != nil {
return fmt.Errorf("insert user %+v: %w", VerifyUsers, err)
newData := map[string]interface{}{
"user_id_hash": VerifyUsers.UserIDHash,
"age_lower_bound": VerifyUsers.AgeLowerBound,
"nationality": VerifyUsers.Nationality,
"uniqueness": VerifyUsers.Uniqueness,
"event_id": VerifyUsers.EventID,
"status": VerifyUsers.Status,
"proof": proofJSON,
"sex": VerifyUsers.Sex,
"sex_enable": VerifyUsers.SexEnable,
"nationality_enable": VerifyUsers.NationalityEnable,
"anonymous_id": VerifyUsers.AnonymousID,
"nullifier": VerifyUsers.Nullifier,
"expiration_lower_bound": VerifyUsers.ExpirationLowerBound,
}

return nil
updateStmt, args, _ := sq.Update(" ").SetMap(newData).ToSql()

newData["user_id"] = VerifyUsers.UserID

query := sq.Insert(verifyUsersTableName).SetMap(newData).
Suffix("ON CONFLICT (user_id) DO "+updateStmt, args...).
Suffix("RETURNING *")

if err = q.db.Get(&response, query); err != nil {
return response, errors.Wrap(err, "failed to upsert new row")
}

return response, nil
}

func (q *VerifyUsersQ) Update(VerifyUsers *data.VerifyUsers) error {
Expand Down
31 changes: 16 additions & 15 deletions internal/data/verify_users.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,21 @@ import (
)

type VerifyUsers struct {
UserID string `db:"user_id"`
UserIDHash string `db:"user_id_hash"`
AgeLowerBound int `db:"age_lower_bound"`
Nationality string `db:"nationality"`
CreatedAt time.Time `db:"created_at"`
Uniqueness bool `db:"uniqueness"`
EventId string `db:"event_id"`
Status string `db:"status"`
Proof []byte `db:"proof"`
Sex string `db:"sex"`
SexEnable bool `db:"sex_enable"`
NationalityEnable bool `db:"nationality_enable"`
AnonymousID string `db:"anonymous_id"`
Nullifier string `db:"nullifier"`
UserID string `db:"user_id"`
UserIDHash string `db:"user_id_hash"`
AgeLowerBound int `db:"age_lower_bound"`
Nationality string `db:"nationality"`
CreatedAt time.Time `db:"created_at"`
Uniqueness bool `db:"uniqueness"`
EventID string `db:"event_id"`
Status string `db:"status"`
Proof []byte `db:"proof"`
Sex string `db:"sex"`
SexEnable bool `db:"sex_enable"`
NationalityEnable bool `db:"nationality_enable"`
AnonymousID string `db:"anonymous_id"`
Nullifier string `db:"nullifier"`
ExpirationLowerBound string `db:"expiration_lower_bound"`
}

type VerifyUsersQ interface {
Expand All @@ -27,7 +28,7 @@ type VerifyUsersQ interface {
Get() (*VerifyUsers, error)
Select() ([]VerifyUsers, error)
Update(*VerifyUsers) error
Insert(*VerifyUsers) error
Upsert(*VerifyUsers) (VerifyUsers, error)
Delete() error

DeleteByID(*VerifyUsers) error
Expand Down
29 changes: 26 additions & 3 deletions internal/service/handlers/ctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package handlers

import (
"context"
"github.com/rarimo/verificator-svc/internal/config"
"github.com/rarimo/verificator-svc/internal/data"
"net/http"

"github.com/rarimo/verificator-svc/internal/config"
"github.com/rarimo/verificator-svc/internal/data"
"github.com/rarimo/web3-auth-svc/pkg/auth"
"github.com/rarimo/web3-auth-svc/resources"
"gitlab.com/distributed_lab/logan/v3"
)

Expand All @@ -16,8 +18,9 @@ const (
verifyUserQCtxKey
verifiersCtxKey
callbackCtxKey
proofParametersCtxKey
userClaimsCtxKey
signatureVerificationCtxKey
authCtxKey
)

func CtxLog(entry *logan.Entry) func(context.Context) context.Context {
Expand Down Expand Up @@ -69,3 +72,23 @@ func CtxSignatureVerification(c config.SignatureVerificationConfig) func(context
func SignatureVerification(r *http.Request) config.SignatureVerificationConfig {
return r.Context().Value(signatureVerificationCtxKey).(config.SignatureVerificationConfig)
}

func CtxUserClaims(claim []resources.Claim) func(context.Context) context.Context {
return func(ctx context.Context) context.Context {
return context.WithValue(ctx, userClaimsCtxKey, claim)
}
}

func UserClaims(r *http.Request) []resources.Claim {
return r.Context().Value(userClaimsCtxKey).([]resources.Claim)
}

func CtxAuthClient(auth *auth.Client) func(context.Context) context.Context {
return func(ctx context.Context) context.Context {
return context.WithValue(ctx, authCtxKey, auth)
}
}

func AuthClient(r *http.Request) *auth.Client {
return r.Context().Value(authCtxKey).(*auth.Client)
}
15 changes: 14 additions & 1 deletion internal/service/handlers/delete_user.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,32 @@
package handlers

import (
"net/http"

"github.com/rarimo/verificator-svc/internal/service/handlers/helpers"
"github.com/rarimo/verificator-svc/internal/service/requests"
"github.com/rarimo/web3-auth-svc/pkg/auth"
"gitlab.com/distributed_lab/ape"
"gitlab.com/distributed_lab/ape/problems"
"net/http"
)

func DeleteUser(w http.ResponseWriter, r *http.Request) {
if !auth.Authenticates(UserClaims(r), auth.AdminGrant) {
ape.RenderErr(w, problems.Unauthorized())
return
}

userID, err := requests.GetPathUserID(r)
if err != nil {
ape.RenderErr(w, problems.BadRequest(err)...)
return
}

if !helpers.Authenticates(AuthClient(r), UserClaims(r), auth.UserGrant(userID)) {
ape.RenderErr(w, problems.Unauthorized())
return
}

deletedUser, err := VerifyUsersQ(r).WhereID(userID).Get()
if err != nil {
Log(r).WithError(err).Error("failed to get user by userID")
Expand Down
Loading

0 comments on commit 3a66346

Please sign in to comment.