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