/
main.go
197 lines (179 loc) · 5.97 KB
/
main.go
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
// The goapp command implements a simple App Engine app to demonstrate how to
// use the Service Control API v2 for admission control. For more information,
// see https://cloud.google.com/service-infrastructure/docs/admission-control.
package main
import (
"errors"
"fmt"
"log"
"net/http"
"os"
"strings"
"time"
// WARNING:`go get google.golang.org/api/servicecontrol/v2" may take
// 30 minutes or longer, depending on your network speed.
"google.golang.org/api/servicecontrol/v2"
)
// Check calls Service Control API v2 for admission control.
// Name specifies the target resource name. Permission specifies
// the required permission on the target resource. Received
// specifies the timestamp when the request is received.
func check(w http.ResponseWriter, r *http.Request, name string, permission string, received time.Time, client *servicecontrol.Service) (string, error) {
// Construct CheckRequest from the incoming HTTP request.
// The code assumes the incoming request processed by App Engine ingress.
req := &servicecontrol.CheckRequest{
ServiceConfigId: "latest",
Attributes: &servicecontrol.AttributeContext{
Origin: &servicecontrol.Peer{
Ip: r.Header.Get("x-appengine-user-ip"),
},
Api: &servicecontrol.Api{
Service: "endpointsapis.appspot.com",
Operation: "google.example.endpointsapis.v1.Workspaces.GetWorkspace",
Version: "v1",
Protocol: r.Header.Get("x-forwarded-proto"),
},
Request: &servicecontrol.Request{
Id: r.Header.Get("x-appengine-request-log-id"),
Time: received.UTC().Format(time.RFC3339),
Method: r.Method,
Scheme: r.Header.Get("x-forwarded-proto"),
Host: r.Host,
Path: r.URL.Path,
Headers: map[string]string{
"authorization": r.Header.Get("authorization"),
"user-agent": r.Header.Get("user-agent"),
"origin": r.Header.Get("origin"),
"referer": r.Header.Get("referer"),
},
},
Resource: &servicecontrol.Resource{
Name: name,
},
},
Resources: []*servicecontrol.ResourceInfo{
{
Name: name,
Type: "endpointsapis.appspot.com/Workspace",
Permission: permission,
},
},
}
resp, err := client.Services.Check("endpointsapis.appspot.com", req).Do()
if err != nil {
return "", err
}
json, err := resp.MarshalJSON()
if err != nil {
return "", err
}
return string(json), nil
}
// Report calls Service Control API v2 for telemetry reporting.
// Name specifies the target resource name. ResponseCode specifies
// the response code returned to user. Received specifies the
// timestamp when the request is received.
func report(w http.ResponseWriter, r *http.Request, name string, responseCode int64, received time.Time, client *servicecontrol.Service) (string, error) {
// Construct ReportRequest from the incoming HTTP request.
// The code assumes the incoming request processed by App Engine ingress.
req := &servicecontrol.ReportRequest{
ServiceConfigId: "latest",
Operations: []*servicecontrol.AttributeContext{
{
Api: &servicecontrol.Api{
Service: "endpointsapis.appspot.com",
Operation: "google.example.endpointsapis.v1.Workspaces.GetWorkspace",
Version: "v1",
Protocol: r.Header.Get("x-forwarded-proto"),
},
Request: &servicecontrol.Request{
Size: r.ContentLength,
Time: received.UTC().Format(time.RFC3339),
},
Response: &servicecontrol.Response{
Time: time.Now().UTC().Format(time.RFC3339),
Code: responseCode,
BackendLatency: "0.007s",
},
Destination: &servicecontrol.Peer{
RegionCode: "us-central1",
},
Resource: &servicecontrol.Resource{
Name: name,
},
},
},
}
_, err := client.Services.Report("endpointsapis.appspot.com", req).Do()
if err != nil {
return "", err
}
return "{}", nil
}
// Parse processes the request path and extract the resource name and
// permissions.
func parse(r *http.Request) (string, string, error) {
// Split the request path.
segments := strings.Split(r.URL.Path, "/")
// The request path must match "/v1/projects/*/locations/*/workspaces/*" or
// "/v1/projects/*/locations/*/workspaces". They correspond to the
// GetWorkspace() and ListWorkspaces() methods defined in ../v1/workspace.proto.
if segments[0] != "" || segments[1] != "v1" || segments[2] != "projects" || segments[4] != "locations" || segments[6] != "workspaces" || len(segments) > 8 {
return "", "", errors.New("Resource '" + r.URL.Path + "' not found.")
}
// Skip prefix "/v1/".
resource := r.URL.Path[4:]
permission := "endpointsapis.appspot.com/workspaces.list"
if len(segments) == 8 {
permission = "endpointsapis.appspot.com/workspaces.get"
}
return resource, permission, nil
}
func indexHandler(w http.ResponseWriter, r *http.Request) {
received := time.Now()
// Create a client for Service Control API v2.
client, err := servicecontrol.NewService(r.Context())
if err != nil {
fmt.Fprintln(w, "Error:")
fmt.Fprintln(w, err.Error())
return
}
resource, permission, err := parse(r)
if err != nil {
fmt.Fprintln(w, "Error:")
fmt.Fprintln(w, err.Error())
return
}
// Perform admission control.
result, err := check(w, r, resource, permission, received, client)
var responseCode int64 = 200
// Print the admission control result.
if err != nil {
fmt.Fprintln(w, "Error:")
fmt.Fprintln(w, err.Error())
responseCode = 403
} else {
fmt.Fprintln(w, "CheckResponse:")
fmt.Fprintln(w, result)
}
// Print all environment variables.
fmt.Fprintln(w, "Environments:")
fmt.Fprintln(w, strings.Join(os.Environ(), "\n"))
// Print all request headers.
fmt.Fprintln(w, "Headers:")
for key, values := range r.Header {
for _, value := range values {
fmt.Fprintf(w, "%v: %v\n", key, value)
}
}
// Perform telemetry report.
report(w, r, resource, responseCode, received, client)
}
func main() {
http.HandleFunc("/", indexHandler)
port := os.Getenv("PORT")
log.Printf("Listen and serve on port %s", port)
if err := http.ListenAndServe(":"+port, nil); err != nil {
log.Fatal(err)
}
}