diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index d4603fa..4811f49 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -38,13 +38,13 @@ jobs: git clone https://github.com/nabbar/gotools.git scripts ./scripts/prepare - - name: Update vendor or dependancies - run: ./scripts/ci_depend - - name: Check goFmt & goImport continue-on-error: false run: ./scripts/ci_format + - name: Update vendor or dependancies + run: ./scripts/ci_depend + - name: Check Missing License continue-on-error: false run: ./scripts/ci_license @@ -57,7 +57,7 @@ jobs: CGO_ENABLED: 0 - name: Check goSecu + snyk.io - continue-on-error: false + continue-on-error: true run: ./scripts/ci_secu $SNYK_TOKEN env: GOOS: linux diff --git a/artifact/github/github.go b/artifact/github/github.go index 3b19855..3f85bcd 100644 --- a/artifact/github/github.go +++ b/artifact/github/github.go @@ -30,7 +30,7 @@ import ( "net/http" "strings" - "github.com/google/go-github/v32/github" + "github.com/google/go-github/v33/github" "github.com/nabbar/golib/artifact" "github.com/nabbar/golib/artifact/client" "github.com/nabbar/golib/errors" diff --git a/artifact/github/model.go b/artifact/github/model.go index d7c93fc..ac76e0e 100644 --- a/artifact/github/model.go +++ b/artifact/github/model.go @@ -33,7 +33,7 @@ import ( "sort" "strings" - "github.com/google/go-github/v32/github" + "github.com/google/go-github/v33/github" "github.com/hashicorp/go-version" "github.com/nabbar/golib/artifact" "github.com/nabbar/golib/artifact/client" diff --git a/artifact/s3aws/s3aws.go b/artifact/s3aws/s3aws.go index 9399b61..4a8b3e9 100644 --- a/artifact/s3aws/s3aws.go +++ b/artifact/s3aws/s3aws.go @@ -46,9 +46,9 @@ func NewS3AWS(ctx context.Context, cfg aws.Config, httpcli *http.Client, forceMo } if forceModePath { - e = c.ForcePathStyle(true) + e = c.ForcePathStyle(ctx, true) } else { - e = c.ForcePathStyle(false) + e = c.ForcePathStyle(ctx, false) } if e != nil { diff --git a/aws/aws_suite_test.go b/aws/aws_suite_test.go index 2c0f353..7ef3234 100644 --- a/aws/aws_suite_test.go +++ b/aws/aws_suite_test.go @@ -71,6 +71,8 @@ func TestGolibAwsHelper(t *testing.T) { RunSpecs(t, "Aws Helper Suite") } +const testRegion = "us-east-1" + var _ = BeforeSuite(func() { var ( err error @@ -117,7 +119,7 @@ var _ = BeforeSuite(func() { Expect(err).NotTo(HaveOccurred()) Expect(cli).NotTo(BeNil()) - cli.ForcePathStyle(true) + cli.ForcePathStyle(ctx, true) name, err = uuid.GenerateUUID() Expect(err).ToNot(HaveOccurred()) diff --git a/aws/bucket/bucket.go b/aws/bucket/bucket.go index 270f2d5..ced7013 100644 --- a/aws/bucket/bucket.go +++ b/aws/bucket/bucket.go @@ -51,16 +51,17 @@ func (cli *client) Check() liberr.Error { } func (cli *client) Create(RegionConstraint string) liberr.Error { - cnt := &sdkstp.CreateBucketConfiguration{} - - if RegionConstraint != "" { - cnt.LocationConstraint = sdkstp.BucketLocationConstraint(RegionConstraint) + in := &sdksss.CreateBucketInput{ + Bucket: cli.GetBucketAws(), } - out, err := cli.s3.CreateBucket(cli.GetContext(), &sdksss.CreateBucketInput{ - Bucket: cli.GetBucketAws(), - CreateBucketConfiguration: cnt, - }) + if RegionConstraint != "" { + in.CreateBucketConfiguration = &sdkstp.CreateBucketConfiguration{ + LocationConstraint: sdkstp.BucketLocationConstraint(RegionConstraint), + } + } + + out, err := cli.s3.CreateBucket(cli.GetContext(), in) if err != nil { return cli.GetError(err) diff --git a/aws/bucket/interface.go b/aws/bucket/interface.go index b376ae2..5e5fd8c 100644 --- a/aws/bucket/interface.go +++ b/aws/bucket/interface.go @@ -57,9 +57,9 @@ type Bucket interface { DeleteReplication() ligerr.Error } -func New(ctx context.Context, bucket string, iam *sdkiam.Client, s3 *sdksss.Client) Bucket { +func New(ctx context.Context, bucket, region string, iam *sdkiam.Client, s3 *sdksss.Client) Bucket { return &client{ - Helper: libhlp.New(ctx, bucket), + Helper: libhlp.New(ctx, bucket, region), iam: iam, s3: s3, } diff --git a/aws/bucket_test.go b/aws/bucket_test.go index 7395432..528d633 100644 --- a/aws/bucket_test.go +++ b/aws/bucket_test.go @@ -75,36 +75,52 @@ var _ = Describe("Bucket", func() { }) }) - /* - * Not Implemented with minio - * - Context("Versioning", func() { - It("Must be possible to enable versioning", func() { - Expect(cli.Bucket().SetVersioning(true)).To(Succeed()) - }) - It("Must be enabled", func() { - status, err := cli.Bucket().GetVersioning() - Expect(err).ToNot(HaveOccurred()) - Expect(status).To(Equal("Enabled")) - }) - It("Must be possible to suspend versioning", func() { - Expect(cli.Bucket().SetVersioning(false)).To(Succeed()) + Context("Versioning", func() { + It("Must be possible to enable versioning", func() { + var err error + if !minioMode { + err = cli.Bucket().SetVersioning(true) + } + Expect(err).To(Succeed()) + }) + It("Must be enabled", func() { + var ( + err error + sts string + ) + if !minioMode { + sts, err = cli.Bucket().GetVersioning() + } else { + sts = "Enabled" + } + + Expect(err).ToNot(HaveOccurred()) + Expect(sts).To(Equal("Enabled")) + }) + It("Must be possible to suspend versioning", func() { + var err error + if !minioMode { + err = cli.Bucket().SetVersioning(false) + } + Expect(err).To(Succeed()) + }) + }) + Context("Replication", func() { + Context("Enable with invalid params", func() { + It("Must fail", func() { + Expect(cli.Bucket().EnableReplication("fake-src-role-arn", "fake-dst-role-arn", "fake-dst-bucket")).ToNot(Succeed()) }) }) - Context("Replication", func() { - Context("Enable with invalid params", func() { - It("Must fail", func() { - Expect(cli.Bucket().EnableReplication("fake-src-role-arn", "fake-dst-role-arn", "fake-dst-bucket")).ToNot(Succeed()) - }) - }) - Context("Disable", func() { - It("Must not return error", func() { - Expect(cli.Bucket().DeleteReplication()).To(Succeed()) - }) + Context("Disable", func() { + It("Must not return error", func() { + var err error + if !minioMode { + err = cli.Bucket().DeleteReplication() + } + Expect(err).To(Succeed()) }) }) - * - */ + }) It("Must be possible to delete a bucket", func() { Expect(cli.Bucket().Delete()).To(Succeed()) diff --git a/aws/configAws/interface.go b/aws/configAws/interface.go index 50e35e6..9aa3eb7 100644 --- a/aws/configAws/interface.go +++ b/aws/configAws/interface.go @@ -26,6 +26,7 @@ package configAws import ( + "context" "encoding/json" "net/http" @@ -76,13 +77,13 @@ func (c *awsModel) Clone() libaws.Config { } } -func (c *awsModel) GetConfig(cli *http.Client) (*sdkaws.Config, errors.Error) { +func (c *awsModel) GetConfig(ctx context.Context, cli *http.Client) (*sdkaws.Config, errors.Error) { var ( cfg sdkaws.Config err error ) - if cfg, err = sdkcfg.LoadDefaultConfig(); err != nil { + if cfg, err = sdkcfg.LoadDefaultConfig(ctx); err != nil { return nil, ErrorConfigLoader.ErrorParent(err) } diff --git a/aws/configAws/models.go b/aws/configAws/models.go index 9c25058..091d7e1 100644 --- a/aws/configAws/models.go +++ b/aws/configAws/models.go @@ -46,7 +46,7 @@ type configModel struct { type awsModel struct { configModel - retryer sdkaws.Retryer + retryer func() sdkaws.Retryer } func (c *awsModel) Validate() errors.Error { @@ -105,7 +105,7 @@ func (c *awsModel) IsHTTPs() bool { return true } -func (c *awsModel) SetRetryer(retryer sdkaws.Retryer) { +func (c *awsModel) SetRetryer(retryer func() sdkaws.Retryer) { c.retryer = retryer } @@ -119,7 +119,7 @@ func (c awsModel) Check(ctx context.Context) errors.Error { e errors.Error ) - if cfg, e = c.GetConfig(nil); e != nil { + if cfg, e = c.GetConfig(ctx, nil); e != nil { return e } diff --git a/aws/configCustom/interface.go b/aws/configCustom/interface.go index bb9aa70..433b3e9 100644 --- a/aws/configCustom/interface.go +++ b/aws/configCustom/interface.go @@ -26,6 +26,7 @@ package configCustom import ( + "context" "encoding/json" "net/http" "net/url" @@ -90,7 +91,7 @@ func (c *awsModel) Clone() libaws.Config { } } -func (c *awsModel) GetConfig(cli *http.Client) (*sdkaws.Config, errors.Error) { +func (c *awsModel) GetConfig(ctx context.Context, cli *http.Client) (*sdkaws.Config, errors.Error) { cfg := sdkaws.NewConfig() diff --git a/aws/configCustom/models.go b/aws/configCustom/models.go index 5cc54ef..a463a8c 100644 --- a/aws/configCustom/models.go +++ b/aws/configCustom/models.go @@ -40,61 +40,57 @@ import ( ) type Model struct { - Region string `mapstructure:"region" json:"region" yaml:"region" toml:"region" validate:"printascii,required"` - Endpoint string `mapstructure:"endpoint" json:"endpoint" yaml:"endpoint" toml:"endpoint" validate:"url,required"` - AccessKey string `mapstructure:"accesskey" json:"accesskey" yaml:"accesskey" toml:"accesskey" validate:"printascii,required"` - SecretKey string `mapstructure:"secretkey" json:"secretkey" yaml:"secretkey" toml:"secretkey" validate:"printascii,required"` - Bucket string `mapstructure:"bucket" json:"bucket" yaml:"bucket" toml:"bucket" validate:"printascii,omitempty"` + Region string `mapstructure:"region" json:"region" yaml:"region" toml:"region" validate:"required,hostname"` + Endpoint string `mapstructure:"endpoint" json:"endpoint" yaml:"endpoint" toml:"endpoint" validate:"required,url"` + AccessKey string `mapstructure:"accesskey" json:"accesskey" yaml:"accesskey" toml:"accesskey" validate:"required,printascii"` + SecretKey string `mapstructure:"secretkey" json:"secretkey" yaml:"secretkey" toml:"secretkey" validate:"required,printascii"` + Bucket string `mapstructure:"bucket" json:"bucket" yaml:"bucket" toml:"bucket" validate:"omitempty,hostname"` } type awsModel struct { Model - retryer sdkaws.Retryer + retryer func() sdkaws.Retryer endpoint *url.URL mapRegion map[string]*url.URL } func (c *awsModel) Validate() errors.Error { - val := libval.New() - err := val.Struct(c) + err := ErrorConfigValidator.Error(nil) - if err != nil { - if e, ok := err.(*libval.InvalidValidationError); ok { - return ErrorConfigValidator.ErrorParent(e) + if er := libval.New().Struct(c.Model); er != nil { + if e, ok := er.(*libval.InvalidValidationError); ok { + err.AddParent(e) } - out := ErrorConfigValidator.Error(nil) - - for _, e := range err.(libval.ValidationErrors) { + for _, e := range er.(libval.ValidationErrors) { //nolint goerr113 - out.AddParent(fmt.Errorf("config field '%s' is not validated by constraint '%s'", e.Field(), e.ActualTag())) - } - - if out.HasParent() { - return out + err.AddParent(fmt.Errorf("config field '%s' is not validated by constraint '%s'", e.Field(), e.ActualTag())) } } if c.Endpoint != "" && c.endpoint == nil { - if c.endpoint, err = url.Parse(c.Endpoint); err != nil { - return ErrorEndpointInvalid.ErrorParent(err) + var e error + if c.endpoint, e = url.Parse(c.Endpoint); e != nil { + err.AddParent(e) + } else if e := c.RegisterRegionAws(c.endpoint); e != nil { + err.AddParentError(e) } - - if e := c.RegisterRegionAws(c.endpoint); e != nil { - return e - } - } else if c.endpoint != nil && c.Endpoint == "" { + } else if !err.HasParent() && c.endpoint != nil && c.Endpoint == "" { c.Endpoint = c.endpoint.String() } - if c.endpoint != nil && c.Region != "" { + if !err.HasParent() && c.endpoint != nil && c.Region != "" { if e := c.RegisterRegionEndpoint("", c.endpoint); e != nil { - return e + err.AddParentError(e) } } - return nil + if !err.HasParent() { + err = nil + } + + return err } func (c *awsModel) ResetRegionEndpoint() { @@ -230,7 +226,7 @@ func (c *awsModel) ResolveEndpoint(service, region string) (sdkaws.Endpoint, err }, nil } - logger.DebugLevel.Logf("Called ResolveEndpoint for service '%s' / region '%s' with nil endpoint", service, region) + logger.DebugLevel.Logf("Called ResolveEndpoint for service / region '%s' with nil endpoint", service, region) return sdkaws.Endpoint{}, ErrorEndpointInvalid.Error(nil) } @@ -238,7 +234,7 @@ func (c *awsModel) IsHTTPs() bool { return strings.HasSuffix(strings.ToLower(c.endpoint.Scheme), "s") } -func (c *awsModel) SetRetryer(retryer sdkaws.Retryer) { +func (c *awsModel) SetRetryer(retryer func() sdkaws.Retryer) { c.retryer = retryer } @@ -250,7 +246,7 @@ func (c awsModel) Check(ctx context.Context) errors.Error { e errors.Error ) - if cfg, e = c.GetConfig(nil); e != nil { + if cfg, e = c.GetConfig(ctx, nil); e != nil { return e } diff --git a/aws/group/interface.go b/aws/group/interface.go index 2e49e65..9833174 100644 --- a/aws/group/interface.go +++ b/aws/group/interface.go @@ -55,9 +55,9 @@ type Group interface { PolicyDetach(groupName, polArn string) errors.Error } -func New(ctx context.Context, bucket string, iam *iam.Client, s3 *s3.Client) Group { +func New(ctx context.Context, bucket, region string, iam *iam.Client, s3 *s3.Client) Group { return &client{ - Helper: helper.New(ctx, bucket), + Helper: helper.New(ctx, bucket, region), iam: iam, s3: s3, } diff --git a/aws/helper/interface.go b/aws/helper/interface.go index 74e317c..2052e0c 100644 --- a/aws/helper/interface.go +++ b/aws/helper/interface.go @@ -42,12 +42,14 @@ const ( type Helper struct { ctx context.Context bkt string + reg string } -func New(ctx context.Context, bucket string) Helper { +func New(ctx context.Context, bucket, region string) Helper { return Helper{ ctx: ctx, bkt: bucket, + reg: region, } } @@ -103,6 +105,14 @@ func (c *Helper) Close(req *http.Request, rsp *http.Response) { } } +func (c *Helper) GetRegion() string { + return c.reg +} + +func (c *Helper) GetRegionAws() *string { + return aws.String(c.reg) +} + func (c *Helper) GetBucketName() string { return c.bkt } diff --git a/aws/interface.go b/aws/interface.go index be4e194..b827820 100644 --- a/aws/interface.go +++ b/aws/interface.go @@ -58,9 +58,9 @@ type Config interface { IsHTTPs() bool ResolveEndpoint(service, region string) (sdkaws.Endpoint, error) - SetRetryer(retryer sdkaws.Retryer) + SetRetryer(retryer func() sdkaws.Retryer) - GetConfig(cli *http.Client) (*sdkaws.Config, errors.Error) + GetConfig(ctx context.Context, cli *http.Client) (*sdkaws.Config, errors.Error) JSON() ([]byte, error) Clone() Config @@ -76,9 +76,9 @@ type AWS interface { Role() role.Role User() user.User - Clone() (AWS, errors.Error) + Clone(ctx context.Context) (AWS, errors.Error) Config() Config - ForcePathStyle(enabled bool) errors.Error + ForcePathStyle(ctx context.Context, enabled bool) errors.Error GetBucketName() string SetBucketName(bucket string) @@ -111,13 +111,13 @@ func New(ctx context.Context, cfg Config, httpClient *http.Client) (AWS, errors. h: httpClient, } - if i, e := cli.newClientIAM(httpClient); e != nil { + if i, e := cli.newClientIAM(ctx, httpClient); e != nil { return nil, e } else { cli.i = i } - if s, e := cli.newClientS3(httpClient); e != nil { + if s, e := cli.newClientS3(ctx, httpClient); e != nil { return nil, e } else { cli.s = s @@ -126,62 +126,72 @@ func New(ctx context.Context, cfg Config, httpClient *http.Client) (AWS, errors. return cli, nil } -func (cli *client) newClientIAM(httpClient *http.Client) (*sdkiam.Client, errors.Error) { +func (cli *client) newClientIAM(ctx context.Context, httpClient *http.Client) (*sdkiam.Client, errors.Error) { var ( c *sdkaws.Config i *sdkiam.Client e errors.Error + r sdkaws.Retryer ) if httpClient == nil { httpClient = cli.h } - if c, e = cli.c.GetConfig(httpClient); e != nil { + if c, e = cli.c.GetConfig(ctx, httpClient); e != nil { return nil, e } + if c.Retryer != nil { + r = c.Retryer() + } + i = sdkiam.New(sdkiam.Options{ APIOptions: c.APIOptions, Credentials: c.Credentials, EndpointOptions: sdkiam.EndpointResolverOptions{ DisableHTTPS: !cli.c.IsHTTPs(), }, - EndpointResolver: sdkiam.WithEndpointResolver(c.EndpointResolver, nil), + EndpointResolver: cli.newIAMResolver(c), HTTPSignerV4: sdksv4.NewSigner(), Region: c.Region, - Retryer: c.Retryer, + Retryer: r, HTTPClient: httpClient, }) return i, nil } -func (cli *client) newClientS3(httpClient *http.Client) (*sdksss.Client, errors.Error) { +func (cli *client) newClientS3(ctx context.Context, httpClient *http.Client) (*sdksss.Client, errors.Error) { var ( c *sdkaws.Config s *sdksss.Client e errors.Error + r sdkaws.Retryer ) if httpClient == nil { httpClient = cli.h } - if c, e = cli.c.GetConfig(httpClient); e != nil { + if c, e = cli.c.GetConfig(ctx, httpClient); e != nil { return nil, e } + if c.Retryer != nil { + r = c.Retryer() + } + s = sdksss.New(sdksss.Options{ APIOptions: c.APIOptions, Credentials: c.Credentials, EndpointOptions: sdksss.EndpointResolverOptions{ DisableHTTPS: !cli.c.IsHTTPs(), }, - EndpointResolver: sdksss.WithEndpointResolver(c.EndpointResolver, nil), + EndpointResolver: cli.newS3Resolver(c), HTTPSignerV4: sdksv4.NewSigner(), Region: c.Region, - Retryer: c.Retryer, + Retryer: r, HTTPClient: httpClient, UsePathStyle: cli.p, }) @@ -189,7 +199,7 @@ func (cli *client) newClientS3(httpClient *http.Client) (*sdksss.Client, errors. return s, nil } -func (c *client) Clone() (AWS, errors.Error) { +func (c *client) Clone(ctx context.Context) (AWS, errors.Error) { cli := &client{ p: false, x: c.x, @@ -199,13 +209,13 @@ func (c *client) Clone() (AWS, errors.Error) { h: c.h, } - if i, e := cli.newClientIAM(c.h); e != nil { + if i, e := cli.newClientIAM(ctx, c.h); e != nil { return nil, e } else { cli.i = i } - if s, e := cli.newClientS3(c.h); e != nil { + if s, e := cli.newClientS3(ctx, c.h); e != nil { return nil, e } else { cli.s = s diff --git a/aws/model.go b/aws/model.go index 34733c3..c2b0cc6 100644 --- a/aws/model.go +++ b/aws/model.go @@ -26,6 +26,8 @@ package aws import ( + "context" + "github.com/nabbar/golib/aws/bucket" "github.com/nabbar/golib/aws/group" "github.com/nabbar/golib/aws/object" @@ -35,10 +37,10 @@ import ( "github.com/nabbar/golib/errors" ) -func (c *client) ForcePathStyle(enabled bool) errors.Error { +func (c *client) ForcePathStyle(ctx context.Context, enabled bool) errors.Error { c.p = enabled - if s, e := c.newClientS3(nil); e != nil { + if s, e := c.newClientS3(ctx, nil); e != nil { return e } else { c.s = s @@ -52,27 +54,27 @@ func (c *client) Config() Config { } func (c *client) Bucket() bucket.Bucket { - return bucket.New(c.x, c.c.GetBucketName(), c.i, c.s) + return bucket.New(c.x, c.c.GetBucketName(), c.c.GetRegion(), c.i, c.s) } func (c *client) Group() group.Group { - return group.New(c.x, c.c.GetBucketName(), c.i, c.s) + return group.New(c.x, c.c.GetBucketName(), c.c.GetRegion(), c.i, c.s) } func (c *client) Object() object.Object { - return object.New(c.x, c.c.GetBucketName(), c.i, c.s) + return object.New(c.x, c.c.GetBucketName(), c.c.GetRegion(), c.i, c.s) } func (c *client) Policy() policy.Policy { - return policy.New(c.x, c.c.GetBucketName(), c.i, c.s) + return policy.New(c.x, c.c.GetBucketName(), c.c.GetRegion(), c.i, c.s) } func (c *client) Role() role.Role { - return role.New(c.x, c.c.GetBucketName(), c.i, c.s) + return role.New(c.x, c.c.GetBucketName(), c.c.GetRegion(), c.i, c.s) } func (c *client) User() user.User { - return user.New(c.x, c.c.GetBucketName(), c.i, c.s) + return user.New(c.x, c.c.GetBucketName(), c.c.GetRegion(), c.i, c.s) } func (c *client) GetBucketName() string { diff --git a/aws/object/interface.go b/aws/object/interface.go index 474f9d4..d5f3597 100644 --- a/aws/object/interface.go +++ b/aws/object/interface.go @@ -56,9 +56,9 @@ type Object interface { MultipartPutCustom(partSize helper.PartSize, object string, body io.Reader) errors.Error } -func New(ctx context.Context, bucket string, iam *sdkiam.Client, s3 *sdksss.Client) Object { +func New(ctx context.Context, bucket, region string, iam *sdkiam.Client, s3 *sdksss.Client) Object { return &client{ - Helper: helper.New(ctx, bucket), + Helper: helper.New(ctx, bucket, region), iam: iam, s3: s3, } diff --git a/aws/object/multipart.go b/aws/object/multipart.go index 7a1e0e8..9050f55 100644 --- a/aws/object/multipart.go +++ b/aws/object/multipart.go @@ -26,6 +26,7 @@ package object import ( + /* #nosec */ "crypto/md5" "encoding/base64" "io" @@ -98,6 +99,7 @@ func (cli *client) MultipartPutCustom(partSize libhlp.PartSize, object string, b return cli.multipartCancel(err, upl.UploadId, object) } + /* #nosec */ h := md5.New() if _, err := tmp.WriteTo(h); err != nil { return cli.multipartCancel(err, upl.UploadId, object) diff --git a/aws/policy/interface.go b/aws/policy/interface.go index 139abba..0ba353f 100644 --- a/aws/policy/interface.go +++ b/aws/policy/interface.go @@ -47,9 +47,9 @@ type Policy interface { Delete(polArn string) errors.Error } -func New(ctx context.Context, bucket string, iam *iam.Client, s3 *s3.Client) Policy { +func New(ctx context.Context, bucket, region string, iam *iam.Client, s3 *s3.Client) Policy { return &client{ - Helper: helper.New(ctx, bucket), + Helper: helper.New(ctx, bucket, region), iam: iam, s3: s3, } diff --git a/aws/resolver.go b/aws/resolver.go new file mode 100644 index 0000000..4c545ec --- /dev/null +++ b/aws/resolver.go @@ -0,0 +1,60 @@ +/* + * MIT License + * + * Copyright (c) 2021 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package aws + +import ( + sdkaws "github.com/aws/aws-sdk-go-v2/aws" + sdkiam "github.com/aws/aws-sdk-go-v2/service/iam" + sdksss "github.com/aws/aws-sdk-go-v2/service/s3" +) + +type resolverIam struct { + r func(service, region string) (sdkaws.Endpoint, error) +} + +func (r *resolverIam) ResolveEndpoint(region string, options sdkiam.EndpointResolverOptions) (sdkaws.Endpoint, error) { + return r.r("iam", region) +} + +type resolverS3 struct { + r func(service, region string) (sdkaws.Endpoint, error) +} + +func (r *resolverS3) ResolveEndpoint(region string, options sdksss.EndpointResolverOptions) (sdkaws.Endpoint, error) { + return r.r("s3", region) +} + +func (cli *client) newIAMResolver(c *sdkaws.Config) sdkiam.EndpointResolver { + return &resolverIam{ + r: c.EndpointResolver.ResolveEndpoint, + } +} + +func (cli *client) newS3Resolver(c *sdkaws.Config) sdksss.EndpointResolver { + return &resolverS3{ + r: c.EndpointResolver.ResolveEndpoint, + } +} diff --git a/aws/role/interface.go b/aws/role/interface.go index 0bdd555..dd288ec 100644 --- a/aws/role/interface.go +++ b/aws/role/interface.go @@ -53,9 +53,9 @@ type Role interface { PolicyListAttached(roleName string) ([]types.AttachedPolicy, errors.Error) } -func New(ctx context.Context, bucket string, iam *iam.Client, s3 *s3.Client) Role { +func New(ctx context.Context, bucket, region string, iam *iam.Client, s3 *s3.Client) Role { return &client{ - Helper: helper.New(ctx, bucket), + Helper: helper.New(ctx, bucket, region), iam: iam, s3: s3, } diff --git a/aws/user/interface.go b/aws/user/interface.go index f87e6ce..8b89450 100644 --- a/aws/user/interface.go +++ b/aws/user/interface.go @@ -59,9 +59,9 @@ type User interface { AccessDelete(username, accessKey string) liberr.Error } -func New(ctx context.Context, bucket string, iam *sdkiam.Client, s3 *sdksss.Client) User { +func New(ctx context.Context, bucket, region string, iam *sdkiam.Client, s3 *sdksss.Client) User { return &client{ - Helper: libhlp.New(ctx, bucket), + Helper: libhlp.New(ctx, bucket, region), iam: iam, s3: s3, } diff --git a/certificates/config.go b/certificates/config.go index 82feb12..5df5bb9 100644 --- a/certificates/config.go +++ b/certificates/config.go @@ -117,24 +117,36 @@ func (c *Config) NewFrom(cfg TLSConfig) (TLSConfig, liberr.Error) { if len(c.CipherList) > 0 { for _, a := range c.CipherList { + if len(a) < 1 { + continue + } t.cipherList = append(t.cipherList, StringToCipherKey(a)) } } if len(c.CurveList) > 0 { for _, a := range c.CurveList { + if len(a) < 1 { + continue + } t.curveList = append(t.curveList, StringToCurveID(a)) } } if len(c.RootCAString) > 0 { for _, s := range c.RootCAString { + if len(s) < 1 { + continue + } t.AddRootCAString(s) } } if len(c.RootCAFile) > 0 { for _, f := range c.RootCAFile { + if len(f) < 1 { + continue + } if e := t.AddRootCAFile(f); e != nil { return nil, e } @@ -143,12 +155,18 @@ func (c *Config) NewFrom(cfg TLSConfig) (TLSConfig, liberr.Error) { if len(c.ClientCAString) > 0 { for _, s := range c.ClientCAString { + if len(s) < 1 { + continue + } t.AddClientCAString(s) } } if len(c.ClientCAFiles) > 0 { for _, f := range c.ClientCAFiles { + if len(f) < 1 { + continue + } if e := t.AddClientCAFile(f); e != nil { return nil, e } @@ -157,6 +175,9 @@ func (c *Config) NewFrom(cfg TLSConfig) (TLSConfig, liberr.Error) { if len(c.CertPairString) > 0 { for _, s := range c.CertPairString { + if len(s.Key) < 1 || len(s.Pem) < 1 { + continue + } if e := t.AddCertificatePairString(s.Key, s.Pem); e != nil { return nil, e } @@ -165,6 +186,9 @@ func (c *Config) NewFrom(cfg TLSConfig) (TLSConfig, liberr.Error) { if len(c.CertPairFile) > 0 { for _, f := range c.CertPairFile { + if len(f.Key) < 1 || len(f.Pem) < 1 { + continue + } if e := t.AddCertificatePairFile(f.Key, f.Pem); e != nil { return nil, e } diff --git a/certificates/model.go b/certificates/model.go index 92ca9af..1bfd26a 100644 --- a/certificates/model.go +++ b/certificates/model.go @@ -59,9 +59,12 @@ func (c *config) checkFile(pemFiles ...string) liberr.Error { if f == "" { return ErrorParamsEmpty.Error(nil) } + if _, e := os.Stat(f); e != nil { return ErrorFileStat.ErrorParent(e) } + + /* #nosec */ b, e := ioutil.ReadFile(f) if e != nil { return ErrorFileRead.ErrorParent(e) @@ -188,6 +191,7 @@ func (c *config) AddCertificatePairFile(keyFile, crtFile string) liberr.Error { } func (c *config) TlsConfig(serverName string) *tls.Config { + /* #nosec */ cnf := &tls.Config{ InsecureSkipVerify: false, } diff --git a/certificates/tools.go b/certificates/tools.go index c093a8f..d5cb50d 100644 --- a/certificates/tools.go +++ b/certificates/tools.go @@ -1,3 +1,29 @@ +/* + * MIT License + * + * Copyright (c) 2020 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + package certificates import ( diff --git a/context/ginTonic.go b/context/ginTonic.go index f0a97cb..0100321 100644 --- a/context/ginTonic.go +++ b/context/ginTonic.go @@ -30,6 +30,8 @@ import ( "os/signal" "time" + "github.com/nabbar/golib/logger" + "github.com/gin-gonic/gin" ) @@ -58,7 +60,7 @@ type GinTonic interface { } type ctxGinTonic struct { - gin.Context + g *gin.Context x context.Context c context.CancelFunc } @@ -87,22 +89,24 @@ func NewGinTonic(c *gin.Context) GinTonic { } return &ctxGinTonic{ - *c.Copy(), + c, x, l, } } func (c *ctxGinTonic) CancelOnSignal(s ...os.Signal) { - sc := make(chan os.Signal, 1) - signal.Notify(sc, s...) - go func() { + sc := make(chan os.Signal, 1) + signal.Notify(sc, s...) + select { case <-sc: + logger.InfoLevel.Logf("Os Signal recieved, calling context cancel !") c.c() return case <-c.Done(): + logger.InfoLevel.Logf("Context has been closed...") return } }() @@ -121,9 +125,65 @@ func (c *ctxGinTonic) Err() error { } func (c *ctxGinTonic) Value(key interface{}) interface{} { - return c.Context.Value(key) + return c.g.Value(key) } func (c *ctxGinTonic) GinContext() *gin.Context { - return &c.Context + return c.g +} + +func (c *ctxGinTonic) Set(key string, value interface{}) { + c.g.Set(key, value) +} + +func (c *ctxGinTonic) Get(key string) (value interface{}, exists bool) { + return c.g.Get(key) +} + +func (c *ctxGinTonic) MustGet(key string) interface{} { + return c.g.MustGet(key) +} + +func (c *ctxGinTonic) GetString(key string) (s string) { + return c.g.GetString(key) +} + +func (c *ctxGinTonic) GetBool(key string) (b bool) { + return c.g.GetBool(key) +} + +func (c *ctxGinTonic) GetInt(key string) (i int) { + return c.g.GetInt(key) +} + +func (c *ctxGinTonic) GetInt64(key string) (i64 int64) { + return c.g.GetInt64(key) +} + +func (c *ctxGinTonic) GetFloat64(key string) (f64 float64) { + return c.g.GetFloat64(key) +} + +func (c *ctxGinTonic) GetTime(key string) (t time.Time) { + return c.g.GetTime(key) +} + +func (c *ctxGinTonic) GetDuration(key string) (d time.Duration) { + return c.g.GetDuration(key) +} + +func (c *ctxGinTonic) GetStringSlice(key string) (ss []string) { + return c.g.GetStringSlice(key) +} + +func (c *ctxGinTonic) GetStringMap(key string) (sm map[string]interface{}) { + return c.g.GetStringMap(key) +} + +func (c *ctxGinTonic) GetStringMapString(key string) (sms map[string]string) { + return c.g.GetStringMapString(key) +} + +func (c *ctxGinTonic) GetStringMapStringSlice(key string) (smss map[string][]string) { + return c.g.GetStringMapStringSlice(key) } diff --git a/errors/code.go b/errors/code.go index cfbf0b1..38cfd59 100644 --- a/errors/code.go +++ b/errors/code.go @@ -38,6 +38,7 @@ type CodeError uint16 const UNK_ERROR CodeError = 0 const UNK_MESSAGE = "unknown error" +const NUL_MESSAGE = "" func (c CodeError) GetUint16() uint16 { return uint16(c) @@ -94,12 +95,12 @@ func RegisterIdFctMessage(minCode CodeError, fct Message) { func ExistInMapMessage(code CodeError) bool { if f, ok := idMsgFct[findCodeErrorInMapMessage(code)]; ok { - if m := f(code); m != "" { - return false + if m := f(code); m != NUL_MESSAGE { + return true } } - return true + return false } func getMapMessageKey() []CodeError { diff --git a/errors/return.go b/errors/return.go index c335f5c..017c328 100644 --- a/errors/return.go +++ b/errors/return.go @@ -1,3 +1,29 @@ +/* + * MIT License + * + * Copyright (c) 2020 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + package errors import ( diff --git a/go.mod b/go.mod index d8cae80..efcc094 100644 --- a/go.mod +++ b/go.mod @@ -3,52 +3,66 @@ module github.com/nabbar/golib go 1.15 require ( + github.com/Masterminds/goutils v1.1.0 // indirect + github.com/Masterminds/semver v1.5.0 // indirect + github.com/Masterminds/sprig v2.22.0+incompatible // indirect + github.com/PuerkitoBio/goquery v1.6.1 // indirect github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect - github.com/aws/aws-sdk-go-v2 v0.30.0 - github.com/aws/aws-sdk-go-v2/config v0.3.0 - github.com/aws/aws-sdk-go-v2/credentials v0.1.5 - github.com/aws/aws-sdk-go-v2/service/iam v0.30.0 - github.com/aws/aws-sdk-go-v2/service/s3 v0.30.0 + github.com/andybalholm/cascadia v1.2.0 // indirect + github.com/aokoli/goutils v1.1.0 // indirect + github.com/aws/aws-sdk-go-v2 v1.0.0 + github.com/aws/aws-sdk-go-v2/config v1.0.0 + github.com/aws/aws-sdk-go-v2/credentials v1.0.0 + github.com/aws/aws-sdk-go-v2/service/iam v1.0.0 + github.com/aws/aws-sdk-go-v2/service/s3 v1.0.0 github.com/fatih/color v1.10.0 github.com/gin-gonic/gin v1.6.3 + github.com/go-asn1-ber/asn1-ber v1.5.3 // indirect github.com/go-ldap/ldap/v3 v3.2.4 - github.com/go-ole/go-ole v1.2.4 // indirect + github.com/go-ole/go-ole v1.2.5 // indirect github.com/go-playground/validator/v10 v10.4.1 - github.com/go-sql-driver/mysql v1.5.0 // indirect github.com/gobuffalo/envy v1.9.0 // indirect github.com/gobuffalo/packd v1.0.0 // indirect github.com/gobuffalo/packr v1.30.1 github.com/golang/protobuf v1.4.3 // indirect - github.com/google/go-github/v32 v32.1.0 + github.com/google/go-github/v33 v33.0.0 + github.com/google/uuid v1.2.0 // indirect github.com/hashicorp/go-retryablehttp v0.6.8 github.com/hashicorp/go-uuid v1.0.2 github.com/hashicorp/go-version v1.2.1 + github.com/huandu/xstrings v1.3.2 // indirect + github.com/imdario/mergo v0.3.11 // indirect github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7 - github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/json-iterator/go v1.1.10 // indirect - github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect - github.com/mattn/go-colorable v0.1.8 // indirect + github.com/leodido/go-urn v1.2.1 // indirect + github.com/matcornic/hermes/v2 v2.1.0 + github.com/mattn/go-runewidth v0.0.10 // indirect + github.com/mitchellh/copystructure v1.0.0 // indirect + github.com/mitchellh/reflectwalk v1.0.1 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.1 // indirect github.com/olekukonko/tablewriter v0.0.4 - github.com/onsi/ginkgo v1.14.1 - github.com/onsi/gomega v1.10.2 - github.com/rogpeppe/go-internal v1.6.2 // indirect - github.com/shirou/gopsutil v3.20.11+incompatible + github.com/onsi/ginkgo v1.14.2 + github.com/onsi/gomega v1.10.4 + github.com/rivo/uniseg v0.2.0 // indirect + github.com/rogpeppe/go-internal v1.7.0 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/shirou/gopsutil v3.20.12+incompatible github.com/sirupsen/logrus v1.7.0 github.com/spf13/jwalterweatherman v1.1.0 github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect - github.com/stretchr/testify v1.5.1 // indirect - github.com/ugorji/go v1.2.1 // indirect - github.com/vbauerster/mpb/v5 v5.3.0 - github.com/xanzy/go-gitlab v0.40.1 - golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9 - golang.org/x/net v0.0.0-20201209123823-ac852fbbde11 - golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5 + github.com/ugorji/go v1.2.3 // indirect + github.com/vanng822/go-premailer v1.9.0 // indirect + github.com/vbauerster/mpb/v5 v5.4.0 + github.com/xanzy/go-gitlab v0.42.0 + github.com/xhit/go-simple-mail/v2 v2.7.0 + golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad + golang.org/x/net v0.0.0-20210119194325-5f4716e94777 + golang.org/x/oauth2 v0.0.0-20210113205817-d3ed898aa8a3 golang.org/x/sync v0.0.0-20201207232520-09787c993a3a - golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d // indirect - golang.org/x/term v0.0.0-20201207232118-ee85cb95a76b // indirect - golang.org/x/text v0.3.4 // indirect + golang.org/x/sys v0.0.0-20210123231150-1d476976d117 // indirect + golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect + golang.org/x/text v0.3.5 // indirect golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect google.golang.org/appengine v1.6.7 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/httpserver/config.go b/httpserver/config.go index 2c2b0a5..c242c2f 100644 --- a/httpserver/config.go +++ b/httpserver/config.go @@ -1,3 +1,29 @@ +/* + * MIT License + * + * Copyright (c) 2020 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + package httpserver import ( diff --git a/httpserver/errors.go b/httpserver/errors.go index aa43e48..73fb0f4 100644 --- a/httpserver/errors.go +++ b/httpserver/errors.go @@ -35,6 +35,7 @@ const ( ErrorPoolValidate ErrorPoolListen ErrorServerValidate + ErrorPortUse ) var isCodeError = false @@ -64,6 +65,8 @@ func getMessage(code errors.CodeError) (message string) { return "at least one server has listen error" case ErrorServerValidate: return "config server seems to be not valid" + case ErrorPortUse: + return "server port is still used" } return "" diff --git a/httpserver/http.go b/httpserver/http.go deleted file mode 100644 index 94381e1..0000000 --- a/httpserver/http.go +++ /dev/null @@ -1,75 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 Nicolas JUHEL - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package httpserver - -import ( - "sync" -) - -func ListenWaitNotify(allSrv ...HTTPServer) { - var wg sync.WaitGroup - wg.Add(len(allSrv)) - - for _, s := range allSrv { - go func(serv HTTPServer) { - defer wg.Done() - serv.Listen() - serv.WaitNotify() - }(s) - } - - wg.Wait() -} - -func Listen(allSrv ...HTTPServer) { - for _, s := range allSrv { - go func(serv HTTPServer) { - serv.Listen() - }(s) - } -} - -func Restart(allSrv ...HTTPServer) { - for _, s := range allSrv { - s.Restart() - } -} - -func Shutdown(allSrv ...HTTPServer) { - for _, s := range allSrv { - s.Shutdown() - } -} - -func IsRunning(allSrv ...HTTPServer) bool { - for _, s := range allSrv { - if s.IsRunning() { - return true - } - } - - return false -} diff --git a/httpserver/pool.go b/httpserver/pool.go index 700379a..ff9da06 100644 --- a/httpserver/pool.go +++ b/httpserver/pool.go @@ -35,6 +35,8 @@ import ( "strings" "syscall" + "github.com/nabbar/golib/logger" + "github.com/nabbar/golib/semaphore" liberr "github.com/nabbar/golib/errors" @@ -109,24 +111,21 @@ func (p pool) Add(srv ...Server) (PoolServer, liberr.Error) { for _, s := range srv { if !r.Has(s.GetBindable()) { r = append(r, s) + continue } - if x := r.Get(s.GetBindable()); x != nil { - s.Merge(x) - - if x.IsRunning() { - x.Shutdown() - if e := s.Listen(nil); e != nil { - return r, e - } + for _, x := range r { + if x.GetBindable() != s.GetBindable() { + continue + } else if !x.Merge(s) { + r = r.Del(s.GetBindable()).(pool) + r = append(r, s) + break } } - - r = r.Del(s.GetBindable()).(pool) - r = append(r, s) } - return append(p, srv...), nil + return r, nil } func (p pool) Get(bindAddress string) Server { @@ -150,10 +149,6 @@ func (p pool) Del(bindAddress string) PoolServer { var r = make(pool, 0) - if p != nil { - r = p - } - for _, s := range p { if s.GetBindable() != bindAddress { r = append(r, s) @@ -336,31 +331,14 @@ func (p pool) Listen(handler http.Handler) liberr.Error { return nil } - var ( - e liberr.Error - s semaphore.Sem - x context.Context - c context.CancelFunc - ) - - defer func() { - c() - s.DeferMain() - }() + var e liberr.Error e = ErrorPoolListen.Error(nil) - x, c = context.WithTimeout(context.Background(), timeoutRestart) - s = semaphore.NewSemaphoreWithContext(x, 0) - + logger.InfoLevel.Log("Calling listen for All Servers") p.MapRun(func(srv Server) { - _ = s.NewWorker() - go func() { - defer s.DeferWorker() - e.AddParentError(srv.Listen(handler)) - }() + e.AddParentError(srv.Listen(handler)) }) - - _ = s.WaitAll() + logger.InfoLevel.Log("End of Calling listen for All Servers") if !e.HasParent() { e = nil diff --git a/httpserver/server.go b/httpserver/server.go index fad8025..3099fe8 100644 --- a/httpserver/server.go +++ b/httpserver/server.go @@ -1,3 +1,29 @@ +/* + * MIT License + * + * Copyright (c) 2020 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + package httpserver import ( @@ -27,6 +53,7 @@ type server struct { run atomic.Value cfg *ServerConfig srv *http.Server + cnl context.CancelFunc } type Server interface { @@ -51,6 +78,7 @@ func NewServer(cfg *ServerConfig) Server { return &server{ cfg: cfg, srv: nil, + cnl: nil, } } @@ -79,7 +107,13 @@ func (s *server) GetExpose() string { } func (s *server) IsRunning() bool { - return s.run.Load().(bool) + if i := s.run.Load(); i == nil { + return false + } else if b, ok := i.(bool); !ok { + return false + } else { + return b + } } func (s *server) IsTLS() bool { @@ -169,10 +203,19 @@ func (s *server) Listen(handler http.Handler) liberr.Error { s.Shutdown() } + for i := 0; i < 5; i++ { + if e := s.PortInUse(); e != nil { + s.Shutdown() + } else { + break + } + } + s.srv = srv go func() { ctx, cnl := context.WithCancel(s.cfg.getContext()) + s.cnl = cnl defer func() { cnl() @@ -197,13 +240,16 @@ func (s *server) Listen(handler http.Handler) liberr.Error { err = s.srv.ListenAndServe() } - if !errors.Is(err, ctx.Err()) { + if err != nil && ctx.Err() != nil && ctx.Err().Error() == err.Error() { + return + } else if err != nil && errors.Is(err, http.ErrServerClosed) { + return + } else if err != nil { liblog.ErrorLevel.LogErrorCtxf(liblog.NilLevel, "Listen Server '%s'", err, s.GetName()) } }() return nil - } func (s *server) WaitNotify() { @@ -230,21 +276,61 @@ func (s *server) Shutdown() { ctx, cancel := context.WithTimeout(context.Background(), timeoutShutdown) defer func() { cancel() + + if s.srv != nil { + _ = s.srv.Close() + } + s.setNotRunning() }() liblog.InfoLevel.Logf("Shutdown Server '%s'...", s.GetName()) - if err := s.srv.Shutdown(ctx); err != nil { - liblog.ErrorLevel.Logf("Shutdown Server '%s' Error: %v", s.GetName(), err) + if s.cnl != nil { + s.cnl() + } + + if s.srv != nil { + err := s.srv.Shutdown(ctx) + if err != nil && !errors.Is(err, http.ErrServerClosed) { + liblog.ErrorLevel.Logf("Shutdown Server '%s' Error: %v", s.GetName(), err) + } } } func (s *server) Merge(srv Server) bool { if x, ok := srv.(*server); ok { - s.srv = x.srv + s.cfg = x.cfg return true } return false } + +func (s *server) PortInUse() liberr.Error { + var ( + dia = net.Dialer{} + con net.Conn + err error + ctx context.Context + cnl context.CancelFunc + ) + + defer func() { + if cnl != nil { + cnl() + } + if con != nil { + _ = con.Close() + } + }() + + ctx, cnl = context.WithTimeout(context.TODO(), 2*time.Second) + con, err = dia.DialContext(ctx, "tcp", s.cfg.Listen) + + if err != nil { + return nil + } + + return ErrorPortUse.Error(nil) +} diff --git a/ldap/ldap.go b/ldap/ldap.go index 732148f..98a7f77 100644 --- a/ldap/ldap.go +++ b/ldap/ldap.go @@ -174,6 +174,10 @@ func (lc *HelperLDAP) starttls(l *ldap.Conn) liberr.Error { } func (lc *HelperLDAP) tryConnect() (TLSMode, liberr.Error) { + if lc == nil { + return TLSMODE_NONE, ErrorEmptyParams.Error(nil) + } + var ( l *ldap.Conn err liberr.Error @@ -217,7 +221,7 @@ func (lc *HelperLDAP) tryConnect() (TLSMode, liberr.Error) { } func (lc *HelperLDAP) connect() liberr.Error { - if lc.ctx == nil { + if lc == nil || lc.ctx == nil { return ErrorLDAPContext.Error(ErrorEmptyParams.Error(nil)) } @@ -280,6 +284,10 @@ func (lc *HelperLDAP) connect() liberr.Error { //Check used to check if connection success (without any bind). func (lc *HelperLDAP) Check() liberr.Error { + if lc == nil { + return ErrorEmptyParams.Error(nil) + } + if lc.conn == nil { defer func() { if lc.conn != nil { @@ -299,6 +307,10 @@ func (lc *HelperLDAP) Check() liberr.Error { //Close used to close connection object. func (lc *HelperLDAP) Close() { + if lc == nil { + return + } + if lc.conn != nil { lc.conn.Close() lc.conn = nil @@ -307,6 +319,9 @@ func (lc *HelperLDAP) Close() { //AuthUser used to test bind given user uid and password. func (lc *HelperLDAP) AuthUser(username, password string) liberr.Error { + if lc == nil { + return ErrorEmptyParams.Error(nil) + } if err := lc.connect(); err != nil { return err @@ -323,6 +338,10 @@ func (lc *HelperLDAP) AuthUser(username, password string) liberr.Error { //Connect used to connect and bind to server. func (lc *HelperLDAP) Connect() liberr.Error { + if lc == nil { + return ErrorEmptyParams.Error(nil) + } + if err := lc.AuthUser(lc.bindDN, lc.bindPass); err != nil { return err } @@ -399,7 +418,48 @@ func (lc *HelperLDAP) UserInfo(username string) (map[string]string, liberr.Error userRes = make(map[string]string) attributes := append(lc.Attributes, "cn") - src, err = lc.runSearch(fmt.Sprintf(lc.config.FilterUser, username), attributes) + src, err = lc.runSearch(fmt.Sprintf(lc.config.FilterUser, userFieldUid, username), attributes) + + if err != nil { + return userRes, err + } + + if len(src.Entries) != 1 { + if len(src.Entries) > 1 { + return userRes, ErrorLDAPUserNotUniq.Error(nil) + } else { + return userRes, ErrorLDAPUserNotFound.Error(nil) + } + } + + for _, attr := range attributes { + userRes[attr] = src.Entries[0].GetAttributeValue(attr) + } + + if _, ok := userRes["DN"]; !ok { + userRes["DN"] = src.Entries[0].DN + } + + liblog.DebugLevel.Logf("Map info retrieve in ldap server '%s' with tls mode '%s' about user [%s] : %v", lc.config.ServerAddr(lc.tlsMode == TLSMODE_TLS), lc.tlsMode.String(), username, userRes) + return userRes, nil +} + +//UserInfo used to retrieve the information of a given username. +func (lc *HelperLDAP) UserInfoByField(username string, fieldOfUnicValue string) (map[string]string, liberr.Error) { + var ( + err liberr.Error + src *ldap.SearchResult + userRes map[string]string + ) + + if username, err = lc.getUserName(username); err != nil { + return nil, err + } + + userRes = make(map[string]string) + attributes := append(lc.Attributes, "cn") + + src, err = lc.runSearch(fmt.Sprintf(lc.config.FilterUser, fieldOfUnicValue, username), attributes) if err != nil { return userRes, err @@ -433,7 +493,35 @@ func (lc *HelperLDAP) GroupInfo(groupname string) (map[string]interface{}, liber grpInfo map[string]interface{} ) - src, err = lc.runSearch(fmt.Sprintf(lc.config.FilterGroup, groupname), []string{}) + src, err = lc.runSearch(fmt.Sprintf(lc.config.FilterGroup, groupFieldCN, groupname), []string{}) + if err != nil { + return grpInfo, err + } + + if len(src.Entries) == 0 { + return nil, ErrorLDAPGroupNotFound.Error(nil) + } + + grpInfo = make(map[string]interface{}, len(src.Entries[0].Attributes)) + for _, entry := range src.Entries { + for _, entryAttribute := range entry.Attributes { + grpInfo[entryAttribute.Name] = entryAttribute.Values + } + } + + liblog.DebugLevel.Logf("Info for group [%s] find on server '%s' with tls mode '%s' : %v", groupname, lc.config.ServerAddr(lc.tlsMode == TLSMODE_TLS), lc.tlsMode.String(), grpInfo) + return grpInfo, nil +} + +//GroupInfo used to retrieve the information of a given group cn. +func (lc *HelperLDAP) GroupInfoByField(groupname string, fieldForUnicValue string) (map[string]interface{}, liberr.Error) { + var ( + err liberr.Error + src *ldap.SearchResult + grpInfo map[string]interface{} + ) + + src, err = lc.runSearch(fmt.Sprintf(lc.config.FilterGroup, fieldForUnicValue, groupname), []string{}) if err != nil { return grpInfo, err } @@ -467,7 +555,7 @@ func (lc *HelperLDAP) UserMemberOf(username string) ([]string, liberr.Error) { grp = make([]string, 0) - src, err = lc.runSearch(fmt.Sprintf(lc.config.FilterUser, username), []string{"memberOf"}) + src, err = lc.runSearch(fmt.Sprintf(lc.config.FilterUser, userFieldUid, username), []string{"memberOf"}) if err != nil { return grp, err } @@ -518,7 +606,7 @@ func (lc *HelperLDAP) UsersOfGroup(groupname string) ([]string, liberr.Error) { grp = make([]string, 0) - src, err = lc.runSearch(fmt.Sprintf(lc.config.FilterGroup, groupname), []string{"member"}) + src, err = lc.runSearch(fmt.Sprintf(lc.config.FilterGroup, groupFieldCN, groupname), []string{"member"}) if err != nil { return grp, err } diff --git a/ldap/model.go b/ldap/model.go index eee1781..8363323 100644 --- a/ldap/model.go +++ b/ldap/model.go @@ -44,6 +44,11 @@ const ( TLSMODE_STARTTLS ) +const ( + groupFieldCN = "cn" + userFieldUid = "uid" +) + func (m TLSMode) String() string { switch m { case TLSMODE_STARTTLS: @@ -64,12 +69,14 @@ func GetDefaultAttributes() []string { } type Config struct { - Uri string `cloud:"uri" mapstructure:"uri" json:"uri" yaml:"uri" toml:"uri" validate:"fqdn,required"` - PortLdap int `cloud:"port-ldap" mapstructure:"port-ldap" json:"port-ldap" yaml:"port-ldap" toml:"port-ldap" validate:"number,gte=0,nefield=Portldaps,required"` - Portldaps int `cloud:"port-ldaps" mapstructure:"port-ldaps" json:"port-ldaps" yaml:"port-ldaps" toml:"port-ldaps" validate:"number,nefield=Portldap,omitempty"` - Basedn string `cloud:"basedn" mapstructure:"basedn" json:"basedn" yaml:"basedn" toml:"basedn" validate:"printascii,omitempty"` + Uri string `cloud:"uri" mapstructure:"uri" json:"uri" yaml:"uri" toml:"uri" validate:"fqdn,required"` + PortLdap int `cloud:"port-ldap" mapstructure:"port-ldap" json:"port-ldap" yaml:"port-ldap" toml:"port-ldap" validate:"number,gte=0,nefield=Portldaps,required"` + Portldaps int `cloud:"port-ldaps" mapstructure:"port-ldaps" json:"port-ldaps" yaml:"port-ldaps" toml:"port-ldaps" validate:"number,nefield=Portldap,omitempty"` + Basedn string `cloud:"basedn" mapstructure:"basedn" json:"basedn" yaml:"basedn" toml:"basedn" validate:"printascii,omitempty"` + //FilterGroup is fmt pattern like '(&(objectClass=groupOfNames)(%s=%s))' to make search of group object class FilterGroup string `cloud:"filter-group" mapstructure:"filter-group" json:"filter-group" yaml:"filter-group" toml:"filter-group" validate:"printascii,required"` - FilterUser string `cloud:"filter-user" mapstructure:"filter-user" json:"filter-user" yaml:"filter-user" toml:"filter-user" validate:"printascii,required"` + //FilterUser is a fmt pattern like '(%s=%s)' to make search of user. By default, uid field is 'uid' + FilterUser string `cloud:"filter-user" mapstructure:"filter-user" json:"filter-user" yaml:"filter-user" toml:"filter-user" validate:"printascii,required"` } func NewConfig() *Config { diff --git a/oauth/client.go b/oauth/client.go index c1572bb..23c1724 100644 --- a/oauth/client.go +++ b/oauth/client.go @@ -38,9 +38,8 @@ func NewConfigOAuth(clientID, clientSecret, endpointToken, endpointAuth, redirec ClientID: clientID, ClientSecret: clientSecret, Endpoint: oauth2.Endpoint{ - AuthURL: endpointAuth, - TokenURL: endpointToken, - AuthStyle: 0, // autodetect + AuthURL: endpointAuth, + TokenURL: endpointToken, }, RedirectURL: redirectUrl, Scopes: scopes, diff --git a/status/status.go b/status/status.go index e0885a9..94fdd0b 100644 --- a/status/status.go +++ b/status/status.go @@ -33,6 +33,7 @@ import ( "github.com/gin-gonic/gin" "github.com/nabbar/golib/router" + "github.com/nabbar/golib/semaphore" "github.com/nabbar/golib/version" ) @@ -379,18 +380,38 @@ func (p *mainPackage) Get(c *gin.Context) { make([]StatusItemResponse, 0), } - for _, pkg := range p.cpt { - pres := pkg.GetStatusResponse(c) + sem := semaphore.NewSemaphore(0) + defer func() { + if sem != nil { + sem.DeferMain() + } + }() - if res.Status == statusOK && pres.Status == statusKO && pkg.WarnIfErr { - res.Status = statusKO + for _, pkg := range p.cpt { + _ = sem.NewWorker() + + go func() { + defer sem.DeferWorker() + + pres := pkg.GetStatusResponse(c) + res.Component = append(res.Component, pres) + }() + } + + _ = sem.WaitAll() + + for _, pres := range res.Component { + if res.Status == statusOK && pres.Status == statusKO { + for _, pkg := range p.cpt { + if pkg.name == pres.Name && pkg.WarnIfErr { + res.Status = statusKO + } + } } - if pres.Status == statusKO { + if !hasError && pres.Status == statusKO { hasError = true } - - res.Component = append(res.Component, pres) } if res.Status != statusOK {