@@ -2,6 +2,8 @@ package proxy
22
33import (
44 "context"
5+ "encoding/base64"
6+ "errors"
57 "fmt"
68 "io"
79 "net/http"
@@ -16,7 +18,10 @@ import (
1618 "go.opentelemetry.io/otel/propagation"
1719
1820 "github.com/couchbase/gocbcorex"
21+ "github.com/couchbase/gocbcorex/cbauthx"
1922 "github.com/couchbase/gocbcorex/contrib/buildversion"
23+ "github.com/couchbase/stellar-gateway/gateway/auth"
24+ "github.com/couchbase/stellar-gateway/gateway/dapiimpl/server_v1"
2025 "go.opentelemetry.io/otel"
2126 "go.opentelemetry.io/otel/attribute"
2227 "go.opentelemetry.io/otel/metric"
@@ -45,12 +50,16 @@ type DataApiProxy struct {
4550 cbClient * gocbcorex.BucketsTrackingAgentManager
4651 disableAdmin bool
4752 debugMode bool
48- mux * http.ServeMux
53+ mux http.Handler
4954
5055 numFailures metric.Int64Counter
5156 numRequests metric.Int64Counter
5257 ttfbMillis metric.Int64Histogram
5358 durationMillis metric.Int64Histogram
59+
60+ username string
61+ password string
62+ authHander * server_v1.AuthHandler
5463}
5564
5665func NewDataApiProxy (
@@ -59,6 +68,8 @@ func NewDataApiProxy(
5968 services []ServiceType ,
6069 disableAdmin bool ,
6170 debugMode bool ,
71+ authHandler * server_v1.AuthHandler ,
72+ username , password string ,
6273) * DataApiProxy {
6374 mux := http .NewServeMux ()
6475
@@ -92,6 +103,9 @@ func NewDataApiProxy(
92103 numRequests : numRequests ,
93104 ttfbMillis : ttfbMillis ,
94105 durationMillis : durationMillis ,
106+ username : username ,
107+ password : password ,
108+ authHander : authHandler ,
95109 }
96110
97111 for _ , serviceName := range services {
@@ -127,9 +141,13 @@ func (p *DataApiProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
127141}
128142
129143func (p * DataApiProxy ) writeError (w http.ResponseWriter , err error , msg string ) {
144+ p .writeErrorWithStatus (w , err , msg , 502 )
145+ }
146+
147+ func (p * DataApiProxy ) writeErrorWithStatus (w http.ResponseWriter , err error , msg string , status int ) {
130148 p .logger .Debug (msg , zap .Error (err ))
131149
132- w .WriteHeader (502 )
150+ w .WriteHeader (status )
133151
134152 if ! p .debugMode {
135153 _ , _ = fmt .Fprintf (w , "%s" , msg )
@@ -252,6 +270,33 @@ func (p *DataApiProxy) proxyService(
252270 // copy some other details
253271 proxyReq .Header = r .Header
254272
273+ // If no auth header has been given, check for a client cert
274+ authHdr := proxyReq .Header .Get ("Authorization" )
275+ if authHdr == "" {
276+ oboUser , oboDomain , err := p .authHander .Authenticator .ValidateConnStateForObo (ctx , r .TLS )
277+ if err != nil {
278+ if errors .Is (err , auth .ErrInvalidCertificate ) {
279+ p .writeError (w , err , "failed to validate cetificate" )
280+ return
281+ } else if errors .Is (err , cbauthx .ErrNoCert ) {
282+ p .writeErrorWithStatus (w , err , "authorization header or client cert are required" , 401 )
283+ return
284+ }
285+
286+ p .writeErrorWithStatus (w , err , "received an unexpected cert authentication error" , 401 )
287+ return
288+ }
289+
290+ // We only set the on behalf of and auth headers if there is a user, if
291+ // we set the onbehalf of header to an empty string and use the admin
292+ // creds then the server seems to ignore the obo header.
293+ if oboUser != "" {
294+ oboHdrStr := base64 .StdEncoding .EncodeToString ([]byte (fmt .Sprintf ("%s:%s" , oboUser , oboDomain )))
295+ proxyReq .Header .Set ("cb-on-behalf-of" , oboHdrStr )
296+ proxyReq .SetBasicAuth (p .username , p .password )
297+ }
298+ }
299+
255300 tr := otelhttp .NewTransport (
256301 roundTripper ,
257302 // By setting the otelhttptrace client in this transport, it can be
0 commit comments