diff --git a/awsauth/sign.go b/awsauth/sign.go new file mode 100644 index 0000000..6f2e3e1 --- /dev/null +++ b/awsauth/sign.go @@ -0,0 +1,77 @@ +package awsauth + +import ( + "errors" + "io" + "net/http" + "time" + + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds" + "github.com/aws/aws-sdk-go/aws/ec2metadata" + "github.com/aws/aws-sdk-go/aws/session" + signerV4 "github.com/aws/aws-sdk-go/aws/signer/v4" +) + +type Signer struct { + awsRegion string + awsService string + v4 *signerV4.Signer +} + +func NewAwsSigner(awsFilename, awsProfile, awsRegion, awsService string) (signer *Signer, err error) { + if err = validateAwsSDKSigner(awsRegion, awsService); err != nil { + return + } + + var sess *session.Session + sess, err = session.NewSession() + if err != nil { + return + } + + creds := credentials.NewChainCredentials( + []credentials.Provider{ + &credentials.EnvProvider{}, + &credentials.SharedCredentialsProvider{ + Filename: awsFilename, + Profile: awsProfile, + }, + &ec2rolecreds.EC2RoleProvider{ + Client: ec2metadata.New(sess), + }, + }, + ) + + signer = &Signer{ + awsRegion: awsRegion, + awsService: awsService, + v4: signerV4.NewSigner(creds), + } + + return +} + +func (s *Signer) Sign(req *http.Request, bodyReader io.ReadSeeker, currentTime time.Time) (err error) { + if s == nil || s.v4 == nil { + return errors.New("v4 signer missing. Cannot sign request") + } + + if _, err = s.v4.Sign(req, bodyReader, s.awsService, s.awsRegion, time.Now()); err != nil { + return + } + + return +} + +func validateAwsSDKSigner(awsRegion, awsService string) error { + if awsRegion == "" { + return errors.New("no AWS region was provided. Cannot sign request") + } + + if awsService == "" { + return errors.New("no AWS service was provided. Cannot sign request") + } + + return nil +} diff --git a/awsauth/sign_test.go b/awsauth/sign_test.go new file mode 100644 index 0000000..564498e --- /dev/null +++ b/awsauth/sign_test.go @@ -0,0 +1,119 @@ +package awsauth + +import ( + "errors" + "net/http/httptest" + "os" + "testing" + "time" + + . "github.com/smartystreets/goconvey/convey" +) + +const ( + envAccessKeyID = "AWS_ACCESS_KEY_ID" + envSecretAccessKey = "AWS_SECRET_ACCESS_KEY" + + testAccessKey = "TEST_ACCESS_KEY" + testSecretAccessKey = "TEST_SECRET_KEY" +) + +func TestCreateNewSigner(t *testing.T) { + Convey("Given that we want to create the aws sdk signer", t, func() { + Convey("When the region is set to an empty string", func() { + Convey("Then an error is returned when retrieving aws sdk signer", func() { + signer, err := NewAwsSigner("", "", "", "es") + So(err, ShouldResemble, errors.New("no AWS region was provided. Cannot sign request")) + So(signer, ShouldBeNil) + }) + }) + + Convey("When the service is set to an empty string", func() { + Convey("Then an error is returned when retrieving aws sdk signer", func() { + signer, err := NewAwsSigner("", "", "eu-west-1", "") + So(err, ShouldResemble, errors.New("no AWS service was provided. Cannot sign request")) + So(signer, ShouldBeNil) + }) + }) + + Convey("When the service and region are set and credentials are set in environment variables", func() { + accessKeyID, secretAccessKey := setEnvironmentVars() + + Convey("Then no error is returned when retrieving aws sdk signer", func() { + signer, err := NewAwsSigner("", "", "eu-west-1", "es") + So(err, ShouldBeNil) + So(signer, ShouldNotBeNil) + + Convey("And no error is returned when attempting to Sign the request", func() { + req := httptest.NewRequest("GET", "http://test-url", nil) + + err := signer.Sign(req, nil, time.Now()) + So(err, ShouldBeNil) + }) + }) + + removeTestEnvironmentVariables(accessKeyID, secretAccessKey) + }) + }) +} + +func TestSignFunc(t *testing.T) { + Convey("Given that we want to use the aws sdk signer to sign request", t, func() { + Convey("When the signer is nil", func() { + Convey("Then an error is returned when attempting to Sign the request", func() { + var signer *Signer + req := httptest.NewRequest("GET", "http://test-url", nil) + + err := signer.Sign(req, nil, time.Now()) + So(err, ShouldResemble, errors.New("v4 signer missing. Cannot sign request")) + }) + }) + + Convey("When the signer.v4 is nil", func() { + Convey("Then an error is returned when attempting to Sign the request", func() { + signer := &Signer{ + v4: nil, + } + req := httptest.NewRequest("GET", "http://test-url", nil) + + err := signer.Sign(req, nil, time.Now()) + So(err, ShouldResemble, errors.New("v4 signer missing. Cannot sign request")) + }) + }) + + Convey("When the signer.v4 is a valid aws v4 signer", func() { + // Create valid v4 signer + accessKeyID, secretAccessKey := setEnvironmentVars() + + signer, err := NewAwsSigner("", "", "eu-west-1", "es") + So(err, ShouldBeNil) + So(signer, ShouldNotBeNil) + So(signer.v4, ShouldNotBeNil) + + Convey("Then the request successfully signs and does not return an error", func() { + + req := httptest.NewRequest("GET", "http://test-url", nil) + + err = signer.Sign(req, nil, time.Now()) + So(err, ShouldBeNil) + }) + + removeTestEnvironmentVariables(accessKeyID, secretAccessKey) + }) + }) +} + +func setEnvironmentVars() (accessKeyID, secretAccessKey string) { + accessKeyID = os.Getenv(envAccessKeyID) + secretAccessKey = os.Getenv(envSecretAccessKey) + + os.Setenv(envAccessKeyID, testAccessKey) + os.Setenv(envSecretAccessKey, testSecretAccessKey) + + return +} + +func removeTestEnvironmentVariables(accessKeyID, secretAccessKey string) { + os.Setenv(envAccessKeyID, accessKeyID) + os.Setenv(envSecretAccessKey, secretAccessKey) +} diff --git a/go.mod b/go.mod index 9500aed..c5590cf 100644 --- a/go.mod +++ b/go.mod @@ -4,17 +4,16 @@ go 1.13 require ( github.com/ONSdigital/dp-api-clients-go v1.41.1 - github.com/ONSdigital/dp-elasticsearch/v2 v2.3.0 // indirect github.com/ONSdigital/dp-healthcheck v1.1.0 // indirect github.com/ONSdigital/log.go v1.1.0 // indirect github.com/ONSdigital/log.go/v2 v2.0.9 - github.com/fatih/color v1.12.0 // indirect + github.com/aws/aws-sdk-go v1.38.15 github.com/gorilla/mux v1.8.0 github.com/justinas/alice v1.2.0 github.com/mattn/go-isatty v0.0.13 // indirect github.com/pkg/errors v0.9.1 - github.com/smartystreets/goconvey v1.6.4 - github.com/stretchr/testify v1.7.0 // indirect + github.com/smartystreets/goconvey v1.7.2 + github.com/stretchr/testify v1.7.0 golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e // indirect ) diff --git a/go.sum b/go.sum index df93e62..3ea54e0 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,6 @@ github.com/ONSdigital/dp-api-clients-go v1.28.0/go.mod h1:iyJy6uRL4B6OYOJA0XMr5U github.com/ONSdigital/dp-api-clients-go v1.34.3/go.mod h1:kX+YKuoLYLfkeLHMvQKRRydZVxO7ZEYyYiwG2xhV51E= github.com/ONSdigital/dp-api-clients-go v1.41.1 h1:xkeT6dCTFSAoBpZxgiJUiuqgcfjCX+c52CIiZo1Y2iU= github.com/ONSdigital/dp-api-clients-go v1.41.1/go.mod h1:Ga1+ANjviu21NFJI9wp5NctJIdB4TJLDGbpQFl2V8Wc= -github.com/ONSdigital/dp-elasticsearch/v2 v2.3.0 h1:RPGEjjpnJQrbEEFY/uPf2yb4ewhRlDk9AJOc7LZfWW4= -github.com/ONSdigital/dp-elasticsearch/v2 v2.3.0/go.mod h1:B+WXR1PRVgrU1/tS3UygATLVTnX+5Sp+M0w5i3LROac= github.com/ONSdigital/dp-healthcheck v1.0.5/go.mod h1:2wbVAUHMl9+4tWhUlxYUuA1dnf2+NrwzC+So5f5BMLk= github.com/ONSdigital/dp-healthcheck v1.1.0 h1:fKOf8MMe8l4EW28ljX0wNZ5oZTgx/slAs7lyEx4eq2c= github.com/ONSdigital/dp-healthcheck v1.1.0/go.mod h1:vZwyjMJiCHjp/sJ2R1ZEqzZT0rJ0+uHVGwxqdP4J5vg= @@ -22,8 +20,8 @@ github.com/ONSdigital/log.go v1.0.1/go.mod h1:dIwSXuvFB5EsZG5x44JhsXZKMd80zlb0DZ github.com/ONSdigital/log.go v1.1.0 h1:XFE8U5lPeiXyujgUtbh+pKCotiICeIGFEAauNk9c24A= github.com/ONSdigital/log.go v1.1.0/go.mod h1:0hOVuYR3bDUI30VRo48d5KHfJIoe+spuPXqgt6UF78o= github.com/ONSdigital/log.go/v2 v2.0.0/go.mod h1:PR7vXrv9dZKUc7SI/0toxBbStk84snmybBnWpe+xY2o= -github.com/ONSdigital/log.go/v2 v2.0.5 h1:kl2lF0vr3BQDwPTAUcarDvX4Um3uE3fjVRfLoHAarBc= github.com/ONSdigital/log.go/v2 v2.0.5/go.mod h1:PR7vXrv9dZKUc7SI/0toxBbStk84snmybBnWpe+xY2o= +github.com/ONSdigital/log.go/v2 v2.0.9 h1:dMtuN89vCP21iRuOBAGInn7ZzxIEGajC3o5pjoicnsY= github.com/ONSdigital/log.go/v2 v2.0.9/go.mod h1:VyTDkL82FtiAkaNFaT+bURBhLbP7NsIx4rkVbdpiuEg= github.com/aws/aws-sdk-go v1.38.15 h1:usaPeqoxFUzy0FfBLZLZHya5Kv2cpURjb1jqCa7+odA= github.com/aws/aws-sdk-go v1.38.15/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= @@ -47,6 +45,7 @@ github.com/hokaccha/go-prettyjson v0.0.0-20210113012101-fb4e108d2519 h1:nqAlWFEd github.com/hokaccha/go-prettyjson v0.0.0-20210113012101-fb4e108d2519/go.mod h1:pFlLw2CfqZiIBOx6BuCeRLCrfxBJipTY0nIOF/VbGcI= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= @@ -69,8 +68,9 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= -github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= +github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -102,6 +102,7 @@ golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -109,7 +110,9 @@ golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/http/custom_roundtripper.go b/http/custom_roundtripper.go index 34b3902..95082d9 100644 --- a/http/custom_roundtripper.go +++ b/http/custom_roundtripper.go @@ -7,7 +7,7 @@ import ( "net/http" "time" - awsAuth "github.com/ONSdigital/dp-elasticsearch/v2/awsauth" + awsAuth "github.com/ONSdigital/dp-net/awsauth" ) type AwsSignerRoundTripper struct {