• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

fogfish / gouldian / 11407613682

18 Oct 2024 04:42PM UTC coverage: 76.343% (-0.4%) from 76.73%
11407613682

push

github

web-flow
support aws iam authorized for api gw (#59)

* support aws iam authorized for api gw

8 of 20 new or added lines in 1 file covered. (40.0%)

3 existing lines in 1 file now uncovered.

1378 of 1805 relevant lines covered (76.34%)

0.85 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.18
/server/aws/apigateway/apigateway.go
1
/*
2

3
  Copyright 2019 Dmitry Kolesnikov, All Rights Reserved
4

5
  Licensed under the Apache License, Version 2.0 (the "License");
6
  you may not use this file except in compliance with the License.
7
  You may obtain a copy of the License at
8

9
      http://www.apache.org/licenses/LICENSE-2.0
10

11
  Unless required by applicable law or agreed to in writing, software
12
  distributed under the License is distributed on an "AS IS" BASIS,
13
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
  See the License for the specific language governing permissions and
15
  limitations under the License.
16

17
*/
18

19
package apigateway
20

21
import (
22
        "context"
23
        "encoding/base64"
24
        "fmt"
25
        "io"
26
        "net/http"
27
        "strings"
28

29
        "github.com/aws/aws-lambda-go/events"
30
        µ "github.com/fogfish/gouldian/v2"
31
        ø "github.com/fogfish/gouldian/v2/output"
32
        "github.com/fogfish/logger"
33
)
34

35
// Request is events.APIGatewayProxyRequest ⟼ µ.Input
36
func Request(r *events.APIGatewayProxyRequest) *µ.Context {
1✔
37
        ctx := µ.NewContext(context.Background())
1✔
38
        req, err := http.NewRequest(r.HTTPMethod, r.Path, requestBody(r))
1✔
39
        if err != nil {
2✔
40
                return nil
1✔
41
        }
1✔
42

43
        for header, value := range r.Headers {
2✔
44
                req.Header.Set(header, value)
1✔
45
        }
1✔
46

47
        q := req.URL.Query()
1✔
48
        for key, val := range r.QueryStringParameters {
1✔
49
                q.Add(key, val)
×
50
        }
×
51
        req.URL.RawQuery = q.Encode()
1✔
52

1✔
53
        ctx.Request = req
1✔
54
        ctx.JWT = jwtFromAuthorizer(r)
1✔
55

1✔
56
        return ctx
1✔
57
}
58

59
func requestBody(r *events.APIGatewayProxyRequest) io.ReadCloser {
1✔
60
        reader := strings.NewReader(r.Body)
1✔
61

1✔
62
        if r.IsBase64Encoded {
1✔
NEW
63
                return io.NopCloser(
×
NEW
64
                        base64.NewDecoder(base64.StdEncoding, reader),
×
NEW
65
                )
×
NEW
66
        }
×
67

68
        return io.NopCloser(reader)
1✔
69
}
70

71
func jwtFromAuthorizer(r *events.APIGatewayProxyRequest) µ.Token {
1✔
72
        if r.RequestContext.Authorizer != nil {
1✔
NEW
73
                if jwt, isJwt := r.RequestContext.Authorizer["claims"]; isJwt {
×
NEW
74
                        switch tkn := jwt.(type) {
×
NEW
75
                        case map[string]interface{}:
×
NEW
76
                                return µ.NewToken(tkn)
×
77
                        }
78
                }
79

UNCOV
80
                return nil
×
81
        }
82

83
        if r.RequestContext.Identity.UserArn != "" {
1✔
NEW
84
                return µ.Token{
×
NEW
85
                        "iss":      "https://aws.amazon.com/iam",
×
NEW
86
                        "sub":      r.RequestContext.Identity.User,
×
NEW
87
                        "username": r.RequestContext.Identity.UserArn,
×
UNCOV
88
                }
×
UNCOV
89
        }
×
90

91
        return nil
1✔
92

93
}
94

95
func ServeAndCommit(commit func(), endpoints ...µ.Routable) func(events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
1✔
96
        h := Serve(endpoints...)
1✔
97

1✔
98
        return func(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
2✔
99
                ret, err := h(req)
1✔
100
                commit()
1✔
101
                return ret, err
1✔
102
        }
1✔
103
}
104

105
// Serve HTTP service
106
func Serve(endpoints ...µ.Routable) func(events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
1✔
107
        api := µ.NewRoutes(endpoints...).Endpoint()
1✔
108

1✔
109
        return func(r events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
2✔
110
                req := Request(&r)
1✔
111
                if req == nil {
2✔
112
                        failure := ø.Status.BadRequest(
1✔
113
                                ø.Error(fmt.Errorf("unknown response %s", r.Path)),
1✔
114
                        ).(*µ.Output)
1✔
115
                        return output(failure, req)
1✔
116
                }
1✔
117

118
                switch v := api(req).(type) {
1✔
119
                case *µ.Output:
1✔
120
                        return output(v, req)
1✔
121
                case µ.NoMatch:
1✔
122
                        failure := ø.Status.NotImplemented(
1✔
123
                                ø.Error(fmt.Errorf("NoMatch %s", r.Path)),
1✔
124
                        ).(*µ.Output)
1✔
125
                        return output(failure, req)
1✔
126
                default:
×
127
                        failure := ø.Status.InternalServerError(
×
128
                                ø.Error(fmt.Errorf("unknown response %s", r.Path)),
×
129
                        ).(*µ.Output)
×
130
                        return output(failure, req)
×
131
                }
132
        }
133
}
134

135
func output(out *µ.Output, req *µ.Context) (events.APIGatewayProxyResponse, error) {
1✔
136
        if out.Failure != nil && req != nil && req.Request != nil {
2✔
137
                logger.Error("%s %v", req.Request.URL, out.Failure)
1✔
138
        }
1✔
139

140
        evt := events.APIGatewayProxyResponse{
1✔
141
                Body:       out.Body,
1✔
142
                StatusCode: out.Status,
1✔
143
                Headers:    joinHead(defaultCORS(req), out.Headers),
1✔
144
        }
1✔
145
        out.Free()
1✔
146

1✔
147
        return evt, nil
1✔
148
}
149

150
func defaultCORS(req *µ.Context) map[string]string {
1✔
151
        return map[string]string{
1✔
152
                "Access-Control-Allow-Origin":  defaultOrigin(req),
1✔
153
                "Access-Control-Allow-Methods": "GET, PUT, POST, DELETE, OPTIONS",
1✔
154
                "Access-Control-Allow-Headers": "Content-Type, Authorization, Accept",
1✔
155
                "Access-Control-Max-Age":       "600",
1✔
156
        }
1✔
157
}
1✔
158

159
func defaultOrigin(req *µ.Context) string {
1✔
160
        if req == nil {
2✔
161
                return "*"
1✔
162
        }
1✔
163

164
        origin := req.Request.Header.Get("Origin")
1✔
165
        if origin != "" {
1✔
166
                return origin
×
167
        }
×
168
        return "*"
1✔
169
}
170

171
func joinHead(a map[string]string, b []struct{ Header, Value string }) map[string]string {
1✔
172
        for _, v := range b {
2✔
173
                if _, ok := a[v.Header]; !ok {
2✔
174
                        a[v.Header] = v.Value
1✔
175
                }
1✔
176
        }
177
        return a
1✔
178
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc