main.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. // The goapp command implements a simple App Engine app to demonstrate how to
  2. // use the Service Control API v2 for admission control. For more information,
  3. // see https://cloud.google.com/service-infrastructure/docs/admission-control.
  4. package main
  5. import (
  6. "errors"
  7. "fmt"
  8. "log"
  9. "net/http"
  10. "os"
  11. "strings"
  12. "time"
  13. // WARNING:`go get google.golang.org/api/servicecontrol/v2" may take
  14. // 30 minutes or longer, depending on your network speed.
  15. "google.golang.org/api/servicecontrol/v2"
  16. )
  17. // Check calls Service Control API v2 for admission control.
  18. // Name specifies the target resource name. Permission specifies
  19. // the required permission on the target resource. Received
  20. // specifies the timestamp when the request is received.
  21. func check(w http.ResponseWriter, r *http.Request, name string, permission string, received time.Time, client *servicecontrol.Service) (string, error) {
  22. // Construct CheckRequest from the incoming HTTP request.
  23. // The code assumes the incoming request processed by App Engine ingress.
  24. req := &servicecontrol.CheckRequest{
  25. ServiceConfigId: "latest",
  26. Attributes: &servicecontrol.AttributeContext{
  27. Origin: &servicecontrol.Peer{
  28. Ip: r.Header.Get("x-appengine-user-ip"),
  29. },
  30. Api: &servicecontrol.Api{
  31. Service: "endpointsapis.appspot.com",
  32. Operation: "google.example.endpointsapis.v1.Workspaces.GetWorkspace",
  33. Version: "v1",
  34. Protocol: r.Header.Get("x-forwarded-proto"),
  35. },
  36. Request: &servicecontrol.Request{
  37. Id: r.Header.Get("x-appengine-request-log-id"),
  38. Time: received.UTC().Format(time.RFC3339),
  39. Method: r.Method,
  40. Scheme: r.Header.Get("x-forwarded-proto"),
  41. Host: r.Host,
  42. Path: r.URL.Path,
  43. Headers: map[string]string{
  44. "authorization": r.Header.Get("authorization"),
  45. "user-agent": r.Header.Get("user-agent"),
  46. "origin": r.Header.Get("origin"),
  47. "referer": r.Header.Get("referer"),
  48. },
  49. },
  50. Resource: &servicecontrol.Resource{
  51. Name: name,
  52. },
  53. },
  54. Resources: []*servicecontrol.ResourceInfo{
  55. {
  56. Name: name,
  57. Type: "endpointsapis.appspot.com/Workspace",
  58. Permission: permission,
  59. },
  60. },
  61. }
  62. resp, err := client.Services.Check("endpointsapis.appspot.com", req).Do()
  63. if err != nil {
  64. return "", err
  65. }
  66. json, err := resp.MarshalJSON()
  67. if err != nil {
  68. return "", err
  69. }
  70. return string(json), nil
  71. }
  72. // Report calls Service Control API v2 for telemetry reporting.
  73. // Name specifies the target resource name. ResponseCode specifies
  74. // the response code returned to user. Received specifies the
  75. // timestamp when the request is received.
  76. func report(w http.ResponseWriter, r *http.Request, name string, responseCode int64, received time.Time, client *servicecontrol.Service) (string, error) {
  77. // Construct ReportRequest from the incoming HTTP request.
  78. // The code assumes the incoming request processed by App Engine ingress.
  79. req := &servicecontrol.ReportRequest{
  80. ServiceConfigId: "latest",
  81. Operations: []*servicecontrol.AttributeContext{
  82. {
  83. Api: &servicecontrol.Api{
  84. Service: "endpointsapis.appspot.com",
  85. Operation: "google.example.endpointsapis.v1.Workspaces.GetWorkspace",
  86. Version: "v1",
  87. Protocol: r.Header.Get("x-forwarded-proto"),
  88. },
  89. Request: &servicecontrol.Request{
  90. Size: r.ContentLength,
  91. Time: received.UTC().Format(time.RFC3339),
  92. },
  93. Response: &servicecontrol.Response{
  94. Time: time.Now().UTC().Format(time.RFC3339),
  95. Code: responseCode,
  96. BackendLatency: "0.007s",
  97. },
  98. Destination: &servicecontrol.Peer{
  99. RegionCode: "us-central1",
  100. },
  101. Resource: &servicecontrol.Resource{
  102. Name: name,
  103. },
  104. },
  105. },
  106. }
  107. _, err := client.Services.Report("endpointsapis.appspot.com", req).Do()
  108. if err != nil {
  109. return "", err
  110. }
  111. return "{}", nil
  112. }
  113. // Parse processes the request path and extract the resource name and
  114. // permissions.
  115. func parse(r *http.Request) (string, string, error) {
  116. // Split the request path.
  117. segments := strings.Split(r.URL.Path, "/")
  118. // The request path must match "/v1/projects/*/locations/*/workspaces/*" or
  119. // "/v1/projects/*/locations/*/workspaces". They correspond to the
  120. // GetWorkspace() and ListWorkspaces() methods defined in ../v1/workspace.proto.
  121. if segments[0] != "" || segments[1] != "v1" || segments[2] != "projects" || segments[4] != "locations" || segments[6] != "workspaces" || len(segments) > 8 {
  122. return "", "", errors.New("Resource '" + r.URL.Path + "' not found.")
  123. }
  124. // Skip prefix "/v1/".
  125. resource := r.URL.Path[4:]
  126. permission := "endpointsapis.appspot.com/workspaces.list"
  127. if len(segments) == 8 {
  128. permission = "endpointsapis.appspot.com/workspaces.get"
  129. }
  130. return resource, permission, nil
  131. }
  132. func indexHandler(w http.ResponseWriter, r *http.Request) {
  133. received := time.Now()
  134. // Create a client for Service Control API v2.
  135. client, err := servicecontrol.NewService(r.Context())
  136. if err != nil {
  137. fmt.Fprintln(w, "Error:")
  138. fmt.Fprintln(w, err.Error())
  139. return
  140. }
  141. resource, permission, err := parse(r)
  142. if err != nil {
  143. fmt.Fprintln(w, "Error:")
  144. fmt.Fprintln(w, err.Error())
  145. return
  146. }
  147. // Perform admission control.
  148. result, err := check(w, r, resource, permission, received, client)
  149. var responseCode int64 = 200
  150. // Print the admission control result.
  151. if err != nil {
  152. fmt.Fprintln(w, "Error:")
  153. fmt.Fprintln(w, err.Error())
  154. responseCode = 403
  155. } else {
  156. fmt.Fprintln(w, "CheckResponse:")
  157. fmt.Fprintln(w, result)
  158. }
  159. // Print all environment variables.
  160. fmt.Fprintln(w, "Environments:")
  161. fmt.Fprintln(w, strings.Join(os.Environ(), "\n"))
  162. // Print all request headers.
  163. fmt.Fprintln(w, "Headers:")
  164. for key, values := range r.Header {
  165. for _, value := range values {
  166. fmt.Fprintf(w, "%v: %v\n", key, value)
  167. }
  168. }
  169. // Perform telemetry report.
  170. report(w, r, resource, responseCode, received, client)
  171. }
  172. func main() {
  173. http.HandleFunc("/", indexHandler)
  174. port := os.Getenv("PORT")
  175. log.Printf("Listen and serve on port %s", port)
  176. if err := http.ListenAndServe(":"+port, nil); err != nil {
  177. log.Fatal(err)
  178. }
  179. }