@@ -10,6 +10,14 @@ import (
10
10
"context"
11
11
"errors"
12
12
"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"
13
21
14
22
"go.opentelemetry.io/collector/component"
15
23
"go.opentelemetry.io/collector/extension/extensionauth"
20
28
errNotHTTPClient = errors .New ("requested authenticator is not a HTTP client authenticator" )
21
29
errNotGRPCClient = errors .New ("requested authenticator is not a gRPC client authenticator" )
22
30
errNotServer = errors .New ("requested authenticator is not a server authenticator" )
31
+ errMetadataNotFound = errors .New ("no request metadata found" )
23
32
)
24
33
25
34
// Authentication defines the auth settings for the receiver.
@@ -28,8 +37,48 @@ type Authentication struct {
28
37
AuthenticatorID component.ID `mapstructure:"authenticator,omitempty"`
29
38
}
30
39
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.
33
82
func (a Authentication ) GetServerAuthenticator (_ context.Context , extensions map [component.ID ]component.Component ) (extensionauth.Server , error ) {
34
83
if ext , found := extensions [a .AuthenticatorID ]; found {
35
84
if server , ok := ext .(extensionauth.Server ); ok {
@@ -41,9 +90,7 @@ func (a Authentication) GetServerAuthenticator(_ context.Context, extensions map
41
90
return nil , fmt .Errorf ("failed to resolve authenticator %q: %w" , a .AuthenticatorID , errAuthenticatorNotFound )
42
91
}
43
92
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.
47
94
func (a Authentication ) GetHTTPClientAuthenticator (_ context.Context , extensions map [component.ID ]component.Component ) (extensionauth.HTTPClient , error ) {
48
95
if ext , found := extensions [a .AuthenticatorID ]; found {
49
96
if client , ok := ext .(extensionauth.HTTPClient ); ok {
@@ -54,9 +101,25 @@ func (a Authentication) GetHTTPClientAuthenticator(_ context.Context, extensions
54
101
return nil , fmt .Errorf ("failed to resolve authenticator %q: %w" , a .AuthenticatorID , errAuthenticatorNotFound )
55
102
}
56
103
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.
60
123
func (a Authentication ) GetGRPCClientAuthenticator (_ context.Context , extensions map [component.ID ]component.Component ) (extensionauth.GRPCClient , error ) {
61
124
if ext , found := extensions [a .AuthenticatorID ]; found {
62
125
if client , ok := ext .(extensionauth.GRPCClient ); ok {
@@ -66,3 +129,74 @@ func (a Authentication) GetGRPCClientAuthenticator(_ context.Context, extensions
66
129
}
67
130
return nil , fmt .Errorf ("failed to resolve authenticator %q: %w" , a .AuthenticatorID , errAuthenticatorNotFound )
68
131
}
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