55package shared
66
77import (
8+ "bytes"
89 "context"
910 "crypto/tls"
1011 "crypto/x509"
1112 "fmt"
13+ "io"
1214 "net"
1315 "net/http"
1416 "net/url"
1517 "os"
1618 "strings"
1719 "time"
20+
21+ "github.com/aws/aws-sdk-go/aws/credentials"
22+ awsv4 "github.com/aws/aws-sdk-go/aws/signer/v4"
1823)
1924
2025var DefaultIonosBasePath = ""
@@ -28,6 +33,8 @@ const (
2833 IonosLogLevelEnvVar = "IONOS_LOG_LEVEL"
2934 IonosFilePathEnvVar = "IONOS_CONFIG_FILE"
3035 IonosCurrentProfileEnvVar = "IONOS_CURRENT_PROFILE"
36+ IonosS3AccessKeyEnvVar = "IONOS_S3_ACCESS_KEY"
37+ IonosS3SecretKeyEnvVar = "IONOS_S3_SECRET_KEY"
3138 DefaultIonosServerUrl = "https://api.ionos.com/"
3239
3340 defaultMaxRetries = 3
@@ -103,6 +110,15 @@ type ServerConfiguration struct {
103110// ServerConfigurations stores multiple ServerConfiguration items
104111type ServerConfigurations []ServerConfiguration
105112
113+ // MiddlewareFunction provides way to implement custom middleware in the prepareRequest
114+ type MiddlewareFunction func (* http.Request )
115+
116+ // MiddlewareFunctionWithError provides way to implement custom middleware with errors in the prepareRequest
117+ type MiddlewareFunctionWithError func (* http.Request ) error
118+
119+ // ResponseMiddlewareFunction provides way to implement custom middleware with errors after the response is received
120+ type ResponseMiddlewareFunction func (* http.Response , []byte ) error
121+
106122// Configuration stores the configuration of the API client
107123type Configuration struct {
108124 Host string `json:"host,omitempty"`
@@ -119,6 +135,10 @@ type Configuration struct {
119135 MaxRetries int `json:"maxRetries,omitempty"`
120136 WaitTime time.Duration `json:"waitTime,omitempty"`
121137 MaxWaitTime time.Duration `json:"maxWaitTime,omitempty"`
138+
139+ Middleware MiddlewareFunction `json:"-"`
140+ MiddlewareWithError MiddlewareFunctionWithError `json:"-"`
141+ ResponseMiddleware ResponseMiddlewareFunction `json:"-"`
122142}
123143
124144// NewConfiguration returns a new shared.Configuration object
@@ -161,9 +181,11 @@ type ClientOptions struct {
161181
162182// Credentials are the credentials that will be used for authentication
163183type Credentials struct {
164- Username string `yaml:"username"`
165- Password string `yaml:"password"`
166- Token string `yaml:"token"`
184+ Username string `yaml:"username"`
185+ Password string `yaml:"password"`
186+ Token string `yaml:"token"`
187+ S3AccessKey string `yaml:"s3AccessKey"`
188+ S3SecretKey string `yaml:"s3SecretKey"`
167189}
168190
169191// NewConfigurationFromOptions returns a new shared.Configuration object created from the client options
@@ -217,7 +239,10 @@ func CreateTransport(insecure bool, certificate string) *http.Transport {
217239}
218240
219241func NewConfigurationFromEnv () * Configuration {
220- return NewConfiguration (os .Getenv (IonosUsernameEnvVar ), os .Getenv (IonosPasswordEnvVar ), os .Getenv (IonosTokenEnvVar ), os .Getenv (IonosApiUrlEnvVar ))
242+ return NewConfiguration (
243+ os .Getenv (IonosUsernameEnvVar ), os .Getenv (IonosPasswordEnvVar ), os .Getenv (IonosTokenEnvVar ),
244+ os .Getenv (IonosApiUrlEnvVar ),
245+ )
221246}
222247
223248// AddDefaultHeader adds a new HTTP header to the default header in the request
@@ -253,7 +278,10 @@ func (sc ServerConfigurations) URL(index int, variables map[string]string) (stri
253278 }
254279 }
255280 if ! found {
256- return "" , fmt .Errorf ("the variable %s in the server URL has invalid value %v. Must be %v" , name , value , variable .EnumValues )
281+ return "" , fmt .Errorf (
282+ "the variable %s in the server URL has invalid value %v. Must be %v" , name , value ,
283+ variable .EnumValues ,
284+ )
257285 }
258286 serverUrl = strings .Replace (serverUrl , "{" + name + "}" , value , - 1 )
259287 } else {
@@ -305,7 +333,9 @@ func getServerVariables(ctx context.Context) (map[string]string, error) {
305333 if variables , ok := sv .(map [string ]string ); ok {
306334 return variables , nil
307335 }
308- return nil , reportError ("ctx value of ContextServerVariables has invalid type %T should be map[string]string" , sv )
336+ return nil , reportError (
337+ "ctx value of ContextServerVariables has invalid type %T should be map[string]string" , sv ,
338+ )
309339 }
310340 return nil , nil
311341}
@@ -314,7 +344,10 @@ func getServerOperationVariables(ctx context.Context, endpoint string) (map[stri
314344 osv := ctx .Value (ContextOperationServerVariables )
315345 if osv != nil {
316346 if operationVariables , ok := osv .(map [string ]map [string ]string ); ! ok {
317- return nil , reportError ("ctx value of ContextOperationServerVariables has invalid type %T should be map[string]map[string]string" , osv )
347+ return nil , reportError (
348+ "ctx value of ContextOperationServerVariables has invalid type %T should be map[string]map[string]string" ,
349+ osv ,
350+ )
318351 } else {
319352 variables , ok := operationVariables [endpoint ]
320353 if ok {
@@ -402,10 +435,12 @@ func OverrideLocationFor(configProvider ConfigProvider, location, endpoint strin
402435 }
403436 }
404437 SdkLogger .Printf ("[DEBUG] Adding new server configuration for location %s" , location )
405- configProvider .GetConfig ().Servers = append (configProvider .GetConfig ().Servers , ServerConfiguration {
406- URL : endpoint ,
407- Description : EndpointOverridden + location ,
408- })
438+ configProvider .GetConfig ().Servers = append (
439+ configProvider .GetConfig ().Servers , ServerConfiguration {
440+ URL : endpoint ,
441+ Description : EndpointOverridden + location ,
442+ },
443+ )
409444}
410445
411446func SetSkipTLSVerify (configProvider ConfigProvider , skipTLSVerify bool ) {
@@ -425,3 +460,27 @@ func AddCertsToClient(authorityData string) *x509.CertPool {
425460 }
426461 return rootCAs
427462}
463+
464+ func SignerMiddleware (region , service , accessKey , secretKey string ) MiddlewareFunctionWithError {
465+ signer := awsv4 .NewSigner (credentials .NewStaticCredentials (accessKey , secretKey , "" ))
466+
467+ // Define default values for region and service to maintain backward compatibility
468+ if region == "" {
469+ region = "eu-central-3"
470+ }
471+ if service == "" {
472+ service = "s3"
473+ }
474+ return func (r * http.Request ) error {
475+ var reader io.ReadSeeker
476+ if r .Body != nil {
477+ bodyBytes , err := io .ReadAll (r .Body )
478+ if err != nil {
479+ return err
480+ }
481+ reader = bytes .NewReader (bodyBytes )
482+ }
483+ _ , err := signer .Sign (r , reader , service , region , time .Now ())
484+ return err
485+ }
486+ }
0 commit comments