~bigbes/core-go

ref: ccd2a4b8755857d1bafd43b5bbb72af0915a4f80 core-go/objects/middleware.go -rw-r--r-- 2.7 KiB
ccd2a4b8 — Drew DeVault auth: handle edge case when fetching users from meta 10 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package objects

import (
	"context"
	"errors"
	"fmt"
	"net/http"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/credentials"
	"github.com/aws/aws-sdk-go-v2/service/s3"
	"github.com/aws/smithy-go/endpoints"
	"github.com/vaughan0/go-ini"

	"git.sr.ht/~sircmpwn/core-go/config"
)

var s3CtxKey = &contextKey{"s3"}

type contextKey struct {
	name string
}

func Middleware(client *s3.Client) func(next http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			ctx := Context(r.Context(), client)
			r = r.WithContext(ctx)
			next.ServeHTTP(w, r)
		})
	}
}

func Context(ctx context.Context, client *s3.Client) context.Context {
	return context.WithValue(ctx, s3CtxKey, client)
}

func ForContext(ctx context.Context) *s3.Client {
	raw, ok := ctx.Value(s3CtxKey).(*s3.Client)
	if !ok {
		panic(fmt.Errorf("Invalid S3 context"))
	}
	return raw
}

var ErrDisabled = errors.New("object storage is not enabled for this server")

type S3Resolver struct {
	conf     ini.File
	resolver s3.EndpointResolverV2
}

func (r *S3Resolver) ResolveEndpoint(
	ctx context.Context,
	params s3.EndpointParameters,
) (transport.Endpoint, error) {
	upstream, _ := r.conf.Get("objects", "s3-upstream")
	endpoint, err := r.resolver.ResolveEndpoint(ctx, params)
	if err != nil {
		return endpoint, err
	}
	endpoint.URI.Host = upstream
	if params.Bucket != nil {
		endpoint.URI.Path = *params.Bucket
	}
	return endpoint, nil
}

func NewClient(conf ini.File) (*s3.Client, error) {
	upstream, _ := conf.Get("objects", "s3-upstream")
	accessKey, _ := conf.Get("objects", "s3-access-key")
	secretKey, _ := conf.Get("objects", "s3-secret-key")
	if upstream == "" || accessKey == "" || secretKey == "" {
		return nil, ErrDisabled
	}
	region, ok := conf.Get("objects", "s3-region")
	if !ok {
		region = "default"
	}

	scheme := "https://"
	if config.GetBool(conf, "objects", "s3-insecure", false) {
		scheme = "http://"
	}

	creds := credentials.NewStaticCredentialsProvider(accessKey, secretKey, "")
	return s3.NewFromConfig(aws.Config{
		Region:      region,
		Credentials: creds,
	}, func(opts *s3.Options) {
		opts.BaseEndpoint = aws.String(scheme + upstream)
		opts.EndpointResolverV2 = &S3Resolver{
			conf,
			s3.NewDefaultEndpointResolverV2(),
		}
	}), nil
}

func URL(conf ini.File) string {
	upstream, _ := conf.Get("objects", "s3-upstream")
	if upstream == "" {
		return ""
	}
	return "https://" + upstream
}

func Enabled(conf ini.File) bool {
	upstream, _ := conf.Get("objects", "s3-upstream")
	accessKey, _ := conf.Get("objects", "s3-access-key")
	secretKey, _ := conf.Get("objects", "s3-secret-key")
	return upstream != "" && accessKey != "" && secretKey != ""
}