First pass of gql read permission

graph-rework-2
noah metz 2023-07-21 19:16:30 -06:00
parent 97815c86ff
commit 76512afd4e
5 changed files with 98 additions and 92 deletions

@ -16,7 +16,7 @@ var GQLMutationSendUpdate = NewField(func()*graphql.Field {
}, },
}, },
Resolve: func(p graphql.ResolveParams) (interface{}, error) { Resolve: func(p graphql.ResolveParams) (interface{}, error) {
ctx, err := GetResolveContext(p) ctx, err := PrepResolve(p)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -83,7 +83,7 @@ var GQLMutationStartChild = NewField(func()*graphql.Field{
}, },
}, },
Resolve: func(p graphql.ResolveParams) (interface{}, error) { Resolve: func(p graphql.ResolveParams) (interface{}, error) {
ctx, err := GetResolveContext(p) ctx, err := PrepResolve(p)
if err != nil { if err != nil {
return nil, err return nil, err
} }

@ -6,7 +6,7 @@ import (
var GQLQuerySelf = &graphql.Field{ var GQLQuerySelf = &graphql.Field{
Type: GQLTypeGQLThread.Type, Type: GQLTypeGQLThread.Type,
Resolve: func(p graphql.ResolveParams) (interface{}, error) { Resolve: func(p graphql.ResolveParams) (interface{}, error) {
ctx, err := GetResolveContext(p) ctx, err := PrepResolve(p)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -23,7 +23,7 @@ var GQLQuerySelf = &graphql.Field{
var GQLQueryUser = &graphql.Field{ var GQLQueryUser = &graphql.Field{
Type: GQLTypeUser.Type, Type: GQLTypeUser.Type,
Resolve: func(p graphql.ResolveParams) (interface{}, error) { Resolve: func(p graphql.ResolveParams) (interface{}, error) {
ctx, err := GetResolveContext(p) ctx, err := PrepResolve(p)
if err != nil { if err != nil {
return nil, err return nil, err
} }

@ -5,7 +5,7 @@ import (
"github.com/graphql-go/graphql" "github.com/graphql-go/graphql"
) )
func GetResolveContext(p graphql.ResolveParams) (*ResolveContext, error) { func PrepResolve(p graphql.ResolveParams) (*ResolveContext, error) {
resolve_context, ok := p.Context.Value("resolve").(*ResolveContext) resolve_context, ok := p.Context.Value("resolve").(*ResolveContext)
if ok == false { if ok == false {
return nil, fmt.Errorf("Bad resolve in params context") return nil, fmt.Errorf("Bad resolve in params context")
@ -43,28 +43,42 @@ func ExtractID(p graphql.ResolveParams, name string) (NodeID, error) {
} }
func GQLNodeID(p graphql.ResolveParams) (interface{}, error) { func GQLNodeID(p graphql.ResolveParams) (interface{}, error) {
ctx, err := PrepResolve(p)
if err != nil {
return nil, err
}
node, ok := p.Source.(Node) node, ok := p.Source.(Node)
if ok == false || node == nil { if ok == false || node == nil {
return nil, fmt.Errorf("Failed to cast source to Node") return nil, fmt.Errorf("Failed to cast source to Node")
} }
err = UseStates(ctx.Context, []Node{node, ctx.User}, func(nodes NodeMap) error {
return node.Allowed("read", "id", ctx.User)
})
if err != nil {
return nil, err
}
return node.ID(), nil return node.ID(), nil
} }
func GQLThreadListen(p graphql.ResolveParams) (interface{}, error) { func GQLThreadListen(p graphql.ResolveParams) (interface{}, error) {
ctx, err := PrepResolve(p)
if err != nil {
return nil, err
}
node, ok := p.Source.(*GQLThread) node, ok := p.Source.(*GQLThread)
if ok == false || node == nil { if ok == false || node == nil {
return nil, fmt.Errorf("Failed to cast source to GQLThread") return nil, fmt.Errorf("Failed to cast source to GQLThread")
} }
ctx, ok := p.Context.Value("graph_context").(*Context)
if ok == false {
return nil, fmt.Errorf("Failed to cast context graph_context to Context")
}
listen := "" listen := ""
err := UseStates(ctx, []Node{node}, func(nodes NodeMap) (error) { err = UseStates(ctx.Context, []Node{node, ctx.User}, func(nodes NodeMap) error {
listen = node.Listen listen = node.Listen
return nil return node.Allowed("read", "listen", ctx.User)
}) })
if err != nil { if err != nil {
@ -75,20 +89,20 @@ func GQLThreadListen(p graphql.ResolveParams) (interface{}, error) {
} }
func GQLThreadParent(p graphql.ResolveParams) (interface{}, error) { func GQLThreadParent(p graphql.ResolveParams) (interface{}, error) {
ctx, err := PrepResolve(p)
if err != nil {
return nil, err
}
node, ok := p.Source.(Thread) node, ok := p.Source.(Thread)
if ok == false || node == nil { if ok == false || node == nil {
return nil, fmt.Errorf("Failed to cast source to Thread") return nil, fmt.Errorf("Failed to cast source to Thread")
} }
ctx, ok := p.Context.Value("graph_context").(*Context)
if ok == false {
return nil, fmt.Errorf("Failed to cast context graph_context to Context")
}
var parent Thread = nil var parent Thread = nil
err := UseStates(ctx, []Node{node}, func(nodes NodeMap) (error) { err = UseStates(ctx.Context, []Node{node, ctx.User}, func(nodes NodeMap) (error) {
parent = node.Parent() parent = node.Parent()
return nil return node.Allowed("read", "parent", ctx.User)
}) })
if err != nil { if err != nil {
@ -99,20 +113,20 @@ func GQLThreadParent(p graphql.ResolveParams) (interface{}, error) {
} }
func GQLThreadState(p graphql.ResolveParams) (interface{}, error) { func GQLThreadState(p graphql.ResolveParams) (interface{}, error) {
ctx, err := PrepResolve(p)
if err != nil {
return nil, err
}
node, ok := p.Source.(Thread) node, ok := p.Source.(Thread)
if ok == false || node == nil { if ok == false || node == nil {
return nil, fmt.Errorf("Failed to cast source to Thread") return nil, fmt.Errorf("Failed to cast source to Thread")
} }
ctx, ok := p.Context.Value("graph_context").(*Context)
if ok == false {
return nil, fmt.Errorf("Failed to cast context graph_context to Context")
}
var state string var state string
err := UseStates(ctx, []Node{node}, func(nodes NodeMap) (error) { err = UseStates(ctx.Context, []Node{node, ctx.User}, func(nodes NodeMap) (error) {
state = node.State() state = node.State()
return nil return node.Allowed("read", "state", ctx.User)
}) })
if err != nil { if err != nil {
@ -123,20 +137,20 @@ func GQLThreadState(p graphql.ResolveParams) (interface{}, error) {
} }
func GQLThreadChildren(p graphql.ResolveParams) (interface{}, error) { func GQLThreadChildren(p graphql.ResolveParams) (interface{}, error) {
ctx, err := PrepResolve(p)
if err != nil {
return nil, err
}
node, ok := p.Source.(Thread) node, ok := p.Source.(Thread)
if ok == false || node == nil { if ok == false || node == nil {
return nil, fmt.Errorf("Failed to cast source to Thread") return nil, fmt.Errorf("Failed to cast source to Thread")
} }
ctx, ok := p.Context.Value("graph_context").(*Context)
if ok == false {
return nil, fmt.Errorf("Failed to cast context graph_context to Context")
}
var children []Thread = nil var children []Thread = nil
err := UseStates(ctx, []Node{node}, func(nodes NodeMap) (error) { err = UseStates(ctx.Context, []Node{node, ctx.User}, func(nodes NodeMap) (error) {
children = node.Children() children = node.Children()
return nil return node.Allowed("read", "children", ctx.User)
}) })
if err != nil { if err != nil {
@ -147,20 +161,20 @@ func GQLThreadChildren(p graphql.ResolveParams) (interface{}, error) {
} }
func GQLLockableName(p graphql.ResolveParams) (interface{}, error) { func GQLLockableName(p graphql.ResolveParams) (interface{}, error) {
node, ok := p.Source.(Lockable) ctx, err := PrepResolve(p)
if ok == false || node == nil { if err != nil {
return nil, fmt.Errorf("Failed to cast source to Lockable") return nil, err
} }
ctx, ok := p.Context.Value("graph_context").(*Context) node, ok := p.Source.(Lockable)
if ok == false || node == nil { if ok == false || node == nil {
return nil, fmt.Errorf("Failed to cast context graph_context to Context") return nil, fmt.Errorf("Failed to cast source to Lockable")
} }
name := "" name := ""
err := UseStates(ctx, []Node{node}, func(nodes NodeMap) error { err = UseStates(ctx.Context, []Node{node, ctx.User}, func(nodes NodeMap) error {
name = node.Name() name = node.Name()
return nil return node.Allowed("read", "name", ctx.User)
}) })
if err != nil { if err != nil {
@ -171,20 +185,20 @@ func GQLLockableName(p graphql.ResolveParams) (interface{}, error) {
} }
func GQLLockableRequirements(p graphql.ResolveParams) (interface{}, error) { func GQLLockableRequirements(p graphql.ResolveParams) (interface{}, error) {
ctx, err := PrepResolve(p)
if err != nil {
return nil, err
}
node, ok := p.Source.(Lockable) node, ok := p.Source.(Lockable)
if ok == false || node == nil { if ok == false || node == nil {
return nil, fmt.Errorf("Failed to cast source to Lockable") return nil, fmt.Errorf("Failed to cast source to Lockable")
} }
ctx, ok := p.Context.Value("graph_context").(*Context)
if ok == false {
return nil, fmt.Errorf("Failed to cast context graph_context to Context")
}
var requirements []Lockable = nil var requirements []Lockable = nil
err := UseStates(ctx, []Node{node}, func(nodes NodeMap) (error) { err = UseStates(ctx.Context, []Node{node, ctx.User}, func(nodes NodeMap) (error) {
requirements = node.Requirements() requirements = node.Requirements()
return nil return node.Allowed("read", "requirements", ctx.User)
}) })
if err != nil { if err != nil {
@ -195,20 +209,20 @@ func GQLLockableRequirements(p graphql.ResolveParams) (interface{}, error) {
} }
func GQLLockableDependencies(p graphql.ResolveParams) (interface{}, error) { func GQLLockableDependencies(p graphql.ResolveParams) (interface{}, error) {
ctx, err := PrepResolve(p)
if err != nil {
return nil, err
}
node, ok := p.Source.(Lockable) node, ok := p.Source.(Lockable)
if ok == false || node == nil { if ok == false || node == nil {
return nil, fmt.Errorf("Failed to cast source to Lockable") return nil, fmt.Errorf("Failed to cast source to Lockable")
} }
ctx, ok := p.Context.Value("graph_context").(*Context)
if ok == false {
return nil, fmt.Errorf("Failed to cast context graph_context to Context")
}
var dependencies []Lockable = nil var dependencies []Lockable = nil
err := UseStates(ctx, []Node{node}, func(nodes NodeMap) (error) { err = UseStates(ctx.Context, []Node{node, ctx.User}, func(nodes NodeMap) (error) {
dependencies = node.Dependencies() dependencies = node.Dependencies()
return nil return node.Allowed("read", "dependencies", ctx.User)
}) })
if err != nil { if err != nil {
@ -219,20 +233,20 @@ func GQLLockableDependencies(p graphql.ResolveParams) (interface{}, error) {
} }
func GQLLockableOwner(p graphql.ResolveParams) (interface{}, error) { func GQLLockableOwner(p graphql.ResolveParams) (interface{}, error) {
ctx, err := PrepResolve(p)
if err != nil {
return nil, err
}
node, ok := p.Source.(Lockable) node, ok := p.Source.(Lockable)
if ok == false || node == nil { if ok == false || node == nil {
return nil, fmt.Errorf("Failed to cast source to Lockable") return nil, fmt.Errorf("Failed to cast source to Lockable")
} }
ctx, ok := p.Context.Value("graph_context").(*Context)
if ok == false {
return nil, fmt.Errorf("Failed to cast context graph_context to Context")
}
var owner Node = nil var owner Node = nil
err := UseStates(ctx, []Node{node}, func(nodes NodeMap) (error) { err = UseStates(ctx.Context, []Node{node, ctx.User}, func(nodes NodeMap) (error) {
owner = node.Owner() owner = node.Owner()
return nil return node.Allowed("read", "owner", ctx.User)
}) })
if err != nil { if err != nil {
@ -243,25 +257,25 @@ func GQLLockableOwner(p graphql.ResolveParams) (interface{}, error) {
} }
func GQLThreadUsers(p graphql.ResolveParams) (interface{}, error) { func GQLThreadUsers(p graphql.ResolveParams) (interface{}, error) {
ctx, err := PrepResolve(p)
if err != nil {
return nil, err
}
node, ok := p.Source.(*GQLThread) node, ok := p.Source.(*GQLThread)
if ok == false || node == nil { if ok == false || node == nil {
return nil, fmt.Errorf("Failed to cast source to GQLThread") return nil, fmt.Errorf("Failed to cast source to GQLThread")
} }
ctx, ok := p.Context.Value("graph_context").(*Context)
if ok == false {
return nil, fmt.Errorf("Failed to cast context graph_context to Context")
}
var users []*User var users []*User
err := UseStates(ctx, []Node{node}, func(nodes NodeMap) error { err = UseStates(ctx.Context, []Node{node, ctx.User}, func(nodes NodeMap) error {
users = make([]*User, len(node.Users)) users = make([]*User, len(node.Users))
i := 0 i := 0
for _, user := range(node.Users) { for _, user := range(node.Users) {
users[i] = user users[i] = user
i += 1 i += 1
} }
return nil return node.Allowed("read", "users", ctx.User)
}) })
if err != nil { if err != nil {

@ -1,6 +1,5 @@
package graphvent package graphvent
import ( import (
"fmt"
"github.com/graphql-go/graphql" "github.com/graphql-go/graphql"
) )
@ -17,19 +16,14 @@ func GQLSubscribeSelf(p graphql.ResolveParams) (interface{}, error) {
} }
func GQLSubscribeFn(p graphql.ResolveParams, send_nil bool, fn func(*Context, *GQLThread, GraphSignal, graphql.ResolveParams)(interface{}, error))(interface{}, error) { func GQLSubscribeFn(p graphql.ResolveParams, send_nil bool, fn func(*Context, *GQLThread, GraphSignal, graphql.ResolveParams)(interface{}, error))(interface{}, error) {
server, ok := p.Context.Value("gql_server").(*GQLThread) ctx, err := PrepResolve(p)
if ok == false { if err != nil {
return nil, fmt.Errorf("Failed to get gql_server from context and cast to GQLServer") return nil, err
}
ctx, ok := p.Context.Value("graph_context").(*Context)
if ok == false {
return nil, fmt.Errorf("Failed to get graph_context from context and cast to Context")
} }
c := make(chan interface{}) c := make(chan interface{})
go func(c chan interface{}, server *GQLThread) { go func(c chan interface{}, server *GQLThread) {
ctx.Log.Logf("gqlws", "GQL_SUBSCRIBE_THREAD_START") ctx.Context.Log.Logf("gqlws", "GQL_SUBSCRIBE_THREAD_START")
sig_c := UpdateChannel(server, 1, RandID()) sig_c := UpdateChannel(server, 1, RandID())
if send_nil == true { if send_nil == true {
sig_c <- nil sig_c <- nil
@ -39,14 +33,14 @@ func GQLSubscribeFn(p graphql.ResolveParams, send_nil bool, fn func(*Context, *G
if ok == false { if ok == false {
return return
} }
ret, err := fn(ctx, server, val, p) ret, err := fn(ctx.Context, server, val, p)
if err != nil { if err != nil {
ctx.Log.Logf("gqlws", "type convertor error %s", err) ctx.Context.Log.Logf("gqlws", "type convertor error %s", err)
return return
} }
c <- ret c <- ret
} }
}(c, server) }(c, ctx.Server)
return c, nil return c, nil
} }

@ -406,21 +406,22 @@ func LoadNodeRecurse(ctx * Context, id NodeID, nodes NodeMap) (Node, error) {
return node, nil return node, nil
} }
// Internal function to check for a duplicate node in a slice by ID // Internal function to filter duplicate nodes from a list
func checkForDuplicate(nodes []Node) error { func filterDuplicates(nodes []Node) []Node {
ret := []Node{}
found := map[NodeID]bool{} found := map[NodeID]bool{}
for _, node := range(nodes) { for _, node := range(nodes) {
if node == nil { if node == nil {
return fmt.Errorf("Cannot get state of nil node") return []Node{}
} }
_, exists := found[node.ID()] _, exists := found[node.ID()]
if exists == true { if exists == false {
return fmt.Errorf("Attempted to get state of %s twice", node.ID())
}
found[node.ID()] = true found[node.ID()] = true
ret = append(ret, node)
} }
return nil }
return ret
} }
// Convert any slice of types that implement Node to a []Node // Convert any slice of types that implement Node to a []Node
@ -443,10 +444,7 @@ func UseStates(ctx * Context, init_nodes []Node, nodes_fn NodesFn) error {
// Add nodes to an existing read context and call nodes_fn with new_nodes locked for read // Add nodes to an existing read context and call nodes_fn with new_nodes locked for read
func UseMoreStates(ctx * Context, new_nodes []Node, nodes NodeMap, nodes_fn NodesFn) error { func UseMoreStates(ctx * Context, new_nodes []Node, nodes NodeMap, nodes_fn NodesFn) error {
err := checkForDuplicate(new_nodes) new_nodes = filterDuplicates(new_nodes)
if err != nil {
return err
}
locked_nodes := []Node{} locked_nodes := []Node{}
for _, node := range(new_nodes) { for _, node := range(new_nodes) {
@ -458,7 +456,7 @@ func UseMoreStates(ctx * Context, new_nodes []Node, nodes NodeMap, nodes_fn Node
} }
} }
err = nodes_fn(nodes) err := nodes_fn(nodes)
for _, node := range(locked_nodes) { for _, node := range(locked_nodes) {
delete(nodes, node.ID()) delete(nodes, node.ID())