@@ -10,16 +10,24 @@ 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"
1319
1420 "go.opentelemetry.io/collector/component"
1521 "go.opentelemetry.io/collector/extension/extensionauth"
22+ "go.opentelemetry.io/collector/internal/grpcutil"
1623)
1724
1825var (
1926 errAuthenticatorNotFound = errors .New ("authenticator not found" )
2027 errNotHTTPClient = errors .New ("requested authenticator is not a HTTP client authenticator" )
2128 errNotGRPCClient = errors .New ("requested authenticator is not a gRPC client authenticator" )
2229 errNotServer = errors .New ("requested authenticator is not a server authenticator" )
30+ errMetadataNotFound = errors .New ("no request metadata found" )
2331)
2432
2533// Authentication defines the auth settings for the receiver.
@@ -28,8 +36,48 @@ type Authentication struct {
2836 AuthenticatorID component.ID `mapstructure:"authenticator,omitempty"`
2937}
3038
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.
39+ // GetGRPCServerOptions attempts to select the appropriate extensionauth.Server from the list of extensions,
40+ // based on the requested extension name and return the grpc.ServerOption to be used with the grpc.Server.
41+ // If an authenticator is not found, an error is returned.
42+ func (a Authentication ) GetGRPCServerOptions (_ context.Context , extensions map [component.ID ]component.Component ) ([]grpc.ServerOption , error ) {
43+ ext , found := extensions [a .AuthenticatorID ]
44+ if ! found {
45+ return nil , fmt .Errorf ("failed to resolve authenticator %q: %w" , a .AuthenticatorID , errAuthenticatorNotFound )
46+ }
47+
48+ eauth , ok := ext .(extensionauth.Server )
49+ if ! ok {
50+ return nil , errNotServer
51+ }
52+
53+ uInterceptor := func (ctx context.Context , req any , info * grpc.UnaryServerInfo , handler grpc.UnaryHandler ) (resp any , err error ) {
54+ return authServerUnaryInterceptor (ctx , req , info , handler , eauth )
55+ }
56+ sInterceptors := func (srv any , ss grpc.ServerStream , info * grpc.StreamServerInfo , handler grpc.StreamHandler ) error {
57+ return authServerStreamInterceptor (srv , ss , info , handler , eauth )
58+ }
59+
60+ return []grpc.ServerOption {grpc .ChainUnaryInterceptor (uInterceptor ), grpc .ChainStreamInterceptor (sInterceptors )}, nil
61+ }
62+
63+ // GetHTTPHandler attempts to select the appropriate extensionauth.Server from the list of extensions,
64+ // based on the requested extension name and return the http.Handler to be used with the http.Server.
65+ // If an authenticator is not found, an error is returned.
66+ func (a Authentication ) GetHTTPHandler (_ context.Context , extensions map [component.ID ]component.Component , next http.Handler , reqParams []string ) (http.Handler , error ) {
67+ ext , found := extensions [a .AuthenticatorID ]
68+ if ! found {
69+ return nil , fmt .Errorf ("failed to resolve authenticator %q: %w" , a .AuthenticatorID , errAuthenticatorNotFound )
70+ }
71+
72+ eauth , ok := ext .(extensionauth.Server )
73+ if ! ok {
74+ return nil , errNotServer
75+ }
76+
77+ return authInterceptor (next , eauth , reqParams ), nil
78+ }
79+
80+ // Deprecated: [v0.123.0] use GetGRPCServerOptions or GetHTTPServer.
3381func (a Authentication ) GetServerAuthenticator (_ context.Context , extensions map [component.ID ]component.Component ) (extensionauth.Server , error ) {
3482 if ext , found := extensions [a .AuthenticatorID ]; found {
3583 if server , ok := ext .(extensionauth.Server ); ok {
@@ -41,9 +89,7 @@ func (a Authentication) GetServerAuthenticator(_ context.Context, extensions map
4189 return nil , fmt .Errorf ("failed to resolve authenticator %q: %w" , a .AuthenticatorID , errAuthenticatorNotFound )
4290}
4391
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.
92+ // Deprecated: [v0.123.0] use GetHTTPRoundTripper.
4793func (a Authentication ) GetHTTPClientAuthenticator (_ context.Context , extensions map [component.ID ]component.Component ) (extensionauth.HTTPClient , error ) {
4894 if ext , found := extensions [a .AuthenticatorID ]; found {
4995 if client , ok := ext .(extensionauth.HTTPClient ); ok {
@@ -54,9 +100,25 @@ func (a Authentication) GetHTTPClientAuthenticator(_ context.Context, extensions
54100 return nil , fmt .Errorf ("failed to resolve authenticator %q: %w" , a .AuthenticatorID , errAuthenticatorNotFound )
55101}
56102
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.
103+ // GetHTTPRoundTripper attempts to select the appropriate extensionauth.Client from the list of extensions,
104+ // based on the component id of the extension and return the http.RoundTripper to be used with the http.Client.
105+ // If an authenticator is not found, an error is returned. This should be only used by HTTP clients.
106+ func (a Authentication ) GetHTTPRoundTripper (_ context.Context , extensions map [component.ID ]component.Component , base http.RoundTripper ) (http.RoundTripper , error ) {
107+ ext , found := extensions [a .AuthenticatorID ]
108+ if ! found {
109+ return nil , fmt .Errorf ("failed to resolve authenticator %q: %w" , a .AuthenticatorID , errAuthenticatorNotFound )
110+ }
111+
112+ // Currently only support `extensionauth.HTTPClient`.
113+ client , ok := ext .(extensionauth.HTTPClient )
114+ if ! ok {
115+ return nil , errNotHTTPClient
116+ }
117+
118+ return client .RoundTripper (base )
119+ }
120+
121+ // Deprecated: [v0.123.0] Use GetGRPCDialOptions.
60122func (a Authentication ) GetGRPCClientAuthenticator (_ context.Context , extensions map [component.ID ]component.Component ) (extensionauth.GRPCClient , error ) {
61123 if ext , found := extensions [a .AuthenticatorID ]; found {
62124 if client , ok := ext .(extensionauth.GRPCClient ); ok {
@@ -66,3 +128,74 @@ func (a Authentication) GetGRPCClientAuthenticator(_ context.Context, extensions
66128 }
67129 return nil , fmt .Errorf ("failed to resolve authenticator %q: %w" , a .AuthenticatorID , errAuthenticatorNotFound )
68130}
131+
132+ // GetGRPCDialOptions attempts to select the appropriate extensionauth.Client from the list of extensions,
133+ // based on the component id of the extension and return the grpc.DialOptions to be used with grpc.ClientConn.
134+ // If an authenticator is not found, an error is returned. This should be only used by gRPC clients.
135+ func (a Authentication ) GetGRPCDialOptions (_ context.Context , extensions map [component.ID ]component.Component ) ([]grpc.DialOption , error ) {
136+ ext , found := extensions [a .AuthenticatorID ]
137+ if ! found {
138+ return nil , fmt .Errorf ("failed to resolve authenticator %q: %w" , a .AuthenticatorID , errAuthenticatorNotFound )
139+ }
140+
141+ // Currently only support `extensionauth.GRPCClient`.
142+ client , ok := ext .(extensionauth.GRPCClient )
143+ if ! ok {
144+ return nil , errNotGRPCClient
145+ }
146+
147+ perRPCCredentials , err := client .PerRPCCredentials ()
148+ if err != nil {
149+ return nil , err
150+ }
151+
152+ return []grpc.DialOption {grpc .WithPerRPCCredentials (perRPCCredentials )}, nil
153+ }
154+
155+ func authServerUnaryInterceptor (ctx context.Context , req any , _ * grpc.UnaryServerInfo , handler grpc.UnaryHandler , eauth extensionauth.Server ) (any , error ) {
156+ headers , ok := metadata .FromIncomingContext (ctx )
157+ if ! ok {
158+ return nil , errMetadataNotFound
159+ }
160+
161+ ctx , err := eauth .Authenticate (ctx , headers )
162+ if err != nil {
163+ return nil , status .Error (codes .Unauthenticated , err .Error ())
164+ }
165+
166+ return handler (ctx , req )
167+ }
168+
169+ func authServerStreamInterceptor (srv any , stream grpc.ServerStream , _ * grpc.StreamServerInfo , handler grpc.StreamHandler , eauth extensionauth.Server ) error {
170+ ctx := stream .Context ()
171+ headers , ok := metadata .FromIncomingContext (ctx )
172+ if ! ok {
173+ return errMetadataNotFound
174+ }
175+
176+ ctx , err := eauth .Authenticate (ctx , headers )
177+ if err != nil {
178+ return status .Error (codes .Unauthenticated , err .Error ())
179+ }
180+
181+ return handler (srv , grpcutil .WrapServerStream (ctx , stream ))
182+ }
183+
184+ func authInterceptor (next http.Handler , eauth extensionauth.Server , requestParams []string ) http.Handler {
185+ return http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
186+ sources := r .Header
187+ query := r .URL .Query ()
188+ for _ , param := range requestParams {
189+ if val , ok := query [param ]; ok {
190+ sources [param ] = val
191+ }
192+ }
193+ ctx , err := eauth .Authenticate (r .Context (), sources )
194+ if err != nil {
195+ http .Error (w , http .StatusText (http .StatusUnauthorized ), http .StatusUnauthorized )
196+ return
197+ }
198+
199+ next .ServeHTTP (w , r .WithContext (ctx ))
200+ })
201+ }
0 commit comments