feature/simplify ucan mpc did (#1195)

* feat: enable DID auth middleware

* feat: implement passkey creation flow

* feat: persist user address in cookie and retrieve user profile using address cookie

* feat: implement human verification challenge during session initialization

* refactor: remove unnecessary random number generation in profile creation

* refactor: rename credential validation handler and update related routes

* feat: improve profile validation and user experience

* feat: add page rendering for profile and passkey creation

* refactor: remove unused register handler and update routes

* refactor: remove unused imports and simplify credential validation

* fix: Correct insecure gRPC client connection

* refactor: rename models files for better organization

* refactor: refactor grpc client creation and management

* refactor: refactor common clients package

* <no value>

* feat: add CapAccount, CapInterchain, CapVault enums

* feat: add ChainId to ResAccount and ResInterchain

* feat: add asset code to resource account enumeration

* refactor: rename services package to providers

* feat: implement gateway database interactions

* refactor: move gateway repository to internal/gateway

* refactor: Migrate database provider to use sqlx

* refactor: Rename Vaults to VaultProvider in HTTPContext struct

* refactor: Migrate from GORM to sqlc Queries in database context methods

* refactor: Replace GORM with standard SQL and simplify database initialization

* refactor: Migrate session management from GORM to sqlc with type conversion

* refactor: Update import paths and model references in context package

* fix: Resolve session type conversion and middleware issues

* refactor: Migrate database from GORM to sqlx

* refactor: Move models to pkg/common, improve code structure

* refactor: move repository package to internal directory

* refactor: move gateway internal packages to context directory

* refactor: migrate database provider to use sqlx queries

* feat: add session ID to HTTP context and use it to load session data

* feat: implement vault creation API endpoint

* feat: add DIDKey generation from PubKey

* refactor: remove unused DIDAuth components

* refactor: move DID auth controller to vault context

* chore: remove unused DIDAuth package

* refactor: improve clarity of enclave refresh function

* feat: implement nonce-based key encryption for improved security

* feat: Add Export and Import methods with comprehensive tests for Enclave

* fix: Validate AES key length in keyshare encryption and decryption

* fix: Resolve key length validation by hashing input keys

* refactor: Update keyshare import to use protocol decoding

* feat: Refactor enclave encryption to support full enclave export/import

* refactor: Simplify Enclave interface methods by removing role parameter

* refactor: remove unnecessary serialization from enclave interface

* refactor: rename models package in gateway context

* refactor: rename keystore vault constants

* refactor: remove context parameter from Resolver methods

* feat: add CurrentBlock context function and update related components

* refactor: rename resolver.go to resolvers.go

* feat: Add SQLite random() generation for session and profile initialization

* refactor: Update SQL queries to use SQLite-style parameter placeholders

* refactor: Replace '?' placeholders with '$n' PostgreSQL parameter syntax

* <no value>

* refactor: refactor gateway to use middleware for database interactions and improve modularity

* feat: implement gateway for Sonr highway

* refactor: Remove unused gateway context and refactor cookie/header handling

* refactor: improve server initialization and middleware handling

* feat: implement human verification for profile creation

* feat: implement session management middleware

* refactor: refactor common models and config to internal package

* refactor: move env config to internal/config

* refactor: move database-related code to  directory

* refactor: move IPFS client to common package and improve code structure

* refactor: move querier to common package and rename to chain_query

* refactor: move webworker model to internal/models

* feat: add initial view template for Sonr.ID

* docs(concepts): Add documentation for cosmos-proto

* docs: move IBC transfer documentation to tools section

* refactor: rename initpkl.go to pkl_init.go for better naming consistency

* docs(theme): update dark mode toggle icons

* refactor: update sqlite3 driver to ncruces/go-sqlite3

* feat: add Vault model and database interactions

* refactor: Improve SQLite schema with better constraints and indexes

* chore: update project dependencies

* fix: use grpc.WithInsecure() for gRPC connection

* config: set localhost as default Sonr gRPC URL

* refactor: improve gateway middleware and refactor server initialization

* refactor: Remove foreign key pragma from schema SQL

* refactor: Remove foreign key constraints from database schema

* refactor: Convert primary key columns from INTEGER to TEXT

* refactor: Remove unnecessary redirect in error handling
This commit is contained in:
Prad Nukala
2024-12-16 20:29:54 +00:00
committed by GitHub
parent 6d27b926f6
commit 7c4586ce90
196 changed files with 4480 additions and 3192 deletions
+44
View File
@@ -0,0 +1,44 @@
package middleware
import (
"net/http"
"github.com/labstack/echo/v4"
"github.com/onsonr/sonr/internal/database/repository"
"github.com/onsonr/sonr/pkg/gateway/types"
)
func ListCredentials(c echo.Context, handle string) ([]*types.CredentialDescriptor, error) {
cc, ok := c.(*GatewayContext)
if !ok {
return nil, echo.NewHTTPError(http.StatusInternalServerError, "Credentials Context not found")
}
creds, err := cc.dbq.GetCredentialsByHandle(bgCtx(), handle)
if err != nil {
return nil, err
}
return types.CredentialArrayToDescriptors(creds), nil
}
func SubmitCredential(c echo.Context, cred *types.CredentialDescriptor) error {
origin := GetOrigin(c)
handle := GetHandle(c)
md := cred.ToModel(handle, origin)
cc, ok := c.(*GatewayContext)
if !ok {
return echo.NewHTTPError(http.StatusInternalServerError, "Credentials Context not found")
}
_, err := cc.dbq.InsertCredential(bgCtx(), repository.InsertCredentialParams{
Handle: handle,
CredentialID: md.CredentialID,
Origin: origin,
Type: md.Type,
Transports: md.Transports,
})
if err != nil {
return err
}
return nil
}
+40
View File
@@ -0,0 +1,40 @@
package middleware
import (
"database/sql"
"github.com/labstack/echo/v4"
"github.com/medama-io/go-useragent"
"github.com/onsonr/sonr/crypto/mpc"
"github.com/onsonr/sonr/internal/config/hway"
"github.com/onsonr/sonr/internal/database/repository"
"github.com/onsonr/sonr/pkg/common"
)
type GatewayContext struct {
echo.Context
agent useragent.UserAgent
id string
dbq *repository.Queries
ipfsClient common.IPFS
tokenStore common.IPFSTokenStore
stagedEnclaves map[string]mpc.Enclave
grpcAddr string
}
func UseGateway(env hway.Hway, ipc common.IPFS, db *sql.DB) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
ua := useragent.NewParser()
ctx := &GatewayContext{
agent: ua.Parse(c.Request().UserAgent()),
Context: c,
dbq: repository.New(db),
ipfsClient: ipc,
grpcAddr: env.GetSonrGrpcUrl(),
tokenStore: common.NewUCANStore(ipc),
}
return next(ctx)
}
}
}
+100
View File
@@ -0,0 +1,100 @@
package middleware
import (
"net/http"
"github.com/labstack/echo/v4"
"github.com/onsonr/sonr/internal/context"
"github.com/onsonr/sonr/internal/database/repository"
)
func CheckHandleUnique(c echo.Context, handle string) bool {
ctx, ok := c.(*GatewayContext)
if !ok {
return false
}
ok, err := ctx.dbq.CheckHandleExists(bgCtx(), handle)
if err != nil {
return false
}
if ok {
return false
}
context.WriteCookie(c, context.UserHandle, handle)
return true
}
func CreateProfile(c echo.Context) (*repository.Profile, error) {
ctx, ok := c.(*GatewayContext)
if !ok {
return nil, echo.NewHTTPError(http.StatusInternalServerError, "Profile Context not found")
}
address := c.FormValue("address")
handle := c.FormValue("handle")
origin := c.FormValue("origin")
name := c.FormValue("name")
profile, err := ctx.dbq.InsertProfile(bgCtx(), repository.InsertProfileParams{
Address: address,
Handle: handle,
Origin: origin,
Name: name,
})
if err != nil {
return nil, err
}
// Update session with profile id
sid := GetSessionID(c)
_, err = ctx.dbq.UpdateSessionWithProfileID(bgCtx(), repository.UpdateSessionWithProfileIDParams{
ProfileID: profile.ID,
ID: sid,
})
if err != nil {
return nil, err
}
return &profile, nil
}
func UpdateProfile(c echo.Context) (*repository.Profile, error) {
ctx, ok := c.(*GatewayContext)
if !ok {
return nil, echo.NewHTTPError(http.StatusInternalServerError, "Profile Context not found")
}
address := c.FormValue("address")
handle := c.FormValue("handle")
name := c.FormValue("name")
profile, err := ctx.dbq.UpdateProfile(bgCtx(), repository.UpdateProfileParams{
Address: address,
Handle: handle,
Name: name,
})
if err != nil {
return nil, err
}
return &profile, nil
}
func ReadProfile(c echo.Context) (*repository.Profile, error) {
ctx, ok := c.(*GatewayContext)
if !ok {
return nil, echo.NewHTTPError(http.StatusInternalServerError, "Profile Context not found")
}
handle := c.Param("handle")
profile, err := ctx.dbq.GetProfileByHandle(bgCtx(), handle)
if err != nil {
return nil, err
}
return &profile, nil
}
func DeleteProfile(c echo.Context) error {
ctx, ok := c.(*GatewayContext)
if !ok {
return echo.NewHTTPError(http.StatusInternalServerError, "Profile Context not found")
}
address := c.Param("address")
err := ctx.dbq.SoftDeleteProfile(bgCtx(), address)
if err != nil {
return err
}
return nil
}
+42
View File
@@ -0,0 +1,42 @@
package middleware
import (
"bytes"
"github.com/a-h/templ"
"github.com/labstack/echo/v4"
"github.com/onsonr/sonr/pkg/gateway/views"
)
func Render(c echo.Context, cmp templ.Component) error {
// Create a buffer to store the rendered HTML
buf := &bytes.Buffer{}
// Render the component to the buffer
err := cmp.Render(c.Request().Context(), buf)
if err != nil {
return err
}
// Set the content type
c.Response().Header().Set(echo.HeaderContentType, echo.MIMETextHTML)
// Write the buffered content to the response
_, err = c.Response().Write(buf.Bytes())
if err != nil {
return err
}
c.Response().WriteHeader(200)
return nil
}
func RenderError(c echo.Context, err error) error {
return Render(c, views.ErrorView(err.Error()))
}
func RenderInitial(c echo.Context) error {
return Render(c, views.InitialView())
}
func RenderLoading(c echo.Context) error {
return Render(c, views.LoadingView())
}
+113
View File
@@ -0,0 +1,113 @@
package middleware
import (
"errors"
"github.com/labstack/echo/v4"
"github.com/onsonr/sonr/pkg/common"
)
// CurrentBlock returns the current block
func CurrentBlock(c echo.Context) uint64 {
cc, ok := c.(*GatewayContext)
if !ok {
return 0
}
qc, err := common.NewNodeClient(cc.grpcAddr)
if err != nil {
return 0
}
resp, err := qc.Status(bgCtx(), &common.StatusRequest{})
if err != nil {
return 0
}
return resp.GetHeight()
}
// GetBankParams returns the bank params
func GetBankParams(c echo.Context) (*common.BankParamsResponse, error) {
cc, ok := c.(*GatewayContext)
if !ok {
return nil, errors.New("gateway context not found")
}
cl, err := common.NewBankClient(cc.grpcAddr)
if err != nil {
return nil, err
}
resp, err := cl.Params(bgCtx(), &common.BankParamsRequest{})
if err != nil {
return nil, err
}
return resp, nil
}
// GetDIDParams returns the DID params
func GetDIDParams(c echo.Context) (*common.DIDParamsResponse, error) {
cc, ok := c.(*GatewayContext)
if !ok {
return nil, errors.New("gateway context not found")
}
cl, err := common.NewDIDClient(cc.grpcAddr)
if err != nil {
return nil, err
}
resp, err := cl.Params(bgCtx(), &common.DIDParamsRequest{})
if err != nil {
return nil, err
}
return resp, nil
}
// GetDWNParams returns the DWN params
func GetDWNParams(c echo.Context) (*common.DWNParamsResponse, error) {
cc, ok := c.(*GatewayContext)
if !ok {
return nil, errors.New("gateway context not found")
}
cl, err := common.NewDWNClient(cc.grpcAddr)
if err != nil {
return nil, err
}
resp, err := cl.Params(bgCtx(), &common.DWNParamsRequest{})
if err != nil {
return nil, err
}
return resp, nil
}
// GetNodeStatus returns the node status
func GetNodeStatus(c echo.Context) (*common.StatusResponse, error) {
cc, ok := c.(*GatewayContext)
if !ok {
return nil, errors.New("gateway context not found")
}
cl, err := common.NewNodeClient(cc.grpcAddr)
if err != nil {
return nil, err
}
resp, err := cl.Status(bgCtx(), &common.StatusRequest{})
if err != nil {
return nil, err
}
return resp, nil
}
// GetSVCParams returns the SVC params
func GetSVCParams(c echo.Context) (*common.SVCParamsResponse, error) {
cc, ok := c.(*GatewayContext)
if !ok {
return nil, errors.New("gateway context not found")
}
cl, err := common.NewSVCClient(cc.grpcAddr)
if err != nil {
return nil, err
}
resp, err := cl.Params(bgCtx(), &common.SVCParamsRequest{})
if err != nil {
return nil, err
}
return resp, nil
}
+109
View File
@@ -0,0 +1,109 @@
package middleware
import (
gocontext "context"
"github.com/labstack/echo/v4"
"github.com/onsonr/sonr/internal/context"
"github.com/onsonr/sonr/internal/database"
)
func NewSession(c echo.Context) error {
cc, ok := c.(*GatewayContext)
if !ok {
return nil
}
baseSessionCreateParams := database.BaseSessionCreateParams(cc)
cc.id = baseSessionCreateParams.ID
if _, err := cc.dbq.CreateSession(bgCtx(), baseSessionCreateParams); err != nil {
return err
}
// Set Cookie
if err := context.WriteCookie(c, context.SessionID, cc.id); err != nil {
return err
}
return nil
}
// ForbiddenDevice returns true if the device is unavailable
func ForbiddenDevice(c echo.Context) bool {
cc, ok := c.(*GatewayContext)
if !ok {
return true
}
return cc.agent.IsBot() || cc.agent.IsTV()
}
func GetOrigin(c echo.Context) string {
return c.Request().Host
}
func GetSessionID(c echo.Context) string {
// Check from context
cc, ok := c.(*GatewayContext)
if !ok {
return ""
}
// check from cookie
if cc.id == "" {
if ok := context.CookieExists(c, context.SessionID); !ok {
return ""
}
cc.id = context.ReadCookieUnsafe(c, context.SessionID)
}
return cc.id
}
func GetSessionChallenge(c echo.Context) string {
cc, ok := c.(*GatewayContext)
if !ok {
return ""
}
s, err := cc.dbq.GetChallengeBySessionID(bgCtx(), cc.id)
if err != nil {
return ""
}
return s
}
func GetHandle(c echo.Context) string {
// First check for the cookie
handle := context.ReadCookieUnsafe(c, context.UserHandle)
if handle != "" {
return handle
}
// Then check the session
cc, ok := c.(*GatewayContext)
if !ok {
return ""
}
s, err := cc.dbq.GetSessionByID(bgCtx(), cc.id)
if err != nil {
return ""
}
profile, err := cc.dbq.GetProfileByID(bgCtx(), s.ProfileID)
if err != nil {
return ""
}
return profile.Handle
}
//
// func GetHumanVerificationNumbers(c echo.Context) (int64, int64) {
// cc, ok := c.(*GatewayContext)
// if !ok {
// return 0, 0
// }
// s, err := cc.dbq.GetHumanVerificationNumbers(bgCtx(), cc.id)
// if err != nil {
// return 0, 0
// }
// return s.IsHumanFirst, s.IsHumanLast
// }
// utility function to get a context
func bgCtx() gocontext.Context {
ctx := gocontext.Background()
return ctx
}
+57
View File
@@ -0,0 +1,57 @@
package middleware
import (
"fmt"
"github.com/labstack/echo/v4"
"github.com/onsonr/sonr/crypto/mpc"
"github.com/onsonr/sonr/internal/context"
"github.com/onsonr/sonr/pkg/gateway/types"
"lukechampine.com/blake3"
)
func Spawn(c echo.Context) (types.CreatePasskeyParams, error) {
cc := c.(*GatewayContext)
block := fmt.Sprintf("%d", CurrentBlock(c))
handle := GetHandle(c)
origin := GetOrigin(c)
challenge := GetSessionChallenge(c)
sid := GetSessionID(c)
nonce, err := calcNonce(sid)
if err != nil {
return types.DefaultCreatePasskeyParams(), err
}
encl, err := mpc.GenEnclave(nonce)
if err != nil {
return types.DefaultCreatePasskeyParams(), err
}
cc.stagedEnclaves[sid] = encl
context.WriteCookie(c, context.SonrAddress, encl.Address())
return types.CreatePasskeyParams{
Address: encl.Address(),
Handle: handle,
Name: origin,
Challenge: challenge,
CreationBlock: block,
}, nil
}
func Claim() (types.CreatePasskeyParams, error) {
return types.CreatePasskeyParams{}, nil
}
// Uses blake3 to hash the sessionID to generate a nonce of length 12 bytes
func calcNonce(sessionID string) ([]byte, error) {
hash := blake3.New(32, nil)
_, err := hash.Write([]byte(sessionID))
if err != nil {
return nil, err
}
// Read the hash into a byte slice
nonce := make([]byte, 12)
_, err = hash.Write(nonce)
if err != nil {
return nil, err
}
return nonce, nil
}