@@ -10,6 +10,14 @@ import (
1010 "context"
1111 "errors"
1212 "fmt"
13+ "net/http"
14+
15+ "google.golang.org/grpc"
16+ "google.golang.org/grpc/codes"
17+ "google.golang.org/grpc/metadata"
18+ "google.golang.org/grpc/status"
19+
20+ "go.opentelemetry.io/collector/internal/grpcutil"
1321
1422 "go.opentelemetry.io/collector/component"
1523 "go.opentelemetry.io/collector/extension/extensionauth"
2028 errNotHTTPClient = errors .New ("requested authenticator is not a HTTP client authenticator" )
2129 errNotGRPCClient = errors .New ("requested authenticator is not a gRPC client authenticator" )
2230 errNotServer = errors .New ("requested authenticator is not a server authenticator" )
31+ errMetadataNotFound = errors .New ("no request metadata found" )
2332)
2433
2534// Authentication defines the auth settings for the receiver.
@@ -28,8 +37,48 @@ type Authentication struct {
2837 AuthenticatorID component.ID `mapstructure:"authenticator,omitempty"`
2938}
3039
31- // GetServerAuthenticator attempts to select the appropriate extensionauth.Server from the list of extensions,
32- // based on the requested extension name. If an authenticator is not found, an error is returned.
40+ // GetGRPCServerOptions attempts to select the appropriate extensionauth.Server from the list of extensions,
41+ // based on the requested extension name and return the grpc.ServerOption to be used with the grpc.Server.
42+ // If an authenticator is not found, an error is returned.
43+ func (a Authentication ) GetGRPCServerOptions (_ context.Context , extensions map [component.ID ]component.Component ) ([]grpc.ServerOption , error ) {
44+ ext , found := extensions [a .AuthenticatorID ]
45+ if ! found {
46+ return nil , fmt .Errorf ("failed to resolve authenticator %q: %w" , a .AuthenticatorID , errAuthenticatorNotFound )
47+ }
48+
49+ eauth , ok := ext .(extensionauth.Server )
50+ if ! ok {
51+ return nil , errNotServer
52+ }
53+
54+ uInterceptor := func (ctx context.Context , req any , info * grpc.UnaryServerInfo , handler grpc.UnaryHandler ) (resp any , err error ) {
55+ return authServerUnaryInterceptor (ctx , req , info , handler , eauth )
56+ }
57+ sInterceptors := func (srv any , ss grpc.ServerStream , info * grpc.StreamServerInfo , handler grpc.StreamHandler ) error {
58+ return authServerStreamInterceptor (srv , ss , info , handler , eauth )
59+ }
60+
61+ return []grpc.ServerOption {grpc .ChainUnaryInterceptor (uInterceptor ), grpc .ChainStreamInterceptor (sInterceptors )}, nil
62+ }
63+
64+ // GetHTTPHandler attempts to select the appropriate extensionauth.Server from the list of extensions,
65+ // based on the requested extension name and return the http.Handler to be used with the http.Server.
66+ // If an authenticator is not found, an error is returned.
67+ func (a Authentication ) GetHTTPHandler (_ context.Context , extensions map [component.ID ]component.Component , next http.Handler , reqParams []string ) (http.Handler , error ) {
68+ ext , found := extensions [a .AuthenticatorID ]
69+ if ! found {
70+ return nil , fmt .Errorf ("failed to resolve authenticator %q: %w" , a .AuthenticatorID , errAuthenticatorNotFound )
71+ }
72+
73+ eauth , ok := ext .(extensionauth.Server )
74+ if ! ok {
75+ return nil , errNotServer
76+ }
77+
78+ return authInterceptor (next , eauth , reqParams ), nil
79+ }
80+
81+ // Deprecated: [v0.123.0] use GetGRPCServerOptions or GetHTTPServer.
3382func (a Authentication ) GetServerAuthenticator (_ context.Context , extensions map [component.ID ]component.Component ) (extensionauth.Server , error ) {
3483 if ext , found := extensions [a .AuthenticatorID ]; found {
3584 if server , ok := ext .(extensionauth.Server ); ok {
@@ -41,9 +90,7 @@ func (a Authentication) GetServerAuthenticator(_ context.Context, extensions map
4190 return nil , fmt .Errorf ("failed to resolve authenticator %q: %w" , a .AuthenticatorID , errAuthenticatorNotFound )
4291}
4392
44- // GetHTTPClientAuthenticator attempts to select the appropriate extensionauth.Client from the list of extensions,
45- // based on the component id of the extension. If an authenticator is not found, an error is returned.
46- // This should be only used by HTTP clients.
93+ // Deprecated: [v0.123.0] use GetHTTPRoundTripper.
4794func (a Authentication ) GetHTTPClientAuthenticator (_ context.Context , extensions map [component.ID ]component.Component ) (extensionauth.HTTPClient , error ) {
4895 if ext , found := extensions [a .AuthenticatorID ]; found {
4996 if client , ok := ext .(extensionauth.HTTPClient ); ok {
@@ -54,9 +101,25 @@ func (a Authentication) GetHTTPClientAuthenticator(_ context.Context, extensions
54101 return nil , fmt .Errorf ("failed to resolve authenticator %q: %w" , a .AuthenticatorID , errAuthenticatorNotFound )
55102}
56103
57- // GetGRPCClientAuthenticator attempts to select the appropriate extensionauth.Client from the list of extensions,
58- // based on the component id of the extension. If an authenticator is not found, an error is returned.
59- // This should be only used by gRPC clients.
104+ // GetHTTPRoundTripper attempts to select the appropriate extensionauth.Client from the list of extensions,
105+ // based on the component id of the extension and return the http.RoundTripper to be used with the http.Client.
106+ // If an authenticator is not found, an error is returned. This should be only used by HTTP clients.
107+ func (a Authentication ) GetHTTPRoundTripper (_ context.Context , extensions map [component.ID ]component.Component , base http.RoundTripper ) (http.RoundTripper , error ) {
108+ ext , found := extensions [a .AuthenticatorID ]
109+ if ! found {
110+ return nil , fmt .Errorf ("failed to resolve authenticator %q: %w" , a .AuthenticatorID , errAuthenticatorNotFound )
111+ }
112+
113+ // Currently only support `extensionauth.HTTPClient`.
114+ client , ok := ext .(extensionauth.HTTPClient )
115+ if ! ok {
116+ return nil , errNotHTTPClient
117+ }
118+
119+ return client .RoundTripper (base )
120+ }
121+
122+ // Deprecated: [v0.123.0] Use GetGRPCDialOptions.
60123func (a Authentication ) GetGRPCClientAuthenticator (_ context.Context , extensions map [component.ID ]component.Component ) (extensionauth.GRPCClient , error ) {
61124 if ext , found := extensions [a .AuthenticatorID ]; found {
62125 if client , ok := ext .(extensionauth.GRPCClient ); ok {
@@ -66,3 +129,74 @@ func (a Authentication) GetGRPCClientAuthenticator(_ context.Context, extensions
66129 }
67130 return nil , fmt .Errorf ("failed to resolve authenticator %q: %w" , a .AuthenticatorID , errAuthenticatorNotFound )
68131}
132+
133+ // GetGRPCDialOptions attempts to select the appropriate extensionauth.Client from the list of extensions,
134+ // based on the component id of the extension and return the grpc.DialOptions to be used with grpc.ClientConn.
135+ // If an authenticator is not found, an error is returned. This should be only used by gRPC clients.
136+ func (a Authentication ) GetGRPCDialOptions (_ context.Context , extensions map [component.ID ]component.Component ) ([]grpc.DialOption , error ) {
137+ ext , found := extensions [a .AuthenticatorID ]
138+ if ! found {
139+ return nil , fmt .Errorf ("failed to resolve authenticator %q: %w" , a .AuthenticatorID , errAuthenticatorNotFound )
140+ }
141+
142+ // Currently only support `extensionauth.GRPCClient`.
143+ client , ok := ext .(extensionauth.GRPCClient )
144+ if ! ok {
145+ return nil , errNotGRPCClient
146+ }
147+
148+ perRPCCredentials , err := client .PerRPCCredentials ()
149+ if err != nil {
150+ return nil , err
151+ }
152+
153+ return []grpc.DialOption {grpc .WithPerRPCCredentials (perRPCCredentials )}, nil
154+ }
155+
156+ func authServerUnaryInterceptor (ctx context.Context , req any , _ * grpc.UnaryServerInfo , handler grpc.UnaryHandler , eauth extensionauth.Server ) (any , error ) {
157+ headers , ok := metadata .FromIncomingContext (ctx )
158+ if ! ok {
159+ return nil , errMetadataNotFound
160+ }
161+
162+ ctx , err := eauth .Authenticate (ctx , headers )
163+ if err != nil {
164+ return nil , status .Error (codes .Unauthenticated , err .Error ())
165+ }
166+
167+ return handler (ctx , req )
168+ }
169+
170+ func authServerStreamInterceptor (srv any , stream grpc.ServerStream , _ * grpc.StreamServerInfo , handler grpc.StreamHandler , eauth extensionauth.Server ) error {
171+ ctx := stream .Context ()
172+ headers , ok := metadata .FromIncomingContext (ctx )
173+ if ! ok {
174+ return errMetadataNotFound
175+ }
176+
177+ ctx , err := eauth .Authenticate (ctx , headers )
178+ if err != nil {
179+ return status .Error (codes .Unauthenticated , err .Error ())
180+ }
181+
182+ return handler (srv , grpcutil .WrapServerStream (ctx , stream ))
183+ }
184+
185+ func authInterceptor (next http.Handler , eauth extensionauth.Server , requestParams []string ) http.Handler {
186+ return http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
187+ sources := r .Header
188+ query := r .URL .Query ()
189+ for _ , param := range requestParams {
190+ if val , ok := query [param ]; ok {
191+ sources [param ] = val
192+ }
193+ }
194+ ctx , err := eauth .Authenticate (r .Context (), sources )
195+ if err != nil {
196+ http .Error (w , http .StatusText (http .StatusUnauthorized ), http .StatusUnauthorized )
197+ return
198+ }
199+
200+ next .ServeHTTP (w , r .WithContext (ctx ))
201+ })
202+ }
0 commit comments