GQLUser improvement

graph-rework-2
noah metz 2023-07-20 00:24:22 -06:00
parent 2fde6ae282
commit 4dc42a813e
6 changed files with 245 additions and 119 deletions

@ -185,6 +185,11 @@ func NewContext(db * badger.DB, log Logger) * Context {
panic(err) panic(err)
} }
err = ctx.RegisterNodeType(NewNodeDef((*GQLUser)(nil), LoadGQLUser, GQLTypeGQLUser()))
if err != nil {
panic(err)
}
ctx.AddGQLType(GQLTypeSignal()) ctx.AddGQLType(GQLTypeSignal())
ctx.GQL.Query.AddFieldConfig("Self", GQLQuerySelf()) ctx.GQL.Query.AddFieldConfig("Self", GQLQuerySelf())

246
gql.go

@ -10,6 +10,7 @@ import (
"github.com/graphql-go/graphql/language/ast" "github.com/graphql-go/graphql/language/ast"
"context" "context"
"encoding/json" "encoding/json"
"encoding/base64"
"io" "io"
"reflect" "reflect"
"fmt" "fmt"
@ -23,7 +24,6 @@ import (
"crypto/sha512" "crypto/sha512"
"crypto/rand" "crypto/rand"
"crypto/x509" "crypto/x509"
"bytes"
"github.com/google/uuid" "github.com/google/uuid"
) )
@ -67,7 +67,7 @@ type AuthRespJSON struct {
Signature []byte `json:"signature"` Signature []byte `json:"signature"`
} }
func NewAuthRespJSON(thread *GQLThread, req AuthReqJSON) (AuthRespJSON, *ecdsa.PublicKey, *ecdsa.PrivateKey, error) { func NewAuthRespJSON(thread *GQLThread, req AuthReqJSON) (AuthRespJSON, *ecdsa.PublicKey, []byte, error) {
// Check if req.Time is within +- 1 second of now // Check if req.Time is within +- 1 second of now
now := time.Now() now := time.Now()
earliest := now.Add(-1 * time.Second) earliest := now.Add(-1 * time.Second)
@ -132,21 +132,14 @@ func NewAuthRespJSON(thread *GQLThread, req AuthReqJSON) (AuthRespJSON, *ecdsa.P
return AuthRespJSON{}, nil, nil, err return AuthRespJSON{}, nil, nil, err
} }
secret_hash := sha512.Sum512(shared_secret)
buf := bytes.NewReader(secret_hash[:])
shared_key, err := ecdsa.GenerateKey(thread.Key.Curve, buf)
if err != nil {
return AuthRespJSON{}, nil, nil, err
}
return AuthRespJSON{ return AuthRespJSON{
Granted: granted, Granted: granted,
ECDHPubkey: ec_key_pub, ECDHPubkey: ec_key_pub,
Signature: resp_sig, Signature: resp_sig,
}, remote_key, shared_key, nil }, remote_key, shared_secret, nil
} }
func ParseAuthRespJSON(resp AuthRespJSON, ecdsa_curve elliptic.Curve, ecdh_curve ecdh.Curve, ec_key *ecdh.PrivateKey) (*ecdsa.PrivateKey, error) { func ParseAuthRespJSON(resp AuthRespJSON, ecdsa_curve elliptic.Curve, ecdh_curve ecdh.Curve, ec_key *ecdh.PrivateKey) ([]byte, error) {
remote, err := ecdh_curve.NewPublicKey(resp.ECDHPubkey) remote, err := ecdh_curve.NewPublicKey(resp.ECDHPubkey)
if err != nil { if err != nil {
return nil, err return nil, err
@ -157,27 +150,19 @@ func ParseAuthRespJSON(resp AuthRespJSON, ecdsa_curve elliptic.Curve, ecdh_curve
return nil, err return nil, err
} }
secret_hash := sha512.Sum512(shared_secret) return shared_secret, nil
buf := bytes.NewReader(secret_hash[:])
shared_key, err := ecdsa.GenerateKey(ecdsa_curve, buf)
if err != nil {
return nil, err
} }
return shared_key, nil type GQLUser struct {
} SimpleLockable
type AuthData struct {
Granted time.Time Granted time.Time
Pubkey *ecdsa.PublicKey Pubkey *ecdsa.PublicKey
Shared *ecdsa.PrivateKey Shared []byte
} }
func (data AuthData) String() string { type GQLUserJSON struct {
return fmt.Sprintf("{Granted: %+v, Pubkey: %s, Shared: %s}", data.Granted, KeyID(data.Pubkey).String(), KeyID(&data.Shared.PublicKey).String()) SimpleLockableJSON
}
type AuthDataJSON struct {
Granted time.Time `json:"granted"` Granted time.Time `json:"granted"`
Pubkey []byte `json:"pubkey"` Pubkey []byte `json:"pubkey"`
Shared []byte `json:"shared"` Shared []byte `json:"shared"`
@ -189,6 +174,66 @@ func KeyID(pub *ecdsa.PublicKey) NodeID {
return NodeID(str) return NodeID(str)
} }
func (user *GQLUser) Type() NodeType {
return NodeType("gql_user")
}
func (user *GQLUser) Serialize() ([]byte, error) {
lockable_json := NewSimpleLockableJSON(&user.SimpleLockable)
pubkey, err := x509.MarshalPKIXPublicKey(user.Pubkey)
if err != nil {
return nil, err
}
return json.MarshalIndent(&GQLUserJSON{
SimpleLockableJSON: lockable_json,
Granted: user.Granted,
Shared: user.Shared,
Pubkey: pubkey,
}, "", " ")
}
func LoadGQLUser(ctx *Context, id NodeID, data []byte, nodes NodeMap) (Node, error) {
var j GQLUserJSON
err := json.Unmarshal(data, &j)
if err != nil {
return nil, err
}
pub, err := x509.ParsePKIXPublicKey(j.Pubkey)
if err != nil {
return nil, err
}
var pubkey *ecdsa.PublicKey
switch pub.(type) {
case *ecdsa.PublicKey:
pubkey = pub.(*ecdsa.PublicKey)
default:
return nil, fmt.Errorf("Invalid key type")
}
user := NewGQLUser(j.Name, j.Granted, pubkey, j.Shared)
nodes[id] = &user
err = RestoreSimpleLockable(ctx, &user, j.SimpleLockableJSON, nodes)
if err != nil {
return nil, err
}
return &user, nil
}
func NewGQLUser(name string, granted time.Time, pubkey *ecdsa.PublicKey, shared []byte) GQLUser {
id := KeyID(pubkey)
return GQLUser{
SimpleLockable: NewSimpleLockable(id, name),
Granted: granted,
Pubkey: pubkey,
Shared: shared,
}
}
func AuthHandler(ctx *Context, server *GQLThread) func(http.ResponseWriter, *http.Request) { func AuthHandler(ctx *Context, server *GQLThread) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
ctx.Log.Logf("gql", "GQL_AUTH_REQUEST: %s", r.RemoteAddr) ctx.Log.Logf("gql", "GQL_AUTH_REQUEST: %s", r.RemoteAddr)
@ -207,7 +252,7 @@ func AuthHandler(ctx *Context, server *GQLThread) func(http.ResponseWriter, *htt
return return
} }
resp, remote_id, shared_key, err := NewAuthRespJSON(server, req) resp, remote_id, shared, err := NewAuthRespJSON(server, req)
if err != nil { if err != nil {
ctx.Log.Logf("gql", "GQL_AUTH_VERIFY_ERROR: %s", err) ctx.Log.Logf("gql", "GQL_AUTH_VERIFY_ERROR: %s", err)
return return
@ -230,20 +275,23 @@ func AuthHandler(ctx *Context, server *GQLThread) func(http.ResponseWriter, *htt
key_id := KeyID(remote_id) key_id := KeyID(remote_id)
new_auth := AuthData{ _, exists := server.Users[key_id]
Granted: time.Now(),
Pubkey: remote_id,
Shared: shared_key,
}
_, exists := server.AuthMap[key_id]
if exists { if exists {
ctx.Log.Logf("gql", "REFRESHING AUTH FOR %s - %s", key_id, new_auth) ctx.Log.Logf("gql", "REFRESHING AUTH FOR %s", key_id)
} else { } else {
ctx.Log.Logf("gql", "AUTHORIZING NEW USER %s - %s", key_id, new_auth) ctx.Log.Logf("gql", "AUTHORIZING NEW USER %s - %s", key_id, shared)
new_user := NewGQLUser(fmt.Sprintf("GQL_USER %s", key_id.String()), time.Now(), remote_id, shared)
err := UpdateStates(ctx, []Node{server}, func(nodes NodeMap) error {
server.Users[key_id] = &new_user
return nil
})
if err != nil {
ctx.Log.Logf("gql", "GQL_AUTH_UPDATE_ERR: %s", err)
return
}
} }
server.AuthMap[key_id] = new_auth
} }
} }
@ -322,7 +370,7 @@ func GraphiQLHandler() func(http.ResponseWriter, *http.Request) {
} }
type GQLWSPayload struct { type GQLPayload struct {
OperationName string `json:"operationName,omitempty"` OperationName string `json:"operationName,omitempty"`
Query string `json:"query,omitempty"` Query string `json:"query,omitempty"`
Variables map[string]interface{} `json:"variables,omitempty"` Variables map[string]interface{} `json:"variables,omitempty"`
@ -333,7 +381,7 @@ type GQLWSPayload struct {
type GQLWSMsg struct { type GQLWSMsg struct {
ID string `json:"id,omitempty"` ID string `json:"id,omitempty"`
Type string `json:"type"` Type string `json:"type"`
Payload GQLWSPayload `json:"payload,omitempty"` Payload GQLPayload `json:"payload,omitempty"`
} }
func enableCORS(w *http.ResponseWriter) { func enableCORS(w *http.ResponseWriter) {
@ -381,6 +429,29 @@ func checkForAuthHeader(header http.Header) (string, bool) {
return "", false return "", false
} }
func CheckAuth(server *GQLThread, r *http.Request) (*GQLUser, error) {
username, password, ok := r.BasicAuth()
if ok == false {
return nil, fmt.Errorf("GQL_REQUEST_ERR: no auth header included in request header")
}
auth_id, err := ParseID(username)
if err != nil {
return nil, fmt.Errorf("GQL_REQUEST_ERR: failed to parse ID from auth username: %s", username)
}
user, exists := server.Users[auth_id]
if exists == false {
return nil, fmt.Errorf("GQL_REQUEST_ERR: no existing authorization for client %s", auth_id)
}
if base64.StdEncoding.EncodeToString(user.Shared) != password {
return nil, fmt.Errorf("GQL_AUTH_FAIL")
}
return user, nil
}
func GQLHandler(ctx * Context, server * GQLThread) func(http.ResponseWriter, *http.Request) { func GQLHandler(ctx * Context, server * GQLThread) func(http.ResponseWriter, *http.Request) {
gql_ctx := context.Background() gql_ctx := context.Background()
gql_ctx = context.WithValue(gql_ctx, "graph_context", ctx) gql_ctx = context.WithValue(gql_ctx, "graph_context", ctx)
@ -394,36 +465,22 @@ func GQLHandler(ctx * Context, server * GQLThread) func(http.ResponseWriter, *ht
header_map[header] = value header_map[header] = value
} }
ctx.Log.Logm("gql", header_map, "REQUEST_HEADERS") ctx.Log.Logm("gql", header_map, "REQUEST_HEADERS")
username, password, ok := r.BasicAuth()
if ok == false {
ctx.Log.Logf("gql", "GQL_REQUEST_ERR: no auth header included in request header")
json.NewEncoder(w).Encode(GQLUnauthorized("No Auth header provided"))
return
}
auth_id, err := ParseID(username) user, err := CheckAuth(server, r)
if err != nil { if err != nil {
ctx.Log.Logf("gql", "GQL_REQUEST_ERR: failed to parse ID from auth username: %s", username) ctx.Log.Logf("gql", "GQL_AUTH_ERR: %s", err)
json.NewEncoder(w).Encode(GQLUnauthorized("Failed to parse ID from username")) json.NewEncoder(w).Encode(GQLUnauthorized(fmt.Sprintf("%s", err)))
return
}
auth, exists := server.AuthMap[auth_id]
if exists == false {
ctx.Log.Logf("gql", "GQL_REQUEST_ERR: no existing authorization for client %s", auth_id)
json.NewEncoder(w).Encode(GQLUnauthorized("No matching authorization for client"))
return return
} }
req_ctx := context.WithValue(gql_ctx, "user", user)
req_ctx := context.WithValue(gql_ctx, "auth", auth)
ctx.Log.Logf("gql", "GQL_AUTH: %+v - %s", auth, password)
str, err := io.ReadAll(r.Body) str, err := io.ReadAll(r.Body)
if err != nil { if err != nil {
ctx.Log.Logf("gql", "GQL_REQUEST_ERR: failed to read request body: %s", err) ctx.Log.Logf("gql", "GQL_READ_ERR: %s", err)
json.NewEncoder(w).Encode(fmt.Sprintf("%e", err))
return return
} }
query := GQLWSPayload{} query := GQLPayload{}
json.Unmarshal(str, &query) json.Unmarshal(str, &query)
params := graphql.Params{ params := graphql.Params{
@ -509,29 +566,12 @@ func GQLWSHandler(ctx * Context, server * GQLThread) func(http.ResponseWriter, *
} }
ctx.Log.Logm("gql", header_map, "REQUEST_HEADERS") ctx.Log.Logm("gql", header_map, "REQUEST_HEADERS")
username, password, ok := r.BasicAuth() user, err := CheckAuth(server, r)
if ok == false {
ctx.Log.Logf("gql", "GQL_REQUEST_ERR: no auth header included in request header")
json.NewEncoder(w).Encode(GQLUnauthorized("No Auth header provided"))
return
}
auth_id, err := ParseID(username)
if err != nil { if err != nil {
ctx.Log.Logf("gql", "GQL_REQUEST_ERR: failed to parse ID from auth username: %s", username) ctx.Log.Logf("gql", "GQL_AUTH_ERR: %s", err)
json.NewEncoder(w).Encode(GQLUnauthorized("Failed to parse ID from username"))
return
}
auth, exists := server.AuthMap[auth_id]
if exists == false {
ctx.Log.Logf("gql", "GQL_REQUEST_ERR: no existing authorization for client %s", auth_id)
json.NewEncoder(w).Encode(GQLUnauthorized("No matching authorization for client"))
return return
} }
req_ctx := context.WithValue(gql_ctx, "user", user)
req_ctx := context.WithValue(gql_ctx, "auth", auth)
ctx.Log.Logf("gql", "GQL_AUTH: %+v - %s", auth, password)
u := ws.HTTPUpgrader{ u := ws.HTTPUpgrader{
Protocol: func(protocol string) bool { Protocol: func(protocol string) bool {
@ -619,7 +659,7 @@ func GQLWSHandler(ctx * Context, server * GQLThread) func(http.ResponseWriter, *
msg, err := json.Marshal(GQLWSMsg{ msg, err := json.Marshal(GQLWSMsg{
ID: msg.ID, ID: msg.ID,
Type: "next", Type: "next",
Payload: GQLWSPayload{ Payload: GQLPayload{
Data: string(data), Data: string(data),
}, },
}) })
@ -651,7 +691,7 @@ type GQLThread struct {
http_server *http.Server http_server *http.Server
http_done *sync.WaitGroup http_done *sync.WaitGroup
Listen string Listen string
AuthMap map[NodeID]AuthData Users map[NodeID]*GQLUser
Key *ecdsa.PrivateKey Key *ecdsa.PrivateKey
ECDH ecdh.Curve ECDH ecdh.Curve
} }
@ -677,7 +717,7 @@ func (thread * GQLThread) DeserializeInfo(ctx *Context, data []byte) (ThreadInfo
type GQLThreadJSON struct { type GQLThreadJSON struct {
SimpleThreadJSON SimpleThreadJSON
Listen string `json:"listen"` Listen string `json:"listen"`
AuthMap map[string]AuthDataJSON `json:"auth_map"` Users []string `json:"users"`
Key []byte `json:"key"` Key []byte `json:"key"`
ECDH uint8 `json:"ecdh_curve"` ECDH uint8 `json:"ecdh_curve"`
} }
@ -706,23 +746,17 @@ func NewGQLThreadJSON(thread *GQLThread) GQLThreadJSON {
panic(err) panic(err)
} }
auth_map := map[string]AuthDataJSON{} users := make([]string, len(thread.Users))
for id, data := range(thread.AuthMap) { i := 0
shared, err := x509.MarshalECPrivateKey(data.Shared) for id, _ := range(thread.Users) {
if err != nil { users[i] = id.String()
panic(err) i += 1
}
auth_map[id.String()] = AuthDataJSON{
Granted: data.Granted,
Pubkey: elliptic.Marshal(data.Pubkey.Curve, data.Pubkey.X, data.Pubkey.Y),
Shared: shared,
}
} }
return GQLThreadJSON{ return GQLThreadJSON{
SimpleThreadJSON: thread_json, SimpleThreadJSON: thread_json,
Listen: thread.Listen, Listen: thread.Listen,
AuthMap: auth_map, Users: users,
Key: ser_key, Key: ser_key,
ECDH: ecdh_curve_ids[thread.ECDH], ECDH: ecdh_curve_ids[thread.ECDH],
} }
@ -746,29 +780,17 @@ func LoadGQLThread(ctx *Context, id NodeID, data []byte, nodes NodeMap) (Node, e
} }
thread := NewGQLThread(id, j.Name, j.StateName, j.Listen, ecdh_curve, key) thread := NewGQLThread(id, j.Name, j.StateName, j.Listen, ecdh_curve, key)
thread.AuthMap = map[NodeID]AuthData{} thread.Users = map[NodeID]*GQLUser{}
for id_str, auth_json := range(j.AuthMap) { for _, id_str := range(j.Users) {
id, err := ParseID(id_str) id, err := ParseID(id_str)
if err != nil { if err != nil {
return nil, err return nil, err
} }
x, y := elliptic.Unmarshal(key.Curve, auth_json.Pubkey) user, err := LoadNodeRecurse(ctx, id, nodes)
if x == nil {
return nil, fmt.Errorf("Failed to load public key for curve %+v from %+v", key.Curve, auth_json.Pubkey)
}
shared, err := x509.ParseECPrivateKey(auth_json.Shared)
if err != nil { if err != nil {
return nil, err return nil, err
} }
thread.AuthMap[id] = AuthData{ thread.Users[id] = user.(*GQLUser)
Granted: auth_json.Granted,
Pubkey: &ecdsa.PublicKey{
Curve: key.Curve,
X: x,
Y: y,
},
Shared: shared,
}
} }
nodes[id] = &thread nodes[id] = &thread
@ -784,7 +806,7 @@ func NewGQLThread(id NodeID, name string, state_name string, listen string, ecdh
return GQLThread{ return GQLThread{
SimpleThread: NewSimpleThread(id, name, state_name, reflect.TypeOf((*ParentThreadInfo)(nil)), gql_actions, gql_handlers), SimpleThread: NewSimpleThread(id, name, state_name, reflect.TypeOf((*ParentThreadInfo)(nil)), gql_actions, gql_handlers),
Listen: listen, Listen: listen,
AuthMap: map[NodeID]AuthData{}, Users: map[NodeID]*GQLUser{},
http_done: &sync.WaitGroup{}, http_done: &sync.WaitGroup{},
Key: key, Key: key,
ECDH: ecdh_curve, ECDH: ecdh_curve,
@ -863,7 +885,7 @@ var gql_actions ThreadActions = ThreadActions{
var gql_handlers ThreadHandlers = ThreadHandlers{ var gql_handlers ThreadHandlers = ThreadHandlers{
"child_added": func(ctx * Context, thread Thread, signal GraphSignal) (string, error) { "child_added": func(ctx * Context, thread Thread, signal GraphSignal) (string, error) {
ctx.Log.Logf("gql", "GQL_THREAD_CHILD_ADDED: %+v", signal) ctx.Log.Logf("gql", "GQL_THREAD_CHILD_ADDED: %+v", signal)
UpdateStates(ctx, []Node{thread}, func(nodes NodeMap)(error) { UpdateStates(ctx, []Node{thread}, func(nodes NodeMap) error {
should_run, exists := thread.ChildInfo(signal.Source()).(*ParentThreadInfo) should_run, exists := thread.ChildInfo(signal.Source()).(*ParentThreadInfo)
if exists == false { if exists == false {
ctx.Log.Logf("gql", "GQL_THREAD_CHILD_ADDED: tried to start %s whis is not a child") ctx.Log.Logf("gql", "GQL_THREAD_CHILD_ADDED: tried to start %s whis is not a child")

@ -385,6 +385,60 @@ func GQLLockableOwner(p graphql.ResolveParams) (interface{}, error) {
return owner, nil return owner, nil
} }
var gql_type_gql_user *graphql.Object = nil
func GQLTypeGQLUser() * graphql.Object {
if gql_type_gql_user == nil {
gql_type_gql_user = graphql.NewObject(graphql.ObjectConfig{
Name: "GQLUser",
Interfaces: []*graphql.Interface{
GQLInterfaceNode(),
GQLInterfaceLockable(),
},
IsTypeOf: func(p graphql.IsTypeOfParams) bool {
ctx, ok := p.Context.Value("graph_context").(*Context)
if ok == false {
return false
}
lockable_type := ctx.GQL.LockableType
value_type := reflect.TypeOf(p.Value)
if value_type.Implements(lockable_type) {
return true
}
return false
},
Fields: graphql.Fields{},
})
gql_type_gql_user.AddFieldConfig("ID", &graphql.Field{
Type: graphql.String,
Resolve: GQLNodeID,
})
gql_type_gql_user.AddFieldConfig("Name", &graphql.Field{
Type: graphql.String,
Resolve: GQLLockableName,
})
gql_type_gql_user.AddFieldConfig("Requirements", &graphql.Field{
Type: GQLListLockable(),
Resolve: GQLLockableRequirements,
})
gql_type_gql_user.AddFieldConfig("Owner", &graphql.Field{
Type: GQLInterfaceLockable(),
Resolve: GQLLockableOwner,
})
gql_type_gql_user.AddFieldConfig("Dependencies", &graphql.Field{
Type: GQLListLockable(),
Resolve: GQLLockableDependencies,
})
}
return gql_type_gql_user
}
var gql_type_gql_thread *graphql.Object = nil var gql_type_gql_thread *graphql.Object = nil
func GQLTypeGQLThread() * graphql.Object { func GQLTypeGQLThread() * graphql.Object {

@ -14,6 +14,7 @@ import (
"crypto/ecdh" "crypto/ecdh"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic" "crypto/elliptic"
"encoding/base64"
) )
func TestGQLThread(t * testing.T) { func TestGQLThread(t * testing.T) {
@ -61,13 +62,22 @@ func TestGQLDBLoad(t * testing.T) {
t1 := &t1_r t1 := &t1_r
update_channel := UpdateChannel(t1, 10, NodeID{}) update_channel := UpdateChannel(t1, 10, NodeID{})
u1_key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
fatalErr(t, err)
u1_shared := []byte{0xDE, 0xAD, 0xBE, 0xEF, 0x01, 0x23, 0x45, 0x67}
u1_r := NewGQLUser("Test User", time.Now(), &u1_key.PublicKey, u1_shared)
u1 := &u1_r
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
fatalErr(t, err) fatalErr(t, err)
gql_r := NewGQLThread(RandID(), "GQL Thread", "init", ":0", ecdh.P256(), key) gql_r := NewGQLThread(RandID(), "GQL Thread", "init", ":0", ecdh.P256(), key)
gql := &gql_r gql := &gql_r
info := NewParentThreadInfo(true, "start", "restore") info := NewParentThreadInfo(true, "start", "restore")
err = UpdateStates(ctx, []Node{gql, t1, l1}, func(nodes NodeMap) error { err = UpdateStates(ctx, []Node{gql, t1, l1, u1}, func(nodes NodeMap) error {
gql.Users[KeyID(&u1_key.PublicKey)] = u1
err := LinkLockables(ctx, gql, []Lockable{l1}, nodes) err := LinkLockables(ctx, gql, []Lockable{l1}, nodes)
if err != nil { if err != nil {
return err return err
@ -177,23 +187,57 @@ func TestGQLAuth(t * testing.T) {
str, err := json.Marshal(auth_req) str, err := json.Marshal(auth_req)
fatalErr(t, err) fatalErr(t, err)
b := bytes.NewBuffer(str) b := bytes.NewBuffer(str)
req, err := http.NewRequest("PUT", url, b) req, err := http.NewRequest("PUT", url, b)
fatalErr(t, err) fatalErr(t, err)
req.Header.Add("Authorization", "TM baddata")
resp, err := client.Do(req) resp, err := client.Do(req)
fatalErr(t, err) fatalErr(t, err)
body, err := io.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
resp.Body.Close()
fatalErr(t, err) fatalErr(t, err)
resp.Body.Close()
var j AuthRespJSON var j AuthRespJSON
err = json.Unmarshal(body, &j) err = json.Unmarshal(body, &j)
fatalErr(t, err) fatalErr(t, err)
shared_key, err := ParseAuthRespJSON(j, elliptic.P256(), ecdh.P256(), ec_key) shared, err := ParseAuthRespJSON(j, elliptic.P256(), ecdh.P256(), ec_key)
fatalErr(t, err)
url = fmt.Sprintf("http://localhost:%d/gql", port)
ser, err := json.MarshalIndent(&GQLPayload{
Query: "query { Self { ID } }",
}, "", " ")
fatalErr(t, err)
b = bytes.NewBuffer(ser)
req, err = http.NewRequest("GET", url, b)
fatalErr(t, err) fatalErr(t, err)
ctx.Log.Logf("test", "TEST_SHARED_SECRET: %s", KeyID(&shared_key.PublicKey).String())
req.SetBasicAuth(KeyID(&id.PublicKey).String(), base64.StdEncoding.EncodeToString(shared))
resp, err = client.Do(req)
fatalErr(t, err)
body, err = io.ReadAll(resp.Body)
fatalErr(t, err)
resp.Body.Close()
ctx.Log.Logf("test", "TEST_RESP: %s", body)
req.SetBasicAuth(KeyID(&id.PublicKey).String(), "BAD_PASSWORD")
resp, err = client.Do(req)
fatalErr(t, err)
body, err = io.ReadAll(resp.Body)
fatalErr(t, err)
resp.Body.Close()
ctx.Log.Logf("test", "TEST_RESP: %s", body)
done <- nil done <- nil
}(gql_t) }(gql_t)

@ -72,6 +72,7 @@ func testContext(t * testing.T) * Context {
func fatalErr(t * testing.T, err error) { func fatalErr(t * testing.T, err error) {
if err != nil { if err != nil {
pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
t.Fatal(err) t.Fatal(err)
} }
} }

@ -355,7 +355,7 @@ func LoadSimpleThread(ctx *Context, id NodeID, data []byte, nodes NodeMap) (Node
return &thread, nil return &thread, nil
} }
// SimpleThread as no associated info with children // SimpleThread has no associated info with children
func (thread * SimpleThread) DeserializeInfo(ctx *Context, data []byte) (ThreadInfo, error) { func (thread * SimpleThread) DeserializeInfo(ctx *Context, data []byte) (ThreadInfo, error) {
if len(data) > 0 { if len(data) > 0 {
return nil, fmt.Errorf("SimpleThread expected to deserialize no info but got %d length data: %s", len(data), string(data)) return nil, fmt.Errorf("SimpleThread expected to deserialize no info but got %d length data: %s", len(data), string(data))