From 202a1003eef330cd3b39aae17df128ee6b2d1af1 Mon Sep 17 00:00:00 2001 From: Nick Johnson Date: Thu, 22 Nov 2018 14:20:55 +1300 Subject: [PATCH] Fixes in response to review --- cmd/geth/config.go | 4 ++-- cmd/geth/main.go | 2 ++ cmd/utils/flags.go | 21 ++++++++++++++--- common/hexutil/json.go | 6 +++++ common/types.go | 4 ++++ ethgraphql/main_test.go | 13 ----------- ethgraphql/main.go => graphql/grahpql.go | 9 ++++---- {ethgraphql => graphql}/graphiql.go | 24 +++++++++++++++++++- graphql/graphql_test.go | 29 ++++++++++++++++++++++++ {ethgraphql => graphql}/schema.go | 18 ++++++++++++++- node/config.go | 14 ++++++++++++ 11 files changed, 120 insertions(+), 24 deletions(-) delete mode 100644 ethgraphql/main_test.go rename ethgraphql/main.go => graphql/grahpql.go (99%) rename {ethgraphql => graphql}/graphiql.go (63%) create mode 100644 graphql/graphql_test.go rename {ethgraphql => graphql}/schema.go (94%) diff --git a/cmd/geth/config.go b/cmd/geth/config.go index aff1530ff4..cb6cf54766 100644 --- a/cmd/geth/config.go +++ b/cmd/geth/config.go @@ -30,7 +30,7 @@ import ( "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/dashboard" "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/ethgraphql" + "github.com/ethereum/go-ethereum/graphql" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/params" whisper "github.com/ethereum/go-ethereum/whisper/whisperv6" @@ -177,7 +177,7 @@ func makeFullNode(ctx *cli.Context) *node.Node { // Configure GraphQL if required if ctx.GlobalIsSet(utils.GraphQLEnabledFlag.Name) { - if err := ethgraphql.RegisterGraphQLService(stack, cfg.Node.GraphQLEndpoint(), cfg.Node.HTTPCors, cfg.Node.HTTPVirtualHosts, cfg.Node.HTTPTimeouts); err != nil { + if err := graphql.RegisterGraphQLService(stack, cfg.Node.GraphQLEndpoint(), cfg.Node.GraphQLCors, cfg.Node.GraphQLVirtualHosts, cfg.Node.HTTPTimeouts); err != nil { utils.Fatalf("Failed to register the Ethereum service: %v", err) } } diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 859d4d959a..2cc881dbc4 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -142,6 +142,8 @@ var ( utils.GraphQLEnabledFlag, utils.GraphQLListenAddrFlag, utils.GraphQLPortFlag, + utils.GraphQLCORSDomainFlag, + utils.GraphQLVirtualHostsFlag, utils.RPCApiFlag, utils.WSEnabledFlag, utils.WSListenAddrFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index e6834442ca..16cae43e3d 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -432,15 +432,25 @@ var ( Usage: "Enable the GraphQL server", } GraphQLListenAddrFlag = cli.StringFlag{ - Name: "graphqladdr", + Name: "graphql.addr", Usage: "GraphQL server listening interface", Value: node.DefaultGraphQLHost, } GraphQLPortFlag = cli.IntFlag{ - Name: "graphqlport", + Name: "graphql.port", Usage: "GraphQL server listening port", Value: node.DefaultGraphQLPort, } + GraphQLCORSDomainFlag = cli.StringFlag{ + Name: "graphql.rpccorsdomain", + Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)", + Value: "", + } + GraphQLVirtualHostsFlag = cli.StringFlag{ + Name: "graphql.rpcvhosts", + Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.", + Value: strings.Join(node.DefaultConfig.HTTPVirtualHosts, ","), + } RPCCORSDomainFlag = cli.StringFlag{ Name: "rpccorsdomain", Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)", @@ -811,8 +821,13 @@ func setGraphQL(ctx *cli.Context, cfg *node.Config) { cfg.GraphQLHost = ctx.GlobalString(GraphQLListenAddrFlag.Name) } } - cfg.GraphQLPort = ctx.GlobalInt(GraphQLPortFlag.Name) + if ctx.GlobalIsSet(GraphQLCORSDomainFlag.Name) { + cfg.GraphQLCors = splitAndTrim(ctx.GlobalString(GraphQLCORSDomainFlag.Name)) + } + if ctx.GlobalIsSet(GraphQLVirtualHostsFlag.Name) { + cfg.GraphQLVirtualHosts = splitAndTrim(ctx.GlobalString(GraphQLVirtualHostsFlag.Name)) + } } // setWS creates the WebSocket RPC listener interface string from the set diff --git a/common/hexutil/json.go b/common/hexutil/json.go index 30bfb61c9b..777b08eca4 100644 --- a/common/hexutil/json.go +++ b/common/hexutil/json.go @@ -72,8 +72,10 @@ func (b Bytes) String() string { return Encode(b) } +// ImplementsGraphQLType returns true if Bytes implements the specified GraphQL type. func (b Bytes) ImplementsGraphQLType(name string) bool { return name == "Bytes" } +// UnmarshalGraphQL unmarshals the provided GraphQL query data. func (b *Bytes) UnmarshalGraphQL(input interface{}) error { var err error switch input := input.(type) { @@ -204,8 +206,10 @@ func (b *Big) String() string { return EncodeBig(b.ToInt()) } +// ImplementsGraphQLType returns true if Big implements the provided GraphQL type. func (b Big) ImplementsGraphQLType(name string) bool { return name == "BigInt" } +// UnmarshalGraphQL unmarshals the provided GraphQL query data. func (b *Big) UnmarshalGraphQL(input interface{}) error { var err error switch input := input.(type) { @@ -268,8 +272,10 @@ func (b Uint64) String() string { return EncodeUint64(uint64(b)) } +// ImplementsGraphQLType returns true if Uint64 implements the provided GraphQL type. func (b Uint64) ImplementsGraphQLType(name string) bool { return name == "Long" } +// UnmarshalGraphQL unmarshals the provided GraphQL query data. func (b *Uint64) UnmarshalGraphQL(input interface{}) error { var err error switch input := input.(type) { diff --git a/common/types.go b/common/types.go index cf61ec66c5..cb74cbd6ff 100644 --- a/common/types.go +++ b/common/types.go @@ -141,8 +141,10 @@ func (h Hash) Value() (driver.Value, error) { return h[:], nil } +// ImplementsGraphQLType returns true if Hash implements the specified GraphQL type. func (_ Hash) ImplementsGraphQLType(name string) bool { return name == "Bytes32" } +// UnmarshalGraphQL unmarshals the provided GraphQL query data. func (h *Hash) UnmarshalGraphQL(input interface{}) error { var err error switch input := input.(type) { @@ -281,8 +283,10 @@ func (a Address) Value() (driver.Value, error) { return a[:], nil } +// ImplementsGraphQLType returns true if Hash implements the specified GraphQL type. func (a Address) ImplementsGraphQLType(name string) bool { return name == "Address" } +// UnmarshalGraphQL unmarshals the provided GraphQL query data. func (a *Address) UnmarshalGraphQL(input interface{}) error { var err error switch input := input.(type) { diff --git a/ethgraphql/main_test.go b/ethgraphql/main_test.go deleted file mode 100644 index acdb143f7d..0000000000 --- a/ethgraphql/main_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package ethgraphql - -import ( - "testing" -) - -func TestBuildSchema(t *testing.T) { - // Make sure the schema can be parsed and matched up to the object model. - _, err := NewHandler(nil) - if err != nil { - t.Errorf("Could not construct GraphQL handler: %v", err) - } -} diff --git a/ethgraphql/main.go b/graphql/grahpql.go similarity index 99% rename from ethgraphql/main.go rename to graphql/grahpql.go index 3c81c80e23..b16271e6d5 100644 --- a/ethgraphql/main.go +++ b/graphql/grahpql.go @@ -14,7 +14,8 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package ethgraphql +// Package graphql provides a GraphQL interface to Ethereum node data. +package graphql import ( "context" @@ -38,7 +39,7 @@ import ( "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" - graphql "github.com/graph-gophers/graphql-go" + graphqlgo "github.com/graph-gophers/graphql-go" "github.com/graph-gophers/graphql-go/relay" ) @@ -1021,7 +1022,7 @@ func (r *Resolver) Syncing() (*SyncState, error) { func NewHandler(be *eth.EthAPIBackend) (http.Handler, error) { q := Resolver{be} - s, err := graphql.ParseSchema(schema, &q) + s, err := graphqlgo.ParseSchema(schema, &q) if err != nil { return nil, err } @@ -1034,7 +1035,7 @@ func NewHandler(be *eth.EthAPIBackend) (http.Handler, error) { return mux, nil } -// Service encapsulates an ETHGraphQL service. +// Service encapsulates a GraphQL service. type Service struct { endpoint string // The host:port endpoint for this service. cors []string // Allowed CORS domains diff --git a/ethgraphql/graphiql.go b/graphql/graphiql.go similarity index 63% rename from ethgraphql/graphiql.go rename to graphql/graphiql.go index 42bb7920fd..6d9dda3e8c 100644 --- a/ethgraphql/graphiql.go +++ b/graphql/graphiql.go @@ -1,4 +1,26 @@ -package ethgraphql +// The MIT License (MIT) +// +// Copyright (c) 2016 Muhammed Thanish +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package graphql import ( "bytes" diff --git a/graphql/graphql_test.go b/graphql/graphql_test.go new file mode 100644 index 0000000000..d63418398a --- /dev/null +++ b/graphql/graphql_test.go @@ -0,0 +1,29 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package graphql + +import ( + "testing" +) + +func TestBuildSchema(t *testing.T) { + // Make sure the schema can be parsed and matched up to the object model. + _, err := NewHandler(nil) + if err != nil { + t.Errorf("Could not construct GraphQL handler: %v", err) + } +} diff --git a/ethgraphql/schema.go b/graphql/schema.go similarity index 94% rename from ethgraphql/schema.go rename to graphql/schema.go index 9367cdc7cf..c1ba87d2d6 100644 --- a/ethgraphql/schema.go +++ b/graphql/schema.go @@ -1,4 +1,20 @@ -package ethgraphql +// Copyright 2018 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package graphql const schema string = ` # Bytes32 is a 32 byte binary string, represented as 0x-prefixed hexadecimal. diff --git a/node/config.go b/node/config.go index a1a896df89..c259223cbb 100644 --- a/node/config.go +++ b/node/config.go @@ -110,6 +110,20 @@ type Config struct { // for ephemeral nodes). GraphQLPort int `toml:",omitempty"` + // GraphQLCors is the Cross-Origin Resource Sharing header to send to requesting + // clients. Please be aware that CORS is a browser enforced security, it's fully + // useless for custom HTTP clients. + GraphQLCors []string `toml:",omitempty"` + + // GraphQLVirtualHosts is the list of virtual hostnames which are allowed on incoming requests. + // This is by default {'localhost'}. Using this prevents attacks like + // DNS rebinding, which bypasses SOP by simply masquerading as being within the same + // origin. These attacks do not utilize CORS, since they are not cross-domain. + // By explicitly checking the Host-header, the server will not allow requests + // made against the server with a malicious host domain. + // Requests using ip address directly are not affected + GraphQLVirtualHosts []string `toml:",omitempty"` + // HTTPCors is the Cross-Origin Resource Sharing header to send to requesting // clients. Please be aware that CORS is a browser enforced security, it's fully // useless for custom HTTP clients.