Skip to content

Commit

Permalink
wip: support authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
hgiasac committed Mar 20, 2024
1 parent f887a14 commit 3c8f9bf
Show file tree
Hide file tree
Showing 15 changed files with 1,939 additions and 1,432 deletions.
2 changes: 1 addition & 1 deletion .hasura-connector/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ FROM gcr.io/distroless/base-debian12:nonroot
# Copy the binary to the production image from the builder stage.
COPY --from=builder /app/ndc-cli /ndc-cli

ENV CONFIGURATION /config
ENV HASURA_CONFIGURATION_DIRECTORY /config

# Run the web service on container startup.
CMD ["/ndc-cli", "serve"]
17 changes: 8 additions & 9 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ go 1.21
toolchain go1.22.0

require (
github.com/hasura/ndc-rest-schema v0.0.0-20240312024503-345ae7cb6078
github.com/hasura/ndc-sdk-go v0.1.1-0.20240312092535-8e480ed1490d
github.com/hasura/ndc-rest-schema v0.0.0-20240320034216-4c2976a29855
github.com/hasura/ndc-sdk-go v0.1.1-0.20240317172640-9c7a7adc1cd3
github.com/lmittmann/tint v1.0.4
gopkg.in/yaml.v3 v3.0.1
)
Expand All @@ -22,15 +22,14 @@ require (
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/pb33f/libopenapi v0.15.14 // indirect
github.com/prometheus/client_golang v1.19.0 // indirect
github.com/prometheus/client_model v0.6.0 // indirect
github.com/prometheus/common v0.49.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/sergi/go-diff v1.3.1 // indirect
github.com/prometheus/common v0.50.0 // indirect
github.com/prometheus/procfs v0.13.0 // indirect
github.com/vmware-labs/yaml-jsonpath v0.3.2 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
go.opentelemetry.io/otel v1.24.0 // indirect
Expand All @@ -45,13 +44,13 @@ require (
go.opentelemetry.io/otel/sdk/metric v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
go.opentelemetry.io/proto/otlp v1.1.0 // indirect
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81 // indirect
golang.org/x/net v0.22.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240304212257-790db918fca8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240304212257-790db918fca8 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240314234333-6e1732d8331c // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c // indirect
google.golang.org/grpc v1.62.1 // indirect
google.golang.org/protobuf v1.33.0 // indirect
)
34 changes: 16 additions & 18 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
Expand All @@ -54,12 +54,10 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 h1:/c3QmbOGMGTOumP2iT/rCwB7b0QDGLKzqOmktBjT+Is=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM=
github.com/hasura/ndc-rest-schema v0.0.0-20240312024503-345ae7cb6078 h1:5lV4lnOfzP+U+yykgaqWESm6L3fTdSmDEVkzY0NDf8s=
github.com/hasura/ndc-rest-schema v0.0.0-20240312024503-345ae7cb6078/go.mod h1:sa89qdTWWNQUHgHx3FST6oBdTcaW9CBbvW2TFne3oQE=
github.com/hasura/ndc-sdk-go v0.1.1-0.20240312081636-37f5f535293c h1:x9VzFBUG5xKz50CYiOtIZTDy3jByRcolIHGi+CcpJBY=
github.com/hasura/ndc-sdk-go v0.1.1-0.20240312081636-37f5f535293c/go.mod h1:u4+Xp/6icjMUQMGil6tgD05y5ZH1eXZ8gX2OCM/kgcU=
github.com/hasura/ndc-sdk-go v0.1.1-0.20240312092535-8e480ed1490d h1:EhmMj8kWlrxxd0gf4naCsUeX0bIjCjGXF9QdBKndn5w=
github.com/hasura/ndc-sdk-go v0.1.1-0.20240312092535-8e480ed1490d/go.mod h1:u4+Xp/6icjMUQMGil6tgD05y5ZH1eXZ8gX2OCM/kgcU=
github.com/hasura/ndc-rest-schema v0.0.0-20240320034216-4c2976a29855 h1:d3bq7jjsK/aPcFih6B+h1uM3CkNY24F8Eo5JXx8QBo8=
github.com/hasura/ndc-rest-schema v0.0.0-20240320034216-4c2976a29855/go.mod h1:R1xvYOx/TqgHEiP5Nm8qTRwFfJmxmV1y1De4b1i1CFg=
github.com/hasura/ndc-sdk-go v0.1.1-0.20240317172640-9c7a7adc1cd3 h1:A1N3ilX1EIxjTA2qaHPXFnIECpYjKhIFmtva8qJrpHk=
github.com/hasura/ndc-sdk-go v0.1.1-0.20240317172640-9c7a7adc1cd3/go.mod h1:EeM3hKbhCfBjDDva8mP4D2KeptTqAaxNqNw8rFQAnMs=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
Expand Down Expand Up @@ -99,10 +97,10 @@ github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7km
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos=
github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8=
github.com/prometheus/common v0.49.0 h1:ToNTdK4zSnPVJmh698mGFkDor9wBI/iGaJy5dbH1EgI=
github.com/prometheus/common v0.49.0/go.mod h1:Kxm+EULxRbUkjGU6WFsQqo3ORzB4tyKvlWFOE9mB2sE=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/prometheus/common v0.50.0 h1:YSZE6aa9+luNa2da6/Tik0q0A5AbR+U003TItK57CPQ=
github.com/prometheus/common v0.50.0/go.mod h1:wHFBCEVWVmHMUpg7pYcOm2QUR/ocQdYSJVQJKnHc3xQ=
github.com/prometheus/procfs v0.13.0 h1:GqzLlQyfsPbaEHaQkO7tbDlriv/4o5Hudv6OXHGKX7o=
github.com/prometheus/procfs v0.13.0/go.mod h1:cd4PFCR54QLnGKPaKGA6l+cfuNXtht43ZKY6tow0Y1g=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
Expand Down Expand Up @@ -147,8 +145,8 @@ go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ=
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81 h1:6R2FC06FonbXQ8pK11/PDFY6N6LWlf9KlzibaCapmqc=
golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
Expand Down Expand Up @@ -195,10 +193,10 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto/googleapis/api v0.0.0-20240304212257-790db918fca8 h1:8eadJkXbwDEMNwcB5O0s5Y5eCfyuCLdvaiOIaGTrWmQ=
google.golang.org/genproto/googleapis/api v0.0.0-20240304212257-790db918fca8/go.mod h1:O1cOfN1Cy6QEYr7VxtjOyP5AdAuR0aJ/MYZaaof623Y=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240304212257-790db918fca8 h1:IR+hp6ypxjH24bkMfEJ0yHR21+gwPWdV+/IBrPQyn3k=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240304212257-790db918fca8/go.mod h1:UCOku4NytXMJuLQE5VuqA5lX3PcHCBo8pxNyvkf4xBs=
google.golang.org/genproto/googleapis/api v0.0.0-20240314234333-6e1732d8331c h1:kaI7oewGK5YnVwj+Y+EJBO/YN1ht8iTL9XkFHtVZLsc=
google.golang.org/genproto/googleapis/api v0.0.0-20240314234333-6e1732d8331c/go.mod h1:VQW3tUculP/D4B+xVCo+VgSq8As6wA9ZjHl//pmk+6s=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c h1:lfpJ/2rWPa/kJgxyyXM8PrNnfCzcmxJ265mADgwmvLI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
Expand Down
4 changes: 1 addition & 3 deletions rest/connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,16 @@ import (
"fmt"
"os"

rest "github.com/hasura/ndc-rest-schema/schema"
"github.com/hasura/ndc-sdk-go/connector"
"github.com/hasura/ndc-sdk-go/schema"
"gopkg.in/yaml.v3"
)

// RESTConnector implements the SDK interface of NDC specification
type RESTConnector struct {
metadata RESTMetadataCollection
capabilities *schema.RawCapabilitiesResponse
schema *schema.RawSchemaResponse
functions map[string]rest.RESTFunctionInfo
procedures map[string]rest.RESTProcedureInfo
client *httpClient
}

Expand Down
4 changes: 4 additions & 0 deletions rest/connector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ func TestRESTConnector_configurationFailure(t *testing.T) {
assertError(t, err, "the config.{json,yaml,yml} file does not exist at")
}

func TestRESTConnector_authentication(t *testing.T) {

}

func assertNdcOperations(t *testing.T, dir string, targetURL string) {
queryFiles, err := os.ReadDir(dir)
if err != nil {
Expand Down
154 changes: 154 additions & 0 deletions rest/metadata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package rest

import (
"fmt"
"net/url"
"strings"

rest "github.com/hasura/ndc-rest-schema/schema"
"github.com/hasura/ndc-rest-schema/utils"
"github.com/hasura/ndc-sdk-go/schema"
)

// RESTMetadataCollection stores list of REST metadata with helper methods
type RESTMetadataCollection []RESTMetadata

func (rms RESTMetadataCollection) GetFunction(name string) (*rest.RESTFunctionInfo, error) {
for _, rm := range rms {
fn, err := rm.GetFunction(name)
if err != nil {
return nil, err
}
if fn != nil {
return fn, nil
}
}
return nil, schema.UnprocessableContentError(fmt.Sprintf("unsupported query: %s", name), nil)
}

func (rms RESTMetadataCollection) GetProcedure(name string) (*rest.RESTProcedureInfo, error) {
for _, rm := range rms {
fn, err := rm.GetProcedure(name)
if err != nil {
return nil, err
}
if fn != nil {
return fn, nil
}
}
return nil, schema.UnprocessableContentError(fmt.Sprintf("unsupported query: %s", name), nil)
}

// RESTMetadata stores REST schema with handy methods to build requests
type RESTMetadata struct {
settings *rest.NDCRestSettings
functions map[string]rest.RESTFunctionInfo
procedures map[string]rest.RESTProcedureInfo
}

func (rm RESTMetadata) GetFunction(name string) (*rest.RESTFunctionInfo, error) {
fn, ok := rm.functions[name]
if !ok {
return nil, nil
}

req, err := rm.buildRequest(fn.Request)
if err != nil {
return nil, err
}
return &rest.RESTFunctionInfo{
Request: req,
FunctionInfo: fn.FunctionInfo,
}, nil
}

func (rm RESTMetadata) GetProcedure(name string) (*rest.RESTProcedureInfo, error) {
fn, ok := rm.procedures[name]
if !ok {
return nil, nil
}

req, err := rm.buildRequest(fn.Request)
if err != nil {
return nil, err
}
return &rest.RESTProcedureInfo{
Request: req,
ProcedureInfo: fn.ProcedureInfo,
}, nil
}

func (rm RESTMetadata) buildRequest(rawReq *rest.Request) (*rest.Request, error) {
req := rawReq.Clone()
req.URL = rm.buildURL(req.URL)

return rm.applySecurity(req)
}

func (rm RESTMetadata) buildURL(endpoint string) string {
if strings.HasPrefix(endpoint, "http") {
return endpoint
}
var host string
for _, server := range rm.settings.Servers {
host = server.URL
break
}

return fmt.Sprintf("%s%s", host, endpoint)
}

func (rm RESTMetadata) applySecurity(req *rest.Request) (*rest.Request, error) {
if req.Security.IsEmpty() {
req.Security = rm.settings.Security
}
if req.Security.IsOptional() || len(rm.settings.SecuritySchemes) == 0 {
return req, nil
}

security := req.Security.First()
securityScheme, ok := rm.settings.SecuritySchemes[security.Name()]
if !ok {
return req, nil
}

if req.Headers == nil {
req.Headers = make(map[string]string)
}
switch securityScheme.Type {
case rest.HTTPAuthScheme:
headerName := securityScheme.Header
if headerName == "" {
headerName = "Authorization"
}
scheme := securityScheme.Name
if securityScheme.Name == "bearer" || securityScheme.Name == "basic" {
scheme = utils.ToPascalCase(securityScheme.Name)
}
req.Headers[headerName] = fmt.Sprintf("%s %s", scheme, securityScheme.Value)
case rest.APIKeyScheme:
switch securityScheme.In {
case rest.APIKeyInHeader:
req.Headers[securityScheme.Name] = securityScheme.Value
case rest.APIKeyInQuery:
endpoint, err := url.Parse(req.URL)
if err != nil {
return nil, err
}

q := endpoint.Query()
q.Add(securityScheme.Name, securityScheme.Value)
endpoint.RawQuery = q.Encode()
req.URL = endpoint.String()
case rest.APIKeyInCookie:
req.Headers["Cookie"] = fmt.Sprintf("%s=%s", securityScheme.Name, securityScheme.Value)
default:
return nil, fmt.Errorf("unsupported location for apiKey scheme: %s", securityScheme.In)
}
// TODO: support OAuth and OIDC
default:
return nil, fmt.Errorf("unsupported security scheme: %s", securityScheme.Type)
}

return req, nil
}
19 changes: 5 additions & 14 deletions rest/mutation.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"encoding/json"
"fmt"

rest "github.com/hasura/ndc-rest-schema/schema"
"github.com/hasura/ndc-sdk-go/schema"
)

Expand Down Expand Up @@ -33,9 +32,9 @@ func (c *RESTConnector) Mutation(ctx context.Context, configuration *Configurati

func (c *RESTConnector) execProcedure(ctx context.Context, operation *schema.MutationOperation) (schema.MutationOperationResults, error) {

procedure := c.getProcedure(operation.Name)
if procedure == nil {
return nil, schema.BadRequestError(fmt.Sprintf("unsupported procedure operation: %s", operation.Name), nil)
procedure, err := c.metadata.GetProcedure(operation.Name)
if err != nil {
return nil, err
}

// 1. resolve arguments, evaluate URL and query parameters
Expand All @@ -55,18 +54,10 @@ func (c *RESTConnector) execProcedure(ctx context.Context, operation *schema.Mut

// 2. create and execute request
// 3. evaluate response selection
rawRequest := procedure.Request.Clone()
rawRequest.URL = endpoint
result, err := c.client.Send(ctx, rawRequest, headers, rawArgs["body"], operation.Fields)
procedure.Request.URL = endpoint
result, err := c.client.Send(ctx, procedure.Request, headers, rawArgs["body"], operation.Fields)
if err != nil {
return nil, err
}
return schema.NewProcedureResult(result).Encode(), nil
}

func (c *RESTConnector) getProcedure(key string) *rest.RESTProcedureInfo {
if item, ok := c.procedures[key]; ok {
return &item
}
return nil
}
Loading

0 comments on commit 3c8f9bf

Please sign in to comment.